Skip to content

Commit 349b745

Browse files
authored
Merge pull request #689 from VariantEffect/feature/bencap/688/score-calibration-me-endpoint
feat: add endpoint for user created calibrations
2 parents bda4ff1 + e11911d commit 349b745

2 files changed

Lines changed: 154 additions & 2 deletions

File tree

src/mavedb/routers/score_calibrations.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from mavedb import deps
88
from mavedb.lib.authentication import get_current_user
9-
from mavedb.lib.authorization import require_current_user_with_email
9+
from mavedb.lib.authorization import require_current_user, require_current_user_with_email
1010
from mavedb.lib.flexible_model_loader import json_or_form_loader
1111
from mavedb.lib.logging import LoggedRoute
1212
from mavedb.lib.logging.context import (
@@ -31,14 +31,15 @@
3131
from mavedb.models.score_calibration import ScoreCalibration
3232
from mavedb.models.score_calibration_functional_classification import ScoreCalibrationFunctionalClassification
3333
from mavedb.models.score_set import ScoreSet
34+
from mavedb.routers.shared import ACCESS_CONTROL_ERROR_RESPONSES, PUBLIC_ERROR_RESPONSES
3435
from mavedb.view_models import score_calibration
3536

3637
logger = logging.getLogger(__name__)
3738

3839
router = APIRouter(
3940
prefix="/api/v1/score-calibrations",
4041
tags=["Score Calibrations"],
41-
responses={404: {"description": "Not found"}},
42+
responses={**PUBLIC_ERROR_RESPONSES},
4243
route_class=LoggedRoute,
4344
)
4445

@@ -54,6 +55,27 @@
5455
)
5556

5657

58+
@router.get(
59+
"/me",
60+
status_code=200,
61+
response_model=list[score_calibration.ScoreCalibrationWithScoreSetUrn],
62+
responses={**ACCESS_CONTROL_ERROR_RESPONSES},
63+
summary="List my calibrations",
64+
)
65+
def list_my_calibrations(
66+
*,
67+
db: Session = Depends(deps.get_db),
68+
user_data: UserData = Depends(require_current_user),
69+
) -> list[ScoreCalibration]:
70+
"""List all score calibrations created by the current user."""
71+
return (
72+
db.query(ScoreCalibration)
73+
.filter(ScoreCalibration.created_by_id == user_data.user.id)
74+
.options(selectinload(ScoreCalibration.score_set).selectinload(ScoreSet.contributors))
75+
.all()
76+
)
77+
78+
5779
@router.get(
5880
"/{urn}",
5981
response_model=score_calibration.ScoreCalibrationWithScoreSetUrn,

tests/routers/test_score_calibrations.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4884,3 +4884,133 @@ def test_cannot_create_score_calibration_without_email(
48844884

48854885
assert response.status_code == 403
48864886
assert "email" in response.json()["detail"].lower()
4887+
4888+
4889+
###########################################################
4890+
# GET /score-calibrations/me
4891+
###########################################################
4892+
4893+
4894+
def test_anonymous_user_cannot_list_my_calibrations(client, setup_router_db, anonymous_app_overrides):
4895+
with DependencyOverrider(anonymous_app_overrides):
4896+
response = client.get("/api/v1/score-calibrations/me")
4897+
4898+
assert response.status_code == 401
4899+
4900+
4901+
def test_authenticated_user_with_no_calibrations_returns_empty_list(client, setup_router_db):
4902+
response = client.get("/api/v1/score-calibrations/me")
4903+
4904+
assert response.status_code == 200
4905+
assert response.json() == []
4906+
4907+
4908+
@pytest.mark.parametrize(
4909+
"mock_publication_fetch",
4910+
[
4911+
[
4912+
{"dbName": "PubMed", "identifier": TEST_PUBMED_IDENTIFIER},
4913+
{"dbName": "bioRxiv", "identifier": TEST_BIORXIV_IDENTIFIER},
4914+
]
4915+
],
4916+
indirect=["mock_publication_fetch"],
4917+
)
4918+
def test_authenticated_user_sees_own_calibrations(
4919+
client, setup_router_db, mock_publication_fetch, session, data_provider, data_files
4920+
):
4921+
experiment = create_experiment(client)
4922+
score_set = create_seq_score_set_with_mapped_variants(
4923+
client,
4924+
session,
4925+
data_provider,
4926+
experiment["urn"],
4927+
data_files / "scores.csv",
4928+
)
4929+
calibration = create_test_score_calibration_in_score_set_via_client(
4930+
client, score_set["urn"], deepcamelize(TEST_BRNICH_SCORE_CALIBRATION_RANGE_BASED)
4931+
)
4932+
4933+
response = client.get("/api/v1/score-calibrations/me")
4934+
4935+
assert response.status_code == 200
4936+
calibrations = response.json()
4937+
assert len(calibrations) == 1
4938+
assert calibrations[0]["urn"] == calibration["urn"]
4939+
assert calibrations[0]["scoreSetUrn"] == score_set["urn"]
4940+
4941+
4942+
@pytest.mark.parametrize(
4943+
"mock_publication_fetch",
4944+
[
4945+
[
4946+
{"dbName": "PubMed", "identifier": TEST_PUBMED_IDENTIFIER},
4947+
{"dbName": "bioRxiv", "identifier": TEST_BIORXIV_IDENTIFIER},
4948+
]
4949+
],
4950+
indirect=["mock_publication_fetch"],
4951+
)
4952+
def test_user_does_not_see_other_users_calibrations(
4953+
client, setup_router_db, mock_publication_fetch, session, data_provider, data_files, extra_user_app_overrides
4954+
):
4955+
experiment = create_experiment(client)
4956+
score_set = create_seq_score_set_with_mapped_variants(
4957+
client,
4958+
session,
4959+
data_provider,
4960+
experiment["urn"],
4961+
data_files / "scores.csv",
4962+
)
4963+
create_test_score_calibration_in_score_set_via_client(
4964+
client, score_set["urn"], deepcamelize(TEST_BRNICH_SCORE_CALIBRATION_RANGE_BASED)
4965+
)
4966+
4967+
with DependencyOverrider(extra_user_app_overrides):
4968+
response = client.get("/api/v1/score-calibrations/me")
4969+
4970+
assert response.status_code == 200
4971+
assert response.json() == []
4972+
4973+
4974+
@pytest.mark.parametrize(
4975+
"mock_publication_fetch",
4976+
[
4977+
[
4978+
{"dbName": "PubMed", "identifier": TEST_PUBMED_IDENTIFIER},
4979+
{"dbName": "bioRxiv", "identifier": TEST_BIORXIV_IDENTIFIER},
4980+
]
4981+
],
4982+
indirect=["mock_publication_fetch"],
4983+
)
4984+
def test_user_sees_calibrations_across_multiple_score_sets(
4985+
client, setup_router_db, mock_publication_fetch, session, data_provider, data_files
4986+
):
4987+
experiment = create_experiment(client)
4988+
score_set_1 = create_seq_score_set_with_mapped_variants(
4989+
client,
4990+
session,
4991+
data_provider,
4992+
experiment["urn"],
4993+
data_files / "scores.csv",
4994+
)
4995+
score_set_2 = create_seq_score_set_with_mapped_variants(
4996+
client,
4997+
session,
4998+
data_provider,
4999+
experiment["urn"],
5000+
data_files / "scores.csv",
5001+
)
5002+
cal_1 = create_test_score_calibration_in_score_set_via_client(
5003+
client, score_set_1["urn"], deepcamelize(TEST_BRNICH_SCORE_CALIBRATION_RANGE_BASED)
5004+
)
5005+
cal_2 = create_test_score_calibration_in_score_set_via_client(
5006+
client, score_set_2["urn"], deepcamelize(TEST_BRNICH_SCORE_CALIBRATION_RANGE_BASED)
5007+
)
5008+
5009+
response = client.get("/api/v1/score-calibrations/me")
5010+
5011+
assert response.status_code == 200
5012+
calibrations = response.json()
5013+
assert len(calibrations) == 2
5014+
returned_urns = {c["urn"] for c in calibrations}
5015+
assert cal_1["urn"] in returned_urns
5016+
assert cal_2["urn"] in returned_urns

0 commit comments

Comments
 (0)