|
| 1 | +"""Define Pydantic models for GA4GH categorical variation objects. |
| 2 | +
|
| 3 | +See the `CatVar page <https://www.ga4gh.org/product/categorical-variation-catvar/>`_ on |
| 4 | +the GA4GH website for more information. |
| 5 | +""" |
| 6 | + |
| 7 | +from enum import Enum |
| 8 | +from typing import Literal |
| 9 | + |
| 10 | +from ga4gh.core.models import ( |
| 11 | + ConceptMapping, |
| 12 | + Entity, |
| 13 | + MappableConcept, |
| 14 | + iriReference, |
| 15 | +) |
| 16 | +from ga4gh.vrs.models import Allele, CopyChange, Range, SequenceLocation, Variation |
| 17 | +from pydantic import BaseModel, Field, RootModel, field_validator |
| 18 | + |
| 19 | + |
| 20 | +class Relation(str, Enum): |
| 21 | + """Defined relationships between members of the categorical variant and the defining |
| 22 | + context. |
| 23 | + """ |
| 24 | + |
| 25 | + TRANSLATES_FROM = "translates_from" |
| 26 | + LIFTOVER_TO = "liftover_to" |
| 27 | + TRANSCRIBES_TO = "transcribes_to" |
| 28 | + |
| 29 | + |
| 30 | +class DefiningAlleleConstraint(BaseModel): |
| 31 | + """The defining allele and its associated relationships that are congruent with |
| 32 | + member variants. |
| 33 | + """ |
| 34 | + |
| 35 | + type: Literal["DefiningAlleleConstraint"] = Field( |
| 36 | + "DefiningAlleleConstraint", description="MUST be 'DefiningAlleleConstraint'" |
| 37 | + ) |
| 38 | + allele: Allele | iriReference |
| 39 | + relations: list[MappableConcept] | None = Field( |
| 40 | + None, |
| 41 | + description="Defined relationships from which members relate to the defining allele.", |
| 42 | + ) |
| 43 | + |
| 44 | + |
| 45 | +class DefiningLocationConstraint(BaseModel): |
| 46 | + """The defining location and its associated relationships that are congruent with |
| 47 | + member locations. |
| 48 | + """ |
| 49 | + |
| 50 | + type: Literal["DefiningLocationConstraint"] = Field( |
| 51 | + "DefiningLocationConstraint", description="MUST be 'DefiningLocationConstraint'" |
| 52 | + ) |
| 53 | + location: SequenceLocation | iriReference |
| 54 | + relations: list[MappableConcept] | None = Field( |
| 55 | + None, |
| 56 | + description="Defined relationships from which members relate to the defining location.", |
| 57 | + ) |
| 58 | + matchCharacteristic: MappableConcept = Field( |
| 59 | + ..., |
| 60 | + description="A characteristic of the location that is used to match the defining location to member locations.", |
| 61 | + ) |
| 62 | + |
| 63 | + |
| 64 | +class CopyCountConstraint(BaseModel): |
| 65 | + """The exact or range of copies that members of this categorical variant must |
| 66 | + satisfy. |
| 67 | + """ |
| 68 | + |
| 69 | + type: Literal["CopyCountConstraint"] = Field( |
| 70 | + "CopyCountConstraint", description="MUST be 'CopyCountConstraint'" |
| 71 | + ) |
| 72 | + copies: int | Range = Field( |
| 73 | + ..., |
| 74 | + description="The precise value or range of copies members of this categorical variant must satisfy.", |
| 75 | + ) |
| 76 | + |
| 77 | + |
| 78 | +class CopyChangeConstraint(BaseModel): |
| 79 | + """A representation of copy number change""" |
| 80 | + |
| 81 | + type: Literal["CopyChangeConstraint"] = Field( |
| 82 | + "CopyChangeConstraint", description="MUST be 'CopyChangeConstraint'" |
| 83 | + ) |
| 84 | + copyChange: str = Field( |
| 85 | + ..., |
| 86 | + description="The relative assessment of the change in copies that members of this categorical variant satisfies.", |
| 87 | + ) |
| 88 | + |
| 89 | + @field_validator("copyChange") |
| 90 | + @classmethod |
| 91 | + def validate_copy_change(cls, v: str) -> str: |
| 92 | + """Validate copyChange property |
| 93 | +
|
| 94 | + :param v: copyChange value |
| 95 | + :raises ValueError: If ``copyChange.code`` is not a valid CopyChange |
| 96 | + :return: copyChange property |
| 97 | + """ |
| 98 | + try: |
| 99 | + CopyChange(v) |
| 100 | + except ValueError as e: |
| 101 | + err_msg = f"copyChange, {v}, not one of {[cc.value for cc in CopyChange]}" |
| 102 | + raise ValueError(err_msg) from e |
| 103 | + return v |
| 104 | + |
| 105 | + |
| 106 | +class Constraint(RootModel): |
| 107 | + """Constraints are used to construct an intensional semantics of categorical variant types.""" |
| 108 | + |
| 109 | + root: ( |
| 110 | + DefiningAlleleConstraint |
| 111 | + | DefiningLocationConstraint |
| 112 | + | CopyCountConstraint |
| 113 | + | CopyChangeConstraint |
| 114 | + ) = Field(..., discriminator="type") |
| 115 | + |
| 116 | + |
| 117 | +class CategoricalVariant(Entity): |
| 118 | + """A representation of a categorically-defined domain for variation, in which |
| 119 | + individual Constraintual variation instances may be members of the domain. |
| 120 | + """ |
| 121 | + |
| 122 | + type: Literal["CategoricalVariant"] = Field( |
| 123 | + "CategoricalVariant", description="MUST be 'CategoricalVariant'" |
| 124 | + ) |
| 125 | + members: list[Variation | iriReference] | None = Field( |
| 126 | + None, |
| 127 | + description="A non-exhaustive list of VRS variation Constraints that satisfy the constraints of this categorical variant.", |
| 128 | + ) |
| 129 | + constraints: list[Constraint] | None = None |
| 130 | + mappings: list[ConceptMapping] | None = Field( |
| 131 | + None, |
| 132 | + description="A list of mappings to concepts in terminologies or code systems. Each mapping should include a coding and a relation.", |
| 133 | + ) |
0 commit comments