Skip to content

Commit fa5aedf

Browse files
committed
Allow ranges to contain a standalone baseline score
Previously, a score range with a baseline score had validation that required a normal score range. This has been temporarily (or perhaps not temporarily) relaxed. Because of this validation change, an associated change was also needed in annotation library code that checked the validity of incoming ranges for annotation.
1 parent e29eb2f commit fa5aedf

File tree

6 files changed

+48
-18
lines changed

6 files changed

+48
-18
lines changed

src/mavedb/lib/annotation/classification.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def functional_classification_of_variant(
3333
# This view model object is much simpler to work with.
3434
score_ranges = ScoreSetRanges(**mapped_variant.variant.score_set.score_ranges).investigator_provided
3535

36-
if not score_ranges:
36+
if not score_ranges or not score_ranges.ranges:
3737
raise ValueError(
3838
f"Variant {mapped_variant.variant.urn} does not have investigator-provided score ranges."
3939
" Unable to classify functional impact."
@@ -71,7 +71,7 @@ def zeiberg_calibration_clinical_classification_of_variant(
7171

7272
score_ranges = ScoreSetRanges(**mapped_variant.variant.score_set.score_ranges).zeiberg_calibration
7373

74-
if not score_ranges:
74+
if not score_ranges or not score_ranges.ranges:
7575
raise ValueError(
7676
f"Variant {mapped_variant.variant.urn} does not have pillar project score ranges."
7777
" Unable to classify clinical impact."

src/mavedb/lib/annotation/util.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ def _can_annotate_variant_base_assumptions(mapped_variant: MappedVariant) -> boo
162162
return True
163163

164164

165-
def _variant_score_ranges_have_required_keys_for_annotation(
165+
def _variant_score_ranges_have_required_keys_and_ranges_for_annotation(
166166
mapped_variant: MappedVariant, key_options: list[str]
167167
) -> bool:
168168
"""
@@ -173,7 +173,8 @@ def _variant_score_ranges_have_required_keys_for_annotation(
173173
key_options (list[str]): List of possible score range keys to check for in the score set.
174174
175175
Returns:
176-
bool: False if none of the required keys are found or if all found keys have None values.
176+
bool: False if none of the required keys are found or if all found keys have None values or if all found keys
177+
do not have range data.
177178
Returns True (implicitly) if at least one required key exists with a non-None value.
178179
"""
179180
if mapped_variant.variant.score_set.score_ranges is None:
@@ -182,6 +183,7 @@ def _variant_score_ranges_have_required_keys_for_annotation(
182183
if not any(
183184
range_key in mapped_variant.variant.score_set.score_ranges
184185
and mapped_variant.variant.score_set.score_ranges[range_key] is not None
186+
and mapped_variant.variant.score_set.score_ranges[range_key]["ranges"]
185187
for range_key in key_options
186188
):
187189
return False
@@ -209,14 +211,14 @@ def can_annotate_variant_for_pathogenicity_evidence(mapped_variant: MappedVarian
209211
Notes:
210212
The function performs two main validation checks:
211213
1. Basic annotation assumptions via _can_annotate_variant_base_assumptions
212-
2. Required clinical range keys via _variant_score_ranges_have_required_keys_for_annotation
214+
2. Required clinical range keys via _variant_score_ranges_have_required_keys_and_ranges_for_annotation
213215
214216
Both checks must pass for the variant to be considered eligible for
215217
pathogenicity evidence annotation.
216218
"""
217219
if not _can_annotate_variant_base_assumptions(mapped_variant):
218220
return False
219-
if not _variant_score_ranges_have_required_keys_for_annotation(mapped_variant, CLINICAL_RANGES):
221+
if not _variant_score_ranges_have_required_keys_and_ranges_for_annotation(mapped_variant, CLINICAL_RANGES):
220222
return False
221223

222224
return True
@@ -245,7 +247,7 @@ def can_annotate_variant_for_functional_statement(mapped_variant: MappedVariant)
245247
"""
246248
if not _can_annotate_variant_base_assumptions(mapped_variant):
247249
return False
248-
if not _variant_score_ranges_have_required_keys_for_annotation(mapped_variant, FUNCTIONAL_RANGES):
250+
if not _variant_score_ranges_have_required_keys_and_ranges_for_annotation(mapped_variant, FUNCTIONAL_RANGES):
249251
return False
250252

251253
return True

src/mavedb/view_models/score_range.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,9 @@ def validate_baseline_score(self: "BrnichScoreRangesBase") -> "BrnichScoreRanges
205205

206206
if baseline_score is not None:
207207
if not any(range_model.classification == "normal" for range_model in ranges):
208-
raise ValidationError("A baseline score has been provided, but no normal classification range exists.")
208+
# For now, we do not raise an error if a baseline score is provided but no normal range exists.
209+
# raise ValidationError("A baseline score has been provided, but no normal classification range exists.")
210+
return self
209211

210212
normal_ranges = [range_model.range for range_model in ranges if range_model.classification == "normal"]
211213

tests/lib/annotation/test_annotate.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@ def test_variant_functional_impact_statement_no_score_ranges(mock_mapped_variant
1919
assert result is None
2020

2121

22+
def test_variant_functional_impact_statement_no_score_range_data(mock_mapped_variant):
23+
mock_mapped_variant.variant.score_set.score_ranges["investigator_provided"]["ranges"] = []
24+
result = variant_functional_impact_statement(mock_mapped_variant)
25+
26+
assert result is None
27+
28+
2229
def test_variant_functional_impact_statement_no_score(mock_mapped_variant):
2330
mock_mapped_variant.variant.data = {"score_data": {"score": None}}
2431
result = variant_functional_impact_statement(mock_mapped_variant)
@@ -73,6 +80,13 @@ def test_variant_pathogenicity_evidence_with_score_ranges_no_thresholds(mock_map
7380
assert result is None
7481

7582

83+
def test_variant_pathogenicity_evidence_with_score_ranges_no_threshold_data(mock_mapped_variant):
84+
mock_mapped_variant.variant.score_set.score_ranges["zeiberg_calibration"]["ranges"] = []
85+
result = variant_pathogenicity_evidence(mock_mapped_variant)
86+
87+
assert result is None
88+
89+
7690
def test_variant_pathogenicity_evidence_with_score_ranges_with_thresholds(mock_mapped_variant):
7791
result = variant_pathogenicity_evidence(mock_mapped_variant)
7892

tests/lib/annotation/test_util.py

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@
44
from mavedb.lib.annotation.util import (
55
variation_from_mapped_variant,
66
_can_annotate_variant_base_assumptions,
7-
_variant_score_ranges_have_required_keys_for_annotation,
7+
_variant_score_ranges_have_required_keys_and_ranges_for_annotation,
88
can_annotate_variant_for_functional_statement,
99
can_annotate_variant_for_pathogenicity_evidence,
1010
)
1111

12-
from tests.helpers.constants import TEST_VALID_POST_MAPPED_VRS_ALLELE, TEST_SEQUENCE_LOCATION_ACCESSION
12+
from tests.helpers.constants import (
13+
TEST_VALID_POST_MAPPED_VRS_ALLELE,
14+
TEST_SEQUENCE_LOCATION_ACCESSION,
15+
TEST_BRNICH_SCORE_SET_RANGE_WITH_SOURCE,
16+
)
1317
from unittest.mock import patch
1418

1519

@@ -53,28 +57,35 @@ def test_score_range_check_returns_false_when_keys_are_none(mock_mapped_variant)
5357
mock_mapped_variant.variant.score_set.score_ranges = None
5458
key_options = ["required_key1", "required_key2"]
5559

56-
assert _variant_score_ranges_have_required_keys_for_annotation(mock_mapped_variant, key_options) is False
60+
assert _variant_score_ranges_have_required_keys_and_ranges_for_annotation(mock_mapped_variant, key_options) is False
5761

5862

5963
def test_score_range_check_returns_false_when_no_keys_present(mock_mapped_variant):
60-
mock_mapped_variant.variant.score_set.score_ranges = {"other_key": "value"}
64+
mock_mapped_variant.variant.score_set.score_ranges = {"other_key": TEST_BRNICH_SCORE_SET_RANGE_WITH_SOURCE}
6165
key_options = ["required_key1", "required_key2"]
6266

63-
assert _variant_score_ranges_have_required_keys_for_annotation(mock_mapped_variant, key_options) is False
67+
assert _variant_score_ranges_have_required_keys_and_ranges_for_annotation(mock_mapped_variant, key_options) is False
6468

6569

6670
def test_score_range_check_returns_false_when_key_present_but_value_is_none(mock_mapped_variant):
6771
mock_mapped_variant.variant.score_set.score_ranges = {"required_key1": None}
6872
key_options = ["required_key1", "required_key2"]
6973

70-
assert _variant_score_ranges_have_required_keys_for_annotation(mock_mapped_variant, key_options) is False
74+
assert _variant_score_ranges_have_required_keys_and_ranges_for_annotation(mock_mapped_variant, key_options) is False
75+
76+
77+
def test_score_range_check_returns_false_when_key_present_but_range_value_is_empty(mock_mapped_variant):
78+
mock_mapped_variant.variant.score_set.score_ranges = {"required_key1": {"ranges": []}}
79+
key_options = ["required_key1", "required_key2"]
80+
81+
assert _variant_score_ranges_have_required_keys_and_ranges_for_annotation(mock_mapped_variant, key_options) is False
7182

7283

7384
def test_score_range_check_returns_none_when_at_least_one_key_has_value(mock_mapped_variant):
74-
mock_mapped_variant.variant.score_set.score_ranges = {"required_key1": "value"}
85+
mock_mapped_variant.variant.score_set.score_ranges = {"required_key1": TEST_BRNICH_SCORE_SET_RANGE_WITH_SOURCE}
7586
key_options = ["required_key1", "required_key2"]
7687

77-
assert _variant_score_ranges_have_required_keys_for_annotation(mock_mapped_variant, key_options) is True
88+
assert _variant_score_ranges_have_required_keys_and_ranges_for_annotation(mock_mapped_variant, key_options) is True
7889

7990

8091
## Test clinical range check
@@ -89,7 +100,7 @@ def test_clinical_range_check_returns_false_when_base_assumptions_fail(mock_mapp
89100

90101
@pytest.mark.parametrize("clinical_ranges", [["clinical_range"], ["other_clinical_range"]])
91102
def test_clinical_range_check_returns_false_when_clinical_ranges_check_fails(mock_mapped_variant, clinical_ranges):
92-
mock_mapped_variant.variant.score_set.score_ranges = {"unrelated_key": "value"}
103+
mock_mapped_variant.variant.score_set.score_ranges = {"unrelated_key": TEST_BRNICH_SCORE_SET_RANGE_WITH_SOURCE}
93104

94105
with patch("mavedb.lib.annotation.util.CLINICAL_RANGES", clinical_ranges):
95106
result = can_annotate_variant_for_pathogenicity_evidence(mock_mapped_variant)
@@ -116,7 +127,7 @@ def test_functional_range_check_returns_false_when_base_assumptions_fail(mock_ma
116127
def test_functional_range_check_returns_false_when_functional_ranges_check_fails(
117128
mock_mapped_variant, functional_ranges
118129
):
119-
mock_mapped_variant.variant.score_set.score_ranges = {"unrelated_key": "value"}
130+
mock_mapped_variant.variant.score_set.score_ranges = {"unrelated_key": TEST_BRNICH_SCORE_SET_RANGE_WITH_SOURCE}
120131

121132
with patch("mavedb.lib.annotation.util.FUNCTIONAL_RANGES", functional_ranges):
122133
result = can_annotate_variant_for_functional_statement(mock_mapped_variant)

tests/view_models/test_score_range.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,7 @@ def test_score_ranges_zeiberg_calibration_ranges_boundaries_may_be_adjacent(
646646
ScoreRangesModel(**valid_data)
647647

648648

649+
@pytest.mark.skip("Not applicable currently. Baseline score may be provided on its own.")
649650
@pytest.mark.parametrize(
650651
"ScoreRangesModel",
651652
[

0 commit comments

Comments
 (0)