From 3c3e250d178c444f26eb7f905d0dd131fc9783bd Mon Sep 17 00:00:00 2001 From: Noreen Mayat Date: Mon, 20 Jul 2026 12:49:42 -0700 Subject: [PATCH 1/2] feat: creating archive endpoint creating endpoint to archive models from front end. --- src/webapp/database.py | 1 + src/webapp/routers/models.py | 41 +++++++++++++++++++++++++++++++ src/webapp/routers/models_test.py | 17 +++++++++++++ 3 files changed, 59 insertions(+) diff --git a/src/webapp/database.py b/src/webapp/database.py index e5e1d541..f5d554a3 100644 --- a/src/webapp/database.py +++ b/src/webapp/database.py @@ -646,6 +646,7 @@ class ModelTable(Base): deleted: Mapped[bool] = mapped_column(nullable=True) # If true, the model has been approved and is ready for use. valid: Mapped[bool] = mapped_column(nullable=True) + archived: Mapped[int] = mapped_column(Integer, default=0) # The time the deletion request was set. deleted_at: Mapped[datetime.datetime] = mapped_column( DateTime(timezone=True), nullable=True diff --git a/src/webapp/routers/models.py b/src/webapp/routers/models.py index c94763a6..e7393e21 100644 --- a/src/webapp/routers/models.py +++ b/src/webapp/routers/models.py @@ -466,6 +466,47 @@ def delete_model( } +@router.patch("/{inst_id}/models/{model_name}/archive") +def archive_model( + inst_id: str, + model_name: str, + current_user: Annotated[BaseUser, Depends(get_current_active_user)], + sql_session: Annotated[Session, Depends(get_session)], +) -> Any: + """Archive a model by setting ``archived`` from 0 to 1.""" + transformed_model_name = str(decode_url_piece(model_name)).strip() + has_access_to_inst_or_err(inst_id, current_user) + + local_session.set(sql_session) + sess = local_session.get() + + model = sess.execute( + select(ModelTable).where( + ModelTable.name == transformed_model_name, + ModelTable.inst_id == str_to_uuid(inst_id), + ) + ).scalar_one_or_none() + if model is None: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Model not found." + ) + if model.archived: + raise HTTPException( + status_code=status.HTTP_409_CONFLICT, + detail="Model is already archived.", + ) + + model.archived = 1 + sess.commit() + + return { + "inst_id": inst_id, + "model_name": transformed_model_name, + "archived": 1, + "status": "Model archived", + } + + @router.get("/{inst_id}/models/{model_name}/runs", response_model=list[RunInfo]) def read_inst_model_outputs( inst_id: str, diff --git a/src/webapp/routers/models_test.py b/src/webapp/routers/models_test.py index 19467e9b..e8f09278 100644 --- a/src/webapp/routers/models_test.py +++ b/src/webapp/routers/models_test.py @@ -283,6 +283,23 @@ def test_read_inst_model(client: TestClient) -> None: assert same_model_orderless(response_model, expected_model) +def test_archive_model(client: TestClient, session: sqlalchemy.orm.Session) -> None: + """Test PATCH /institutions/{inst_id}/models/{model_name}/archive.""" + base = "/institutions/" + uuid_to_str(USER_VALID_INST_UUID) + "/models/" + assert client.patch(base + "missing_model/archive").status_code == 404 + + response = client.patch(base + "sample_model_for_school_1/archive") + assert response.status_code == 200 + assert response.json() == { + "inst_id": uuid_to_str(USER_VALID_INST_UUID), + "model_name": "sample_model_for_school_1", + "archived": 1, + "status": "Model archived", + } + assert session.get(ModelTable, SAMPLE_UUID).archived == 1 + assert client.patch(base + "sample_model_for_school_1/archive").status_code == 409 + + def test_read_inst_model_outputs(client: TestClient) -> None: """Test GET /institutions/345/models/10/output.""" MOCK_STORAGE.list_blobs_in_folder.return_value = [] From c734f7c160bca7be2f9fb5c78a8be5d4a0e37bde Mon Sep 17 00:00:00 2001 From: Noreen Mayat Date: Mon, 20 Jul 2026 12:53:43 -0700 Subject: [PATCH 2/2] fix: type check error --- src/webapp/routers/models_test.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/webapp/routers/models_test.py b/src/webapp/routers/models_test.py index e8f09278..7911fd76 100644 --- a/src/webapp/routers/models_test.py +++ b/src/webapp/routers/models_test.py @@ -296,7 +296,9 @@ def test_archive_model(client: TestClient, session: sqlalchemy.orm.Session) -> N "archived": 1, "status": "Model archived", } - assert session.get(ModelTable, SAMPLE_UUID).archived == 1 + model_row = session.get(ModelTable, SAMPLE_UUID) + assert model_row is not None + assert model_row.archived == 1 assert client.patch(base + "sample_model_for_school_1/archive").status_code == 409