Skip to content

Commit 9ad50d8

Browse files
committed
Fixes #358: Literal 'null' inserted for NULL score ranges.
1 parent 3435abd commit 9ad50d8

File tree

3 files changed

+108
-28
lines changed

3 files changed

+108
-28
lines changed

src/mavedb/routers/score_sets.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from fastapi.encoders import jsonable_encoder
1010
from fastapi.exceptions import HTTPException
1111
from fastapi.responses import StreamingResponse
12-
from sqlalchemy import or_, select
12+
from sqlalchemy import null, or_, select
1313
from sqlalchemy.exc import MultipleResultsFound, NoResultFound
1414
from sqlalchemy.orm import Session
1515

@@ -312,10 +312,10 @@ def get_score_set_mapped_variants(
312312

313313
mapped_variants = (
314314
db.query(MappedVariant)
315-
.filter(ScoreSet.urn == urn)
316-
.filter(ScoreSet.id == Variant.score_set_id)
317-
.filter(Variant.id == MappedVariant.variant_id)
318-
.all()
315+
.filter(ScoreSet.urn == urn)
316+
.filter(ScoreSet.id == Variant.score_set_id)
317+
.filter(Variant.id == MappedVariant.variant_id)
318+
.all()
319319
)
320320

321321
if not mapped_variants:
@@ -482,10 +482,9 @@ async def create_score_set(
482482
for identifier in item_create.primary_publication_identifiers or []
483483
]
484484
publication_identifiers = [
485-
await find_or_create_publication_identifier(db, identifier.identifier,
486-
identifier.db_name)
487-
for identifier in item_create.secondary_publication_identifiers or []
488-
] + primary_publication_identifiers
485+
await find_or_create_publication_identifier(db, identifier.identifier, identifier.db_name)
486+
for identifier in item_create.secondary_publication_identifiers or []
487+
] + primary_publication_identifiers
489488

490489
# create a temporary `primary` attribute on each of our publications that indicates
491490
# to our association proxy whether it is a primary publication or not
@@ -595,6 +594,7 @@ async def create_score_set(
595594
"secondary_publication_identifiers",
596595
"superseded_score_set_urn",
597596
"target_genes",
597+
"score_ranges",
598598
},
599599
),
600600
experiment=experiment,
@@ -608,6 +608,7 @@ async def create_score_set(
608608
processing_state=ProcessingState.incomplete,
609609
created_by=user_data.user,
610610
modified_by=user_data.user,
611+
score_ranges=item_create.score_ranges.dict() if item_create.score_ranges else null(),
611612
) # type: ignore
612613

613614
db.add(item)
@@ -809,7 +810,7 @@ async def update_score_set(
809810
if item_update.score_ranges:
810811
item.score_ranges = item_update.score_ranges.dict()
811812
else:
812-
item.score_ranges = None
813+
item.score_ranges = null()
813814

814815
# Delete the old target gene, WT sequence, and reference map. These will be deleted when we set the score set's
815816
# target_gene to None, because we have set cascade='all,delete-orphan' on ScoreSet.target_gene. (Since the

tests/lib/test_score_set.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
)
2222
from mavedb.models.score_set import ScoreSet
2323
from mavedb.models.variant import Variant
24+
from tests.helpers.constants import TEST_SAVED_SCORESET_RANGE
2425
from tests.helpers.util import create_acc_score_set, create_experiment, create_seq_score_set
2526

2627

@@ -307,3 +308,24 @@ def test_create_variants_acc_score_set(setup_lib_db, client, session):
307308
assert db_variant.urn.split("#")[0] == score_set.urn
308309

309310
session.commit()
311+
312+
313+
def test_create_null_score_range(setup_lib_db, client, session):
314+
experiment = create_experiment(client)
315+
create_seq_score_set(client, experiment["urn"])
316+
score_set = session.scalar(select(ScoreSet).where(ScoreSet.score_ranges.is_(None)))
317+
318+
assert score_set is not None
319+
320+
321+
def test_update_null_score_range(setup_lib_db, client, session):
322+
experiment = create_experiment(client)
323+
score_set = create_seq_score_set(client, experiment["urn"], update={"scoreRanges": TEST_SAVED_SCORESET_RANGE})
324+
db_score_set = session.scalar(select(ScoreSet).where(ScoreSet.score_ranges.is_(None)))
325+
assert db_score_set is None
326+
327+
score_set.pop("scoreRanges")
328+
score_set = client.put(f"/api/v1/score-sets/{score_set['urn']}", json=score_set)
329+
db_score_set = session.scalar(select(ScoreSet).where(ScoreSet.score_ranges.is_(None)))
330+
331+
assert db_score_set is not None

tests/routers/test_score_set.py

Lines changed: 75 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,39 @@ def test_create_score_set_with_score_range(client, setup_router_db):
155155
assert response.status_code == 200
156156

157157

158+
def test_remove_score_range_from_score_set(client, setup_router_db):
159+
experiment = create_experiment(client)
160+
score_set = deepcopy(TEST_MINIMAL_SEQ_SCORESET)
161+
score_set["experimentUrn"] = experiment["urn"]
162+
score_set.update({"score_ranges": TEST_SCORESET_RANGE})
163+
164+
response = client.post("/api/v1/score-sets/", json=score_set)
165+
assert response.status_code == 200
166+
response_data = response.json()
167+
168+
jsonschema.validate(instance=response_data, schema=ScoreSet.schema())
169+
assert isinstance(MAVEDB_TMP_URN_RE.fullmatch(response_data["urn"]), re.Match)
170+
171+
expected_response = update_expected_response_for_created_resources(
172+
deepcopy(TEST_MINIMAL_SEQ_SCORESET_RESPONSE), experiment, response_data
173+
)
174+
expected_response["scoreRanges"] = TEST_SAVED_SCORESET_RANGE
175+
176+
assert sorted(expected_response.keys()) == sorted(response_data.keys())
177+
for key in expected_response:
178+
assert (key, expected_response[key]) == (key, response_data[key])
179+
180+
score_set.pop("score_ranges")
181+
response = client.put(f"/api/v1/score-sets/{response_data['urn']}", json=score_set)
182+
assert response.status_code == 200
183+
response_data = response.json()
184+
185+
jsonschema.validate(instance=response_data, schema=ScoreSet.schema())
186+
assert isinstance(MAVEDB_TMP_URN_RE.fullmatch(response_data["urn"]), re.Match)
187+
188+
assert "scoreRanges" not in response_data.keys()
189+
190+
158191
def test_cannot_create_score_set_without_email(client, setup_router_db):
159192
experiment = create_experiment(client)
160193
score_set_post_payload = deepcopy(TEST_MINIMAL_SEQ_SCORESET)
@@ -1284,7 +1317,7 @@ def test_cannot_add_score_set_to_meta_analysis_experiment(session, data_provider
12841317
meta_score_set_1 = (client.post(f"/api/v1/score-sets/{meta_score_set_1['urn']}/publish")).json()
12851318
assert isinstance(MAVEDB_SCORE_SET_URN_RE.fullmatch(meta_score_set_1["urn"]), re.Match)
12861319
score_set_2 = deepcopy(TEST_MINIMAL_SEQ_SCORESET)
1287-
score_set_2["experimentUrn"] = meta_score_set_1['experiment']['urn']
1320+
score_set_2["experimentUrn"] = meta_score_set_1["experiment"]["urn"]
12881321
jsonschema.validate(instance=score_set_2, schema=ScoreSetCreate.schema())
12891322

12901323
response = client.post("/api/v1/score-sets/", json=score_set_2)
@@ -1293,7 +1326,9 @@ def test_cannot_add_score_set_to_meta_analysis_experiment(session, data_provider
12931326
assert "Score sets may not be added to a meta-analysis experiment." in response_data["detail"]
12941327

12951328

1296-
def test_create_single_score_set_meta_analysis_to_others_score_set(session, data_provider, client, setup_router_db, data_files):
1329+
def test_create_single_score_set_meta_analysis_to_others_score_set(
1330+
session, data_provider, client, setup_router_db, data_files
1331+
):
12971332
experiment = create_experiment(client)
12981333
score_set = create_seq_score_set_with_variants(
12991334
client, session, data_provider, experiment["urn"], data_files / "scores.csv"
@@ -1380,6 +1415,7 @@ def test_multiple_score_set_meta_analysis_multiple_experiment_sets_with_differen
13801415
assert meta_score_set["urn"] == "urn:mavedb:00000003-0-1"
13811416
assert isinstance(MAVEDB_SCORE_SET_URN_RE.fullmatch(meta_score_set["urn"]), re.Match)
13821417

1418+
13831419
########################################################################################################################
13841420
# Score set search
13851421
########################################################################################################################
@@ -1494,7 +1530,9 @@ def test_search_others_private_score_sets_urn_match(session, data_provider, clie
14941530

14951531

14961532
# There is space in the end of test urn. The search result returned nothing before.
1497-
def test_search_others_private_score_sets_urn_with_space_match(session, data_provider, client, setup_router_db, data_files):
1533+
def test_search_others_private_score_sets_urn_with_space_match(
1534+
session, data_provider, client, setup_router_db, data_files
1535+
):
14981536
experiment_1 = create_experiment(client)
14991537
score_set_1_1 = create_seq_score_set_with_variants(
15001538
client, session, data_provider, experiment_1["urn"], data_files / "scores.csv"
@@ -1552,7 +1590,7 @@ def test_search_public_score_sets_urn_with_space_match(session, data_provider, c
15521590
client, session, data_provider, experiment_1["urn"], data_files / "scores.csv"
15531591
)
15541592
score_set_response = client.post(f"/api/v1/score-sets/{score_set_1_1['urn']}/publish")
1555-
published_score_set = score_set_response.json()
1593+
published_score_set = score_set_response.json()
15561594
assert score_set_response.status_code == 200
15571595
urn_with_space = published_score_set["urn"] + " "
15581596
search_payload = {"urn": urn_with_space}
@@ -1620,7 +1658,9 @@ def test_search_others_public_score_sets_urn_match(session, data_provider, clien
16201658
assert response.json()[0]["urn"] == publish_score_set["urn"]
16211659

16221660

1623-
def test_search_others_public_score_sets_urn_with_space_match(session, data_provider, client, setup_router_db, data_files):
1661+
def test_search_others_public_score_sets_urn_with_space_match(
1662+
session, data_provider, client, setup_router_db, data_files
1663+
):
16241664
experiment_1 = create_experiment(client)
16251665
score_set_1_1 = create_seq_score_set_with_variants(
16261666
client, session, data_provider, experiment_1["urn"], data_files / "scores.csv"
@@ -1637,7 +1677,9 @@ def test_search_others_public_score_sets_urn_with_space_match(session, data_prov
16371677
assert response.json()[0]["urn"] == published_score_set["urn"]
16381678

16391679

1640-
def test_search_private_score_sets_not_showing_public_score_set(session, data_provider, client, setup_router_db, data_files):
1680+
def test_search_private_score_sets_not_showing_public_score_set(
1681+
session, data_provider, client, setup_router_db, data_files
1682+
):
16411683
experiment_1 = create_experiment(client)
16421684
score_set_1_1 = create_seq_score_set_with_variants(
16431685
client, session, data_provider, experiment_1["urn"], data_files / "scores.csv"
@@ -1654,14 +1696,14 @@ def test_search_private_score_sets_not_showing_public_score_set(session, data_pr
16541696
assert response.json()[0]["urn"] == score_set_1_2["urn"]
16551697

16561698

1657-
def test_search_public_score_sets_not_showing_private_score_set(session, data_provider, client, setup_router_db, data_files):
1699+
def test_search_public_score_sets_not_showing_private_score_set(
1700+
session, data_provider, client, setup_router_db, data_files
1701+
):
16581702
experiment_1 = create_experiment(client)
16591703
score_set_1_1 = create_seq_score_set_with_variants(
16601704
client, session, data_provider, experiment_1["urn"], data_files / "scores.csv"
16611705
)
1662-
create_seq_score_set_with_variants(
1663-
client, session, data_provider, experiment_1["urn"], data_files / "scores.csv"
1664-
)
1706+
create_seq_score_set_with_variants(client, session, data_provider, experiment_1["urn"], data_files / "scores.csv")
16651707
score_set_response = client.post(f"/api/v1/score-sets/{score_set_1_1['urn']}/publish")
16661708
assert score_set_response.status_code == 200
16671709
published_score_set = score_set_response.json()
@@ -1672,7 +1714,6 @@ def test_search_public_score_sets_not_showing_private_score_set(session, data_pr
16721714
assert response.json()[0]["urn"] == published_score_set["urn"]
16731715

16741716

1675-
16761717
########################################################################################################################
16771718
# Score set deletion
16781719
########################################################################################################################
@@ -1913,6 +1954,7 @@ def test_can_modify_metadata_for_score_set_with_inactive_license(session, client
19131954
# Supersede score set
19141955
########################################################################################################################
19151956

1957+
19161958
def test_create_superseding_score_set(session, data_provider, client, setup_router_db, data_files):
19171959
experiment = create_experiment(client)
19181960
score_set = create_seq_score_set_with_variants(
@@ -1927,6 +1969,7 @@ def test_create_superseding_score_set(session, data_provider, client, setup_rout
19271969
superseding_score_set_response = client.post("/api/v1/score-sets/", json=score_set_post_payload)
19281970
assert superseding_score_set_response.status_code == 200
19291971

1972+
19301973
def test_can_view_unpublished_superseding_score_set(session, data_provider, client, setup_router_db, data_files):
19311974
experiment = create_experiment(client)
19321975
unpublished_score_set = create_seq_score_set_with_variants(
@@ -1947,7 +1990,10 @@ def test_can_view_unpublished_superseding_score_set(session, data_provider, clie
19471990
assert score_set["urn"] == superseding_score_set["supersededScoreSet"]["urn"]
19481991
assert score_set["supersedingScoreSet"]["urn"] == superseding_score_set["urn"]
19491992

1950-
def test_cannot_view_others_unpublished_superseding_score_set(session, data_provider, client, setup_router_db, data_files):
1993+
1994+
def test_cannot_view_others_unpublished_superseding_score_set(
1995+
session, data_provider, client, setup_router_db, data_files
1996+
):
19511997
experiment = create_experiment(client)
19521998
unpublished_score_set = create_seq_score_set_with_variants(
19531999
client, session, data_provider, experiment["urn"], data_files / "scores.csv"
@@ -1969,6 +2015,7 @@ def test_cannot_view_others_unpublished_superseding_score_set(session, data_prov
19692015
# Other users can't view the unpublished superseding score set.
19702016
assert "supersedingScoreSet" not in score_set
19712017

2018+
19722019
def test_can_view_others_published_superseding_score_set(session, data_provider, client, setup_router_db, data_files):
19732020
experiment = create_experiment(client)
19742021
unpublished_score_set = create_seq_score_set_with_variants(
@@ -2001,7 +2048,9 @@ def test_can_view_others_published_superseding_score_set(session, data_provider,
20012048

20022049

20032050
# The superseding score set is unpublished so the newest version to its owner is the unpublished one.
2004-
def test_show_correct_score_set_version_with_superseded_score_set_to_its_owner(session, data_provider, client, setup_router_db, data_files):
2051+
def test_show_correct_score_set_version_with_superseded_score_set_to_its_owner(
2052+
session, data_provider, client, setup_router_db, data_files
2053+
):
20052054
experiment = create_experiment(client)
20062055
unpublished_score_set = create_seq_score_set_with_variants(
20072056
client, session, data_provider, experiment["urn"], data_files / "scores.csv"
@@ -2019,7 +2068,7 @@ def test_show_correct_score_set_version_with_superseded_score_set_to_its_owner(s
20192068
score_set = score_set_response.json()
20202069
assert score_set_response.status_code == 200
20212070
assert score_set["urn"] == superseding_score_set["urn"]
2022-
2071+
20232072

20242073
def test_anonymous_user_cannot_add_score_calibrations_to_score_set(client, setup_router_db, anonymous_app_overrides):
20252074
experiment = create_experiment(client)
@@ -2093,6 +2142,7 @@ def test_score_set_not_found_for_non_existent_score_set_when_adding_score_calibr
20932142
# Score set download files
20942143
########################################################################################################################
20952144

2145+
20962146
# Test file doesn't have hgvs_splice so its values are all NA.
20972147
def test_download_scores_file(session, data_provider, client, setup_router_db, data_files):
20982148
experiment = create_experiment(client)
@@ -2105,7 +2155,9 @@ def test_download_scores_file(session, data_provider, client, setup_router_db, d
21052155
publish_score_set = publish_score_set_response.json()
21062156
print(publish_score_set)
21072157

2108-
download_scores_csv_response = client.get(f"/api/v1/score-sets/{publish_score_set['urn']}/scores?drop_na_columns=true")
2158+
download_scores_csv_response = client.get(
2159+
f"/api/v1/score-sets/{publish_score_set['urn']}/scores?drop_na_columns=true"
2160+
)
21092161
assert download_scores_csv_response.status_code == 200
21102162
download_scores_csv = download_scores_csv_response.text
21112163
csv_header = download_scores_csv.split("\n")[0]
@@ -2118,15 +2170,20 @@ def test_download_scores_file(session, data_provider, client, setup_router_db, d
21182170
def test_download_counts_file(session, data_provider, client, setup_router_db, data_files):
21192171
experiment = create_experiment(client)
21202172
score_set = create_seq_score_set_with_variants(
2121-
client, session, data_provider, experiment["urn"],
2173+
client,
2174+
session,
2175+
data_provider,
2176+
experiment["urn"],
21222177
scores_csv_path=data_files / "scores.csv",
2123-
counts_csv_path = data_files / "counts.csv"
2178+
counts_csv_path=data_files / "counts.csv",
21242179
)
21252180
publish_score_set_response = client.post(f"/api/v1/score-sets/{score_set['urn']}/publish")
21262181
assert publish_score_set_response.status_code == 200
21272182
publish_score_set = publish_score_set_response.json()
21282183

2129-
download_counts_csv_response = client.get(f"/api/v1/score-sets/{publish_score_set['urn']}/counts?drop_na_columns=true")
2184+
download_counts_csv_response = client.get(
2185+
f"/api/v1/score-sets/{publish_score_set['urn']}/counts?drop_na_columns=true"
2186+
)
21302187
assert download_counts_csv_response.status_code == 200
21312188
download_counts_csv = download_counts_csv_response.text
21322189
csv_header = download_counts_csv.split("\n")[0]

0 commit comments

Comments
 (0)