Skip to content

Commit 126c591

Browse files
committed
feat: add file presence/absence checks in calibration creation and modification routes
1 parent be4402b commit 126c591

File tree

2 files changed

+181
-1
lines changed

2 files changed

+181
-1
lines changed

src/mavedb/routers/score_calibrations.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,19 @@ async def create_score_calibration_route(
283283
# permission to update the score set itself.
284284
assert_permission(user_data, score_set, Action.UPDATE)
285285

286+
if calibration.class_based and not classes_file:
287+
raise HTTPException(
288+
status_code=422,
289+
detail="A classes_file must be provided when creating a class-based calibration.",
290+
)
291+
286292
if classes_file:
293+
if calibration.range_based:
294+
raise HTTPException(
295+
status_code=422,
296+
detail="A classes_file should not be provided when creating a range-based calibration.",
297+
)
298+
287299
try:
288300
classes_df = csv_data_to_df(classes_file.file, induce_hgvs_cols=False)
289301
except UnicodeDecodeError as e:
@@ -437,7 +449,19 @@ async def modify_score_calibration_route(
437449

438450
assert_permission(user_data, item, Action.UPDATE)
439451

452+
if calibration_update.class_based and not classes_file:
453+
raise HTTPException(
454+
status_code=422,
455+
detail="A classes_file must be provided when modifying a class-based calibration.",
456+
)
457+
440458
if classes_file:
459+
if calibration_update.range_based:
460+
raise HTTPException(
461+
status_code=422,
462+
detail="A classes_file should not be provided when modifying a range-based calibration.",
463+
)
464+
441465
try:
442466
classes_df = csv_data_to_df(classes_file.file, induce_hgvs_cols=False)
443467
except UnicodeDecodeError as e:

tests/routers/test_score_calibrations.py

Lines changed: 157 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1309,6 +1309,80 @@ def test_cannot_create_score_calibration_in_public_score_set_when_score_set_not_
13091309
assert f"insufficient permissions on score set with URN '{score_set['urn']}'" in error["detail"]
13101310

13111311

1312+
@pytest.mark.parametrize(
1313+
"mock_publication_fetch",
1314+
[
1315+
[
1316+
{"dbName": "PubMed", "identifier": TEST_PUBMED_IDENTIFIER},
1317+
{"dbName": "bioRxiv", "identifier": TEST_BIORXIV_IDENTIFIER},
1318+
]
1319+
],
1320+
indirect=["mock_publication_fetch"],
1321+
)
1322+
def test_cannot_create_class_based_score_calibration_without_classes_file(
1323+
client, setup_router_db, mock_publication_fetch, session, data_provider, data_files
1324+
):
1325+
experiment = create_experiment(client)
1326+
score_set = create_seq_score_set_with_mapped_variants(
1327+
client,
1328+
session,
1329+
data_provider,
1330+
experiment["urn"],
1331+
data_files / "scores.csv",
1332+
)
1333+
1334+
response = client.post(
1335+
"/api/v1/score-calibrations",
1336+
json={
1337+
"scoreSetUrn": score_set["urn"],
1338+
**deepcamelize(TEST_BRNICH_SCORE_CALIBRATION_CLASS_BASED),
1339+
},
1340+
)
1341+
1342+
assert response.status_code == 422
1343+
error = response.json()
1344+
assert "A classes_file must be provided when creating a class-based calibration" in str(error["detail"])
1345+
1346+
1347+
@pytest.mark.parametrize(
1348+
"mock_publication_fetch",
1349+
[
1350+
[
1351+
{"dbName": "PubMed", "identifier": TEST_PUBMED_IDENTIFIER},
1352+
{"dbName": "bioRxiv", "identifier": TEST_BIORXIV_IDENTIFIER},
1353+
]
1354+
],
1355+
indirect=["mock_publication_fetch"],
1356+
)
1357+
def test_cannot_create_range_based_score_calibration_with_classes_file(
1358+
client, setup_router_db, mock_publication_fetch, session, data_provider, data_files
1359+
):
1360+
experiment = create_experiment(client)
1361+
score_set = create_seq_score_set_with_mapped_variants(
1362+
client,
1363+
session,
1364+
data_provider,
1365+
experiment["urn"],
1366+
data_files / "scores.csv",
1367+
)
1368+
1369+
classification_csv_path = data_files / "calibration_classes.csv"
1370+
with open(classification_csv_path, "rb") as class_file:
1371+
response = client.post(
1372+
"/api/v1/score-calibrations",
1373+
files={"classes_file": (classification_csv_path.name, class_file, "text/csv")},
1374+
data={
1375+
"calibration_json": json.dumps(
1376+
{"scoreSetUrn": score_set["urn"], **deepcamelize(TEST_BRNICH_SCORE_CALIBRATION_RANGE_BASED)}
1377+
),
1378+
},
1379+
)
1380+
1381+
assert response.status_code == 422
1382+
error = response.json()
1383+
assert "A classes_file should not be provided when creating a range-based calibration" in str(error["detail"])
1384+
1385+
13121386
@pytest.mark.parametrize(
13131387
"mock_publication_fetch",
13141388
[
@@ -1626,6 +1700,89 @@ def test_cannot_update_score_calibration_when_calibration_not_exists(
16261700
assert "The requested score calibration does not exist" in error["detail"]
16271701

16281702

1703+
@pytest.mark.parametrize(
1704+
"mock_publication_fetch",
1705+
[
1706+
[
1707+
{"dbName": "PubMed", "identifier": TEST_PUBMED_IDENTIFIER},
1708+
{"dbName": "bioRxiv", "identifier": TEST_BIORXIV_IDENTIFIER},
1709+
]
1710+
],
1711+
indirect=["mock_publication_fetch"],
1712+
)
1713+
def test_cannot_update_class_based_score_calibration_without_class_file(
1714+
client, setup_router_db, mock_publication_fetch, session, data_provider, data_files
1715+
):
1716+
experiment = create_experiment(client)
1717+
score_set = create_seq_score_set_with_mapped_variants(
1718+
client,
1719+
session,
1720+
data_provider,
1721+
experiment["urn"],
1722+
data_files / "scores.csv",
1723+
)
1724+
calibration = create_test_score_calibration_in_score_set_via_client(
1725+
client, score_set["urn"], deepcamelize(TEST_BRNICH_SCORE_CALIBRATION_RANGE_BASED)
1726+
)
1727+
1728+
response = client.put(
1729+
f"/api/v1/score-calibrations/{calibration['urn']}",
1730+
json={
1731+
"scoreSetUrn": score_set["urn"],
1732+
**deepcamelize(TEST_BRNICH_SCORE_CALIBRATION_CLASS_BASED),
1733+
},
1734+
)
1735+
1736+
assert response.status_code == 422
1737+
error = response.json()
1738+
assert "A classes_file must be provided when modifying a class-based calibration" in str(error["detail"])
1739+
1740+
1741+
@pytest.mark.parametrize(
1742+
"mock_publication_fetch",
1743+
[
1744+
[
1745+
{"dbName": "PubMed", "identifier": TEST_PUBMED_IDENTIFIER},
1746+
{"dbName": "bioRxiv", "identifier": TEST_BIORXIV_IDENTIFIER},
1747+
]
1748+
],
1749+
indirect=["mock_publication_fetch"],
1750+
)
1751+
def test_cannot_update_range_based_score_calibration_with_class_file(
1752+
client, setup_router_db, mock_publication_fetch, session, data_provider, data_files
1753+
):
1754+
experiment = create_experiment(client)
1755+
score_set = create_seq_score_set_with_mapped_variants(
1756+
client,
1757+
session,
1758+
data_provider,
1759+
experiment["urn"],
1760+
data_files / "scores.csv",
1761+
)
1762+
calibration = create_test_score_calibration_in_score_set_via_client(
1763+
client, score_set["urn"], deepcamelize(TEST_BRNICH_SCORE_CALIBRATION_RANGE_BASED)
1764+
)
1765+
1766+
classification_csv_path = data_files / "calibration_classes.csv"
1767+
with open(classification_csv_path, "rb") as class_file:
1768+
response = client.put(
1769+
f"/api/v1/score-calibrations/{calibration['urn']}",
1770+
files={"classes_file": (classification_csv_path.name, class_file, "text/csv")},
1771+
data={
1772+
"calibration_json": json.dumps(
1773+
{
1774+
"scoreSetUrn": score_set["urn"],
1775+
**deepcamelize(TEST_PATHOGENICITY_SCORE_CALIBRATION),
1776+
}
1777+
),
1778+
},
1779+
)
1780+
1781+
assert response.status_code == 422
1782+
error = response.json()
1783+
assert "A classes_file should not be provided when modifying a range-based calibration" in str(error["detail"])
1784+
1785+
16291786
@pytest.mark.parametrize(
16301787
"mock_publication_fetch",
16311788
[
@@ -2315,7 +2472,6 @@ def test_can_modify_score_calibration_to_class_based(
23152472
"calibration_json": json.dumps({"scoreSetUrn": score_set["urn"], **updated_calibration_data}),
23162473
},
23172474
)
2318-
print(response.text)
23192475

23202476
assert response.status_code == 200
23212477
calibration_response = response.json()

0 commit comments

Comments
 (0)