Skip to content

Commit af7cddb

Browse files
committed
fix: robust synthetic property handling for variants
- Refactored Variant view models to ensure robust handling of synthetic and required fields for both ORM and dict contexts. - Added tests to verify all key attributes and synthetic properties are correctly handled in both construction modes. - Ensured creation from both dict and ORM contexts, mirroring the approach used for other models.
1 parent 9c95538 commit af7cddb

File tree

2 files changed

+70
-13
lines changed

2 files changed

+70
-13
lines changed

src/mavedb/view_models/variant.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
from pydantic import model_validator
55

66
from mavedb.lib.validation.exceptions import ValidationError
7-
from mavedb.view_models.mapped_variant import MappedVariant, SavedMappedVariant
87
from mavedb.view_models import record_type_validator, set_record_type
98
from mavedb.view_models.base.base import BaseModel
9+
from mavedb.view_models.mapped_variant import MappedVariant, SavedMappedVariant
1010

1111

1212
class VariantEffectMeasurementBase(BaseModel):
@@ -51,18 +51,19 @@ class SavedVariantEffectMeasurementWithMappedVariant(SavedVariantEffectMeasureme
5151

5252
mapped_variant: Optional[SavedMappedVariant] = None
5353

54+
# These 'synthetic' fields are generated from other model properties. Transform data from other properties as needed, setting
55+
# the appropriate field on the model itself. Then, proceed with Pydantic ingestion once fields are created. Only perform these
56+
# transformations if the relevant attributes are present on the input data (i.e., when creating from an ORM object).
5457
@model_validator(mode="before")
55-
def generate_score_set_urn_list(cls, data: Any):
56-
if not hasattr(data, "mapped_variant"):
58+
def generate_associated_mapped_variant(cls, data: Any):
59+
if hasattr(data, "mapped_variants"):
5760
try:
58-
mapped_variant = None
59-
if data.mapped_variants:
60-
mapped_variant = next(
61-
mapped_variant for mapped_variant in data.mapped_variants if mapped_variant.current
62-
)
61+
mapped_variant = next(
62+
(mapped_variant for mapped_variant in data.mapped_variants if mapped_variant.current), None
63+
)
6364
data.__setattr__("mapped_variant", mapped_variant)
64-
except AttributeError as exc:
65-
raise ValidationError(f"Unable to create {cls.__name__} without attribute: {exc}.") # type: ignore
65+
except (AttributeError, KeyError) as exc:
66+
raise ValidationError(f"Unable to coerce mapped variant for {cls.__name__}: {exc}.") # type: ignore
6667
return data
6768

6869

tests/view_models/test_variant.py

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1-
from mavedb.view_models.variant import VariantEffectMeasurementCreate, VariantEffectMeasurement
2-
1+
from mavedb.view_models.variant import (
2+
SavedVariantEffectMeasurementWithMappedVariant,
3+
VariantEffectMeasurement,
4+
VariantEffectMeasurementCreate,
5+
)
6+
from tests.helpers.constants import (
7+
TEST_MINIMAL_MAPPED_VARIANT,
8+
TEST_MINIMAL_VARIANT,
9+
TEST_POPULATED_VARIANT,
10+
TEST_SAVED_VARIANT,
11+
)
312
from tests.helpers.util.common import dummy_attributed_object_from_dict
4-
from tests.helpers.constants import TEST_MINIMAL_VARIANT, TEST_POPULATED_VARIANT, TEST_SAVED_VARIANT
513

614

715
def test_minimal_variant_create():
@@ -19,3 +27,51 @@ def test_saved_variant():
1927
dummy_attributed_object_from_dict({**TEST_SAVED_VARIANT, "score_set_id": 1})
2028
)
2129
assert all(variant.__getattribute__(k) == v for k, v in TEST_SAVED_VARIANT.items())
30+
31+
32+
def test_can_create_saved_variant_with_mapping_with_all_attributed_properties():
33+
variant = TEST_SAVED_VARIANT.copy()
34+
variant["score_set_id"] = 1
35+
variant["mapped_variants"] = [
36+
dummy_attributed_object_from_dict(
37+
{
38+
**TEST_MINIMAL_MAPPED_VARIANT,
39+
"id": 1,
40+
"variant": dummy_attributed_object_from_dict({"urn": "urn:mavedb:variant-xxx"}),
41+
}
42+
)
43+
]
44+
variant_attributed_object = dummy_attributed_object_from_dict(variant)
45+
saved_variant = SavedVariantEffectMeasurementWithMappedVariant.model_validate(variant_attributed_object)
46+
assert saved_variant.mapped_variant is not None
47+
assert saved_variant.mapped_variant.variant_urn == "urn:mavedb:variant-xxx"
48+
for k, v in TEST_SAVED_VARIANT.items():
49+
assert saved_variant.__getattribute__(k) == v
50+
51+
52+
# Missing attributed properties here are unproblematic, as they are optional on the view model.
53+
def test_can_create_saved_variant_with_mapping_with_missing_attributed_properties():
54+
variant = TEST_SAVED_VARIANT.copy()
55+
variant.pop("mapped_variants", None)
56+
variant["score_set_id"] = 1
57+
58+
variant_attributed_object = dummy_attributed_object_from_dict(variant)
59+
saved_variant = SavedVariantEffectMeasurementWithMappedVariant.model_validate(variant_attributed_object)
60+
for k, v in TEST_SAVED_VARIANT.items():
61+
assert saved_variant.__getattribute__(k) == v
62+
63+
64+
def test_can_create_saved_variant_with_mapping_from_non_orm_context():
65+
variant = TEST_SAVED_VARIANT.copy()
66+
variant["score_set_id"] = 1
67+
variant["mapped_variant"] = {
68+
**TEST_MINIMAL_MAPPED_VARIANT,
69+
"id": 1,
70+
"variant_urn": "urn:mavedb:variant-xxx",
71+
}
72+
73+
saved_variant = SavedVariantEffectMeasurementWithMappedVariant.model_validate(variant)
74+
assert saved_variant.mapped_variant is not None
75+
assert saved_variant.mapped_variant.variant_urn == "urn:mavedb:variant-xxx"
76+
for k, v in TEST_SAVED_VARIANT.items():
77+
assert saved_variant.__getattribute__(k) == v

0 commit comments

Comments
 (0)