Skip to content

Commit 8ee4c1a

Browse files
committed
fix: use functional classification enum in place of old style strings
1 parent 4962f4f commit 8ee4c1a

File tree

4 files changed

+40
-26
lines changed

4 files changed

+40
-26
lines changed

src/mavedb/scripts/load_calibration_csv.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
import csv
9393
import re
9494
from pathlib import Path
95-
from typing import Any, Dict, List, Literal, Optional, Tuple
95+
from typing import Any, Dict, List, Optional, Tuple
9696

9797
import click
9898
from sqlalchemy.orm import Session
@@ -101,6 +101,7 @@
101101
from mavedb.lib.oddspaths import oddspaths_evidence_strength_equivalent
102102
from mavedb.lib.score_calibrations import create_score_calibration_in_score_set
103103
from mavedb.models import score_calibration
104+
from mavedb.models.enums.functional_classification import FunctionalClassification as FunctionalClassifcationOptions
104105
from mavedb.models.score_set import ScoreSet
105106
from mavedb.models.user import User
106107
from mavedb.scripts.environment import with_database_session
@@ -152,23 +153,21 @@ def parse_interval(text: str) -> Tuple[Optional[float], Optional[float], bool, b
152153
return lower, upper, inclusive_lower, inclusive_upper
153154

154155

155-
def normalize_classification(
156-
raw: Optional[str], strength: Optional[str]
157-
) -> Literal["normal", "abnormal", "not_specified"]:
156+
def normalize_classification(raw: Optional[str], strength: Optional[str]) -> FunctionalClassifcationOptions:
158157
if raw:
159158
r = raw.strip().lower()
160159
if r in {"normal", "abnormal", "not_specified"}:
161-
return r # type: ignore[return-value]
160+
return FunctionalClassifcationOptions[r]
162161
if r in {"indeterminate", "uncertain", "unknown"}:
163-
return "not_specified"
162+
return FunctionalClassifcationOptions.not_specified
164163

165164
if strength:
166165
if strength.upper().startswith("PS"):
167-
return "abnormal"
166+
return FunctionalClassifcationOptions.abnormal
168167
if strength.upper().startswith("BS"):
169-
return "normal"
168+
return FunctionalClassifcationOptions.normal
170169

171-
return "not_specified"
170+
return FunctionalClassifcationOptions.not_specified
172171

173172

174173
def build_publications(

src/mavedb/scripts/load_pp_style_calibration.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
from sqlalchemy.orm import Session
8585

8686
from mavedb.lib.score_calibrations import create_score_calibration_in_score_set
87+
from mavedb.models.enums.functional_classification import FunctionalClassification as FunctionalClassifcationOptions
8788
from mavedb.models.score_calibration import ScoreCalibration
8889
from mavedb.models.score_set import ScoreSet
8990
from mavedb.models.user import User
@@ -214,7 +215,9 @@ def main(db: Session, archive_path: str, dataset_map: str, overwrite: bool) -> N
214215

215216
functional_range = score_calibration.FunctionalClassificationCreate(
216217
label=f"{ps_or_bs} {strength_label} ({points})",
217-
classification="abnormal" if points > 0 else "normal",
218+
classification=FunctionalClassifcationOptions.abnormal
219+
if points > 0
220+
else FunctionalClassifcationOptions.normal,
218221
range=range_data,
219222
acmg_classification=acmg_classification.ACMGClassificationCreate(
220223
points=int(points),

tests/helpers/constants.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from humps import camelize
44

5+
from mavedb.models.enums.functional_classification import FunctionalClassification as FunctionalClassificationOptions
56
from mavedb.models.enums.processing_state import ProcessingState
67

78
VALID_EXPERIMENT_SET_URN = "urn:mavedb:01234567"
@@ -1411,7 +1412,7 @@
14111412
TEST_FUNCTIONAL_RANGE_NORMAL = {
14121413
"label": "test normal functional range",
14131414
"description": "A normal functional range",
1414-
"functional_classification": "normal",
1415+
"functional_classification": FunctionalClassificationOptions.normal.value,
14151416
"range": [1.0, 5.0],
14161417
"acmg_classification": TEST_ACMG_BS3_STRONG_CLASSIFICATION,
14171418
"oddspaths_ratio": TEST_BS3_STRONG_ODDS_PATH_RATIO,
@@ -1431,7 +1432,7 @@
14311432
TEST_FUNCTIONAL_RANGE_ABNORMAL = {
14321433
"label": "test abnormal functional range",
14331434
"description": "An abnormal functional range",
1434-
"functional_classification": "abnormal",
1435+
"functional_classification": FunctionalClassificationOptions.abnormal.value,
14351436
"range": [-5.0, -1.0],
14361437
"acmg_classification": TEST_ACMG_PS3_STRONG_CLASSIFICATION,
14371438
"oddspaths_ratio": TEST_PS3_STRONG_ODDS_PATH_RATIO,
@@ -1450,7 +1451,7 @@
14501451

14511452
TEST_FUNCTIONAL_RANGE_NOT_SPECIFIED = {
14521453
"label": "test not specified functional range",
1453-
"functional_classification": "not_specified",
1454+
"functional_classification": FunctionalClassificationOptions.not_specified.value,
14541455
"range": [-1.0, 1.0],
14551456
"inclusive_lower_bound": True,
14561457
"inclusive_upper_bound": False,
@@ -1467,7 +1468,7 @@
14671468
TEST_FUNCTIONAL_CLASSIFICATION_NORMAL = {
14681469
"label": "test normal functional class",
14691470
"description": "A normal functional class",
1470-
"functional_classification": "normal",
1471+
"functional_classification": FunctionalClassificationOptions.normal.value,
14711472
"class": "normal_class",
14721473
"acmg_classification": TEST_ACMG_BS3_STRONG_CLASSIFICATION,
14731474
"oddspaths_ratio": TEST_BS3_STRONG_ODDS_PATH_RATIO,
@@ -1485,7 +1486,7 @@
14851486
TEST_FUNCTIONAL_CLASSIFICATION_ABNORMAL = {
14861487
"label": "test abnormal functional class",
14871488
"description": "An abnormal functional class",
1488-
"functional_classification": "abnormal",
1489+
"functional_classification": FunctionalClassificationOptions.abnormal.value,
14891490
"class": "abnormal_class",
14901491
"acmg_classification": TEST_ACMG_PS3_STRONG_CLASSIFICATION,
14911492
"oddspaths_ratio": TEST_PS3_STRONG_ODDS_PATH_RATIO,
@@ -1502,7 +1503,7 @@
15021503

15031504
TEST_FUNCTIONAL_CLASSIFICATION_NOT_SPECIFIED = {
15041505
"label": "test not specified functional class",
1505-
"functional_classification": "not_specified",
1506+
"functional_classification": FunctionalClassificationOptions.not_specified.value,
15061507
"class": "not_specified_class",
15071508
}
15081509

@@ -1517,7 +1518,7 @@
15171518
TEST_FUNCTIONAL_RANGE_INCLUDING_NEGATIVE_INFINITY = {
15181519
"label": "test functional range including negative infinity",
15191520
"description": "A functional range including negative infinity",
1520-
"functional_classification": "not_specified",
1521+
"functional_classification": FunctionalClassificationOptions.not_specified.value,
15211522
"range": [None, 0.0],
15221523
"inclusive_lower_bound": False,
15231524
"inclusive_upper_bound": False,
@@ -1533,7 +1534,7 @@
15331534
TEST_FUNCTIONAL_RANGE_INCLUDING_POSITIVE_INFINITY = {
15341535
"label": "test functional range including positive infinity",
15351536
"description": "A functional range including positive infinity",
1536-
"functional_classification": "not_specified",
1537+
"functional_classification": FunctionalClassificationOptions.not_specified.value,
15371538
"range": [0.0, None],
15381539
"inclusive_lower_bound": False,
15391540
"inclusive_upper_bound": False,

tests/view_models/test_score_calibration.py

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from pydantic import ValidationError
55

66
from mavedb.lib.acmg import ACMGCriterion
7+
from mavedb.models.enums.functional_classification import FunctionalClassification as FunctionalClassificationOptions
78
from mavedb.models.enums.score_calibration_relation import ScoreCalibrationRelation
89
from mavedb.view_models.score_calibration import (
910
FunctionalClassificationCreate,
@@ -182,7 +183,7 @@ def test_is_contained_by_range():
182183
fr = FunctionalClassificationCreate.model_validate(
183184
{
184185
"label": "test range",
185-
"functional_classification": "abnormal",
186+
"functional_classification": FunctionalClassificationOptions.abnormal,
186187
"range": (0.0, 1.0),
187188
"inclusive_lower_bound": True,
188189
"inclusive_upper_bound": True,
@@ -205,7 +206,7 @@ def test_inclusive_bounds_get_default_when_unset_and_range_exists():
205206
fr = FunctionalClassificationCreate.model_validate(
206207
{
207208
"label": "test range",
208-
"functional_classification": "abnormal",
209+
"functional_classification": FunctionalClassificationOptions.abnormal,
209210
"range": (0.0, 1.0),
210211
}
211212
)
@@ -218,7 +219,7 @@ def test_inclusive_bounds_remain_none_when_range_is_none():
218219
fr = FunctionalClassificationCreate.model_validate(
219220
{
220221
"label": "test range",
221-
"functional_classification": "abnormal",
222+
"functional_classification": FunctionalClassificationOptions.abnormal,
222223
"class": "some_class",
223224
}
224225
)
@@ -245,7 +246,7 @@ def test_inclusive_bounds_remain_none_when_range_is_none():
245246
def test_cant_set_inclusive_bounds_when_range_is_none(bound_property, bound_value, match_text):
246247
invalid_data = {
247248
"label": "test range",
248-
"functional_classification": "abnormal",
249+
"functional_classification": FunctionalClassificationOptions.abnormal,
249250
"class": "some_class",
250251
bound_property: bound_value,
251252
}
@@ -373,7 +374,9 @@ def test_can_create_score_calibration_when_unclassified_ranges_overlap_with_clas
373374
# Make the first two ranges overlap, one being 'not_specified'
374375
valid_data["functional_classifications"][0]["range"] = [1.5, 3.0]
375376
valid_data["functional_classifications"][1]["range"] = [2.0, 4.0]
376-
valid_data["functional_classifications"][0]["functional_classification"] = "not_specified"
377+
valid_data["functional_classifications"][0]["functional_classification"] = (
378+
FunctionalClassificationOptions.not_specified
379+
)
377380
sc = ScoreCalibrationCreate.model_validate(valid_data)
378381
assert len(sc.functional_classifications) == len(valid_data["functional_classifications"])
379382

@@ -383,8 +386,12 @@ def test_can_create_score_calibration_when_unclassified_ranges_overlap_with_each
383386
# Make the first two ranges overlap, both being 'not_specified'
384387
valid_data["functional_classifications"][0]["range"] = [1.5, 3.0]
385388
valid_data["functional_classifications"][1]["range"] = [2.0, 4.0]
386-
valid_data["functional_classifications"][0]["functional_classification"] = "not_specified"
387-
valid_data["functional_classifications"][1]["functional_classification"] = "not_specified"
389+
valid_data["functional_classifications"][0]["functional_classification"] = (
390+
FunctionalClassificationOptions.not_specified
391+
)
392+
valid_data["functional_classifications"][1]["functional_classification"] = (
393+
FunctionalClassificationOptions.not_specified
394+
)
388395
sc = ScoreCalibrationCreate.model_validate(valid_data)
389396
assert len(sc.functional_classifications) == len(valid_data["functional_classifications"])
390397

@@ -616,7 +623,11 @@ def test_cannot_create_score_calibration_with_mixed_range_and_class_based_functi
616623
invalid_data = deepcopy(TEST_BRNICH_SCORE_CALIBRATION_RANGE_BASED)
617624
# Add a class-based functional classification to a range-based calibration
618625
invalid_data["functional_classifications"].append(
619-
{"label": "class based classification", "functional_classification": "abnormal", "class": "some_class"}
626+
{
627+
"label": "class based classification",
628+
"functional_classification": FunctionalClassificationOptions.abnormal,
629+
"class": "some_class",
630+
}
620631
)
621632

622633
with pytest.raises(

0 commit comments

Comments
 (0)