Skip to content

Commit 31e69c7

Browse files
committed
feat: allow 'not_specified' classifications to overlap in score calibration ranges
1 parent 00602eb commit 31e69c7

File tree

2 files changed

+35
-13
lines changed

2 files changed

+35
-13
lines changed

src/mavedb/view_models/score_calibration.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@
1717
)
1818
from mavedb.lib.validation.utilities import inf_or_float
1919
from mavedb.view_models import record_type_validator, set_record_type
20-
from mavedb.view_models.base.base import BaseModel
2120
from mavedb.view_models.acmg_classification import (
21+
ACMGClassification,
2222
ACMGClassificationBase,
2323
ACMGClassificationCreate,
2424
ACMGClassificationModify,
2525
SavedACMGClassification,
26-
ACMGClassification,
2726
)
27+
from mavedb.view_models.base.base import BaseModel
2828
from mavedb.view_models.publication_identifier import (
2929
PublicationIdentifier,
3030
PublicationIdentifierBase,
@@ -33,7 +33,6 @@
3333
)
3434
from mavedb.view_models.user import SavedUser, User
3535

36-
3736
### Functional range models
3837

3938

@@ -197,6 +196,10 @@ def ranges_do_not_overlap(
197196
"""Ensure that no two functional ranges overlap (respecting inclusivity)."""
198197

199198
def test_overlap(range_test: FunctionalRangeBase, range_check: FunctionalRangeBase) -> bool:
199+
# Allow 'not_specified' classifications to overlap with anything.
200+
if range_test.classification == "not_specified" or range_check.classification == "not_specified":
201+
return False
202+
200203
if min(inf_or_float(range_test.range[0], True), inf_or_float(range_check.range[0], True)) == inf_or_float(
201204
range_test.range[0], True
202205
):
@@ -223,7 +226,7 @@ def test_overlap(range_test: FunctionalRangeBase, range_check: FunctionalRangeBa
223226
for b in list(field_value)[i + 1 :]:
224227
if test_overlap(a, b):
225228
raise ValidationError(
226-
f"Score ranges may not overlap; `{a.label}` ({a.range}) overlaps with `{b.label}` ({b.range}).",
229+
f"Classified score ranges may not overlap; `{a.label}` ({a.range}) overlaps with `{b.label}` ({b.range}). To allow overlap, set one or both classifications to 'not_specified'.",
227230
custom_loc=["body", i, "range"],
228231
)
229232
return field_value

tests/view_models/test_score_calibration.py

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,19 @@
1111
ScoreCalibrationCreate,
1212
ScoreCalibrationWithScoreSetUrn,
1313
)
14-
1514
from tests.helpers.constants import (
16-
TEST_FUNCTIONAL_RANGE_NORMAL,
15+
TEST_BRNICH_SCORE_CALIBRATION,
1716
TEST_FUNCTIONAL_RANGE_ABNORMAL,
18-
TEST_FUNCTIONAL_RANGE_NOT_SPECIFIED,
19-
TEST_FUNCTIONAL_RANGE_INCLUDING_POSITIVE_INFINITY,
2017
TEST_FUNCTIONAL_RANGE_INCLUDING_NEGATIVE_INFINITY,
21-
TEST_BRNICH_SCORE_CALIBRATION,
18+
TEST_FUNCTIONAL_RANGE_INCLUDING_POSITIVE_INFINITY,
19+
TEST_FUNCTIONAL_RANGE_NORMAL,
20+
TEST_FUNCTIONAL_RANGE_NOT_SPECIFIED,
2221
TEST_PATHOGENICITY_SCORE_CALIBRATION,
2322
TEST_SAVED_BRNICH_SCORE_CALIBRATION,
2423
TEST_SAVED_PATHOGENICITY_SCORE_CALIBRATION,
2524
)
2625
from tests.helpers.util.common import dummy_attributed_object_from_dict
2726

28-
2927
##############################################################################
3028
# Tests for FunctionalRange view models
3129
##############################################################################
@@ -260,22 +258,43 @@ def test_can_create_valid_score_calibration_without_functional_ranges(valid_cali
260258
assert sc.calibration_metadata is None
261259

262260

263-
def test_cannot_create_score_calibration_when_ranges_overlap():
261+
def test_cannot_create_score_calibration_when_classification_ranges_overlap():
264262
invalid_data = deepcopy(TEST_BRNICH_SCORE_CALIBRATION)
265263
# Make the first two ranges overlap
266264
invalid_data["functional_ranges"][0]["range"] = [1.0, 3.0]
267265
invalid_data["functional_ranges"][1]["range"] = [2.0, 4.0]
268-
with pytest.raises(ValidationError, match="Score ranges may not overlap; `"):
266+
with pytest.raises(ValidationError, match="Classified score ranges may not overlap; `"):
269267
ScoreCalibrationCreate.model_validate(invalid_data)
270268

271269

270+
def test_can_create_score_calibration_when_unclassified_ranges_overlap_with_classified_ranges():
271+
valid_data = deepcopy(TEST_BRNICH_SCORE_CALIBRATION)
272+
# Make the first two ranges overlap, one being 'not_specified'
273+
valid_data["functional_ranges"][0]["range"] = [1.5, 3.0]
274+
valid_data["functional_ranges"][1]["range"] = [2.0, 4.0]
275+
valid_data["functional_ranges"][0]["classification"] = "not_specified"
276+
sc = ScoreCalibrationCreate.model_validate(valid_data)
277+
assert len(sc.functional_ranges) == len(valid_data["functional_ranges"])
278+
279+
280+
def test_can_create_score_calibration_when_unclassified_ranges_overlap_with_each_other():
281+
valid_data = deepcopy(TEST_BRNICH_SCORE_CALIBRATION)
282+
# Make the first two ranges overlap, both being 'not_specified'
283+
valid_data["functional_ranges"][0]["range"] = [1.5, 3.0]
284+
valid_data["functional_ranges"][1]["range"] = [2.0, 4.0]
285+
valid_data["functional_ranges"][0]["classification"] = "not_specified"
286+
valid_data["functional_ranges"][1]["classification"] = "not_specified"
287+
sc = ScoreCalibrationCreate.model_validate(valid_data)
288+
assert len(sc.functional_ranges) == len(valid_data["functional_ranges"])
289+
290+
272291
def test_cannot_create_score_calibration_when_ranges_touch_with_inclusive_ranges():
273292
invalid_data = deepcopy(TEST_BRNICH_SCORE_CALIBRATION)
274293
# Make the first two ranges touch
275294
invalid_data["functional_ranges"][0]["range"] = [1.0, 2.0]
276295
invalid_data["functional_ranges"][1]["range"] = [2.0, 4.0]
277296
invalid_data["functional_ranges"][0]["inclusive_upper_bound"] = True
278-
with pytest.raises(ValidationError, match="Score ranges may not overlap; `"):
297+
with pytest.raises(ValidationError, match="Classified score ranges may not overlap; `"):
279298
ScoreCalibrationCreate.model_validate(invalid_data)
280299

281300

0 commit comments

Comments
 (0)