diff --git a/.gitmodules b/.gitmodules index 8459e75..cc31205 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "submodules/va_spec"] path = submodules/va_spec url = https://github.com/ga4gh/va-spec - branch = 1.0.0-ballot.2025-03 + branch = issue-319 diff --git a/src/ga4gh/va_spec/aac_2017/models.py b/src/ga4gh/va_spec/aac_2017/models.py index 320edcd..84acce0 100644 --- a/src/ga4gh/va_spec/aac_2017/models.py +++ b/src/ga4gh/va_spec/aac_2017/models.py @@ -63,7 +63,10 @@ def validate_strength(cls, v: MappableConcept | None) -> MappableConcept | None: :return: Validated strength value """ return validate_mappable_concept( - v, System.AMP_ASCO_CAP, AMP_ASCO_CAP_LEVELS, mc_is_required=False + v, + System.AMP_ASCO_CAP, + valid_codes=AMP_ASCO_CAP_LEVELS, + mc_is_required=False, ) @field_validator("classification") @@ -75,7 +78,9 @@ def validate_classification(cls, v: MappableConcept) -> MappableConcept: :raises ValueError: If invalid classification values are provided :return: Validated classification value """ - return validate_mappable_concept(v, System.AMP_ASCO_CAP, AMP_ASCO_CAP_TIERS) + return validate_mappable_concept( + v, System.AMP_ASCO_CAP, valid_codes=AMP_ASCO_CAP_TIERS + ) class VariantDiagnosticStudyStatement(BaseModel, AmpAscoCapValidatorMixin): diff --git a/src/ga4gh/va_spec/acmg_2015/__init__.py b/src/ga4gh/va_spec/acmg_2015/__init__.py index 4853d76..0228946 100644 --- a/src/ga4gh/va_spec/acmg_2015/__init__.py +++ b/src/ga4gh/va_spec/acmg_2015/__init__.py @@ -2,18 +2,14 @@ from .models import ( ACMG_CLASSIFICATIONS, - EVIDENCE_OUTCOME_VALUES, AcmgClassification, - EvidenceOutcome, - VariantPathogenicityFunctionalImpactEvidenceLine, + VariantPathogenicityEvidenceLine, VariantPathogenicityStatement, ) __all__ = [ "ACMG_CLASSIFICATIONS", - "EVIDENCE_OUTCOME_VALUES", "AcmgClassification", - "EvidenceOutcome", - "VariantPathogenicityFunctionalImpactEvidenceLine", + "VariantPathogenicityEvidenceLine", "VariantPathogenicityStatement", ] diff --git a/src/ga4gh/va_spec/acmg_2015/models.py b/src/ga4gh/va_spec/acmg_2015/models.py index e923bb1..3c2c6f1 100644 --- a/src/ga4gh/va_spec/acmg_2015/models.py +++ b/src/ga4gh/va_spec/acmg_2015/models.py @@ -24,22 +24,6 @@ from pydantic import BaseModel, Field, field_validator, model_validator -class EvidenceOutcome(str, Enum): - """Define constraints for evidence outcome values""" - - PS3 = "PS3" - PS3_MODERATE = "PS3_moderate" - PS3_SUPPORTING = "PS3_supporting" - PS3_NOT_MET = "PS3_not_met" - BS3 = "BS3" - BS3_MODERATE = "BS3_moderate" - BS3_SUPPORTING = "BS3_supporting" - BS3_NOT_MET = "BS3_not_met" - - -EVIDENCE_OUTCOME_VALUES = [v.value for v in EvidenceOutcome.__members__.values()] - - class AcmgClassification(str, Enum): """Define constraints for ACMG classifications""" @@ -53,17 +37,15 @@ class AcmgClassification(str, Enum): ACMG_CLASSIFICATIONS = [v.value for v in AcmgClassification.__members__.values()] -class VariantPathogenicityFunctionalImpactEvidenceLine( - BaseModel, EvidenceLineValidatorMixin -): - """An Evidence Line that describes how information about the functional impact of a - variant on a gene or gene product was interpreted as evidence for or against the - variant's pathogenicity. +class VariantPathogenicityEvidenceLine(BaseModel, EvidenceLineValidatorMixin): + """An Evidence Line that describes how information about the specific criterion + evidence for the variant was assessed as evidence for or against the variant's + pathogenicity. """ targetProposition: VariantPathogenicityProposition | None = Field( None, - description="A Variant Pathogenicity Proposition against which functional impact information was assessed, in determining the strength and direction of support this information provides as evidence.", + description="A Variant Pathogenicity Proposition against which specific information was assessed, in determining the strength and direction of support this information provides as evidence.", ) strengthOfEvidenceProvided: MappableConcept | None = Field( None, @@ -71,9 +53,41 @@ class VariantPathogenicityFunctionalImpactEvidenceLine( ) specifiedBy: Method | iriReference = Field( ..., - description="The guidelines that were followed to interpret variant functional impact information as evidence for or against the assessed variant's pathogenicity.", + description="The guidelines that were followed to assess variant information as evidence for or against the assessed variant's pathogenicity.", ) + class Criterion(str, Enum): + """Define ACMG 2015 criterion values""" + + PVS1 = "PVS1" + PS1 = "PS1" + PS2 = "PS2" + PS3 = "PS3" + PS4 = "PS4" + PM1 = "PM1" + PM2 = "PM2" + PM3 = "PM3" + PM4 = "PM4" + PM5 = "PM5" + PM6 = "PM6" + PP1 = "PP1" + PP2 = "PP2" + PP3 = "PP3" + PP4 = "PP4" + PP5 = "PP5" + BA1 = "BA1" + BS1 = "BS1" + BS2 = "BS2" + BS3 = "BS3" + BS4 = "BS4" + BP1 = "BP1" + BP2 = "BP2" + BP3 = "BP3" + BP4 = "BP4" + BP5 = "BP5" + BP6 = "BP6" + BP7 = "BP7" + @field_validator("strengthOfEvidenceProvided") @classmethod def validate_strength_of_evidence_provided( @@ -86,24 +100,12 @@ def validate_strength_of_evidence_provided( :return: Validated strengthOfEvidenceProvided value """ return validate_mappable_concept( - v, System.ACMG, STRENGTH_OF_EVIDENCE_PROVIDED_VALUES, mc_is_required=False + v, + System.ACMG, + valid_codes=STRENGTH_OF_EVIDENCE_PROVIDED_VALUES, + mc_is_required=False, ) - @field_validator("specifiedBy") - @classmethod - def validate_specified_by(cls, v: Method | iriReference) -> Method | iriReference: - """Validate specifiedBy - - :param v: specifiedBy - :raises ValueError: If invalid specifiedBy values are provided - :return: Validated specifiedBy value - """ - if isinstance(v, Method) and not v.reportedIn: - err_msg = "`reportedIn` is required." - raise ValueError(err_msg) - - return v - @model_validator(mode="before") def validate_evidence_outcome(cls, values: dict) -> dict: # noqa: N805 """Validate ``evidenceOutcome`` property if it exists @@ -113,9 +115,8 @@ def validate_evidence_outcome(cls, values: dict) -> dict: # noqa: N805 :return: Validated input values. If ``evidenceOutcome`` exists, then it will be validated and converted to a ``MappableConcept`` """ - return cls._validate_evidence_outcome( - values, System.ACMG, EVIDENCE_OUTCOME_VALUES - ) + acmg_code_pattern = r"^((?:PVS1)(?:_(?:not_met|(?:strong|moderate|supporting)))?|(?:PS[1-4]|BS[1-4])(?:_(?:not_met|(?:very_strong|moderate|supporting)))?|BA1(?:_not_met)?|(?:PM[1-6])(?:_(?:not_met|(?:very_strong|strong|supporting)))?|(PP[1-5]|BP[1-7])(?:_(?:not_met|very_strong|strong|moderate))?)$" + return cls._validate_evidence_outcome(values, System.ACMG, acmg_code_pattern) class VariantPathogenicityStatement(BaseModel, StatementValidatorMixin): @@ -148,7 +149,7 @@ def validate_strength(cls, v: MappableConcept | None) -> MappableConcept | None: :return: Validated strength value """ return validate_mappable_concept( - v, System.ACMG, STRENGTHS, mc_is_required=False + v, System.ACMG, valid_codes=STRENGTHS, mc_is_required=False ) @field_validator("classification") diff --git a/src/ga4gh/va_spec/base/__init__.py b/src/ga4gh/va_spec/base/__init__.py index cf9183f..79d710f 100644 --- a/src/ga4gh/va_spec/base/__init__.py +++ b/src/ga4gh/va_spec/base/__init__.py @@ -19,6 +19,7 @@ StudyGroup, StudyResult, SubjectVariantProposition, + TumorVariantFrequencyStudyResult, VariantDiagnosticProposition, VariantOncogenicityProposition, VariantPathogenicityProposition, @@ -85,4 +86,5 @@ "VariantPathogenicityProposition", "VariantPrognosticProposition", "VariantTherapeuticResponseProposition", + "TumorVariantFrequencyStudyResult", ] diff --git a/src/ga4gh/va_spec/base/core.py b/src/ga4gh/va_spec/base/core.py index 27c59e6..fdcca58 100644 --- a/src/ga4gh/va_spec/base/core.py +++ b/src/ga4gh/va_spec/base/core.py @@ -116,6 +116,44 @@ class CohortAlleleFrequencyStudyResult(_StudyResult, BaseModelForbidExtra): ) +class TumorVariantFrequencyStudyResult(_StudyResult, BaseModelForbidExtra): + """A Study Result that reports measures related to the frequency of an variant + across different tumor types. + """ + + type: Literal["TumorVariantFrequencyStudyResult"] = Field( + "TumorVariantFrequencyStudyResult", + description="MUST be 'TumorVariantFrequencyStudyResult'.", + ) + sourceDataSet: DataSet | None = Field( + None, + description="The dataset from which data in the Tumor Variant Frequency Study Result was taken.", + ) + focusVariant: Allele | CategoricalVariant | iriReference = Field( + ..., + description="The variant for which frequency data is reported in the Study Result", + ) + affectedSampleCount: int = Field( + ..., description="The number of tumor samples that contain the focus variant" + ) + totalSampleCount: int = Field( + ..., + description="The total number of tumor samples included in the dataset", + ) + affectedFrequency: float = Field( + ..., + description="The frequency of tumor samples that include the focus variant.", + ) + sampleGroup: StudyGroup | None = Field( + None, + description="The set of samples about which the frequency data was generated.", + ) + subGroupFrequency: list[TumorVariantFrequencyStudyResult] | None = Field( + None, + description="A list of Tumor Variant Frequency Study Result objects describing subsets of the sample group currently being described. Subgroups can be further subdivided into more subcohorts. This enables, for example, the description of frequency data within samples with a narrower categorical variant than the root focus variant, or samples with a specific tumors type", + ) + + class ExperimentalVariantFunctionalImpactStudyResult( _StudyResult, BaseModelForbidExtra ): @@ -352,7 +390,7 @@ class Method(Entity, BaseModelForbidExtra): type: Literal["Method"] = Field( CoreType.METHOD.value, description=f"MUST be '{CoreType.METHOD.value}'." ) - subtype: MappableConcept | None = Field( + methodType: str | None = Field( None, description="A specific type of method that a Method instance represents (e.g. 'Variant Interpretation Guideline', or 'Experimental Protocol').", ) @@ -374,7 +412,7 @@ class Contribution(Entity, BaseModelForbidExtra): contributor: Agent | None = Field( None, description="The agent that made the contribution." ) - activityType: MappableConcept | None = Field( + activityType: str | None = Field( None, description="The specific type of activity performed or role played by an agent in making the contribution (e.g. for a publication, agents may contribute as a primary author, editor, figure designer, data generator, etc.). Values of this property may be framed as activities, or as contribution roles (e.g. using terms from the Contribution Role Ontology (CRO)).", ) @@ -391,7 +429,7 @@ class Document(Entity, BaseModelForbidExtra): type: Literal["Document"] = Field( CoreType.DOCUMENT.value, description=f"Must be '{CoreType.DOCUMENT.value}'" ) - subtype: MappableConcept | None = Field( + documentType: str | None = Field( None, description="A specific type of document that a Document instance represents (e.g. 'publication', 'patent', 'pathology report')", ) @@ -427,7 +465,7 @@ class Agent(Entity, BaseModelForbidExtra): CoreType.AGENT.value, description=f"MUST be '{CoreType.AGENT.value}'." ) name: str | None = Field(None, description="The given name of the Agent.") - subtype: MappableConcept | None = Field( + agentType: str | None = Field( None, description="A specific type of agent the Agent object represents. Recommended subtypes include codes for `person`, `organization`, or `software`.", ) @@ -451,7 +489,7 @@ class DataSet(Entity, BaseModelForbidExtra): type: Literal["DataSet"] = Field( CoreType.DATA_SET.value, description=f"MUST be '{CoreType.DATA_SET.value}'." ) - subtype: MappableConcept | None = Field( + datasetType: str | None = Field( None, description="A specific type of data set the DataSet instance represents (e.g. a 'clinical data set', a 'sequencing data set', a 'gene expression data set', a 'genome annotation data set')", ) @@ -673,13 +711,14 @@ class EvidenceLineValidatorMixin: @staticmethod def _validate_evidence_outcome( - values: dict, system: System, codes: list[str] + values: dict, system: System, code_pattern: str ) -> dict: """Validate ``evidenceOutcome`` property if it exists :param values: Input values - :param system: System that should be used in ``MappableConcept`` - :param codes: Codes that should be used in ``MappableConcept`` + :param system: System that should be used for ``primaryCoding.system`` + :param code_pattern: The regex pattern that should be used for + ``primaryCoding.code`` :raises ValueError: If ``evidenceOutcome`` exists and is invalid :return: Validated input values. If ``evidenceOutcome`` exists, then it will be validated and converted to a ``MappableConcept`` @@ -687,9 +726,29 @@ def _validate_evidence_outcome( if "evidenceOutcome" in values: mc = MappableConcept(**values["evidenceOutcome"]) values["evidenceOutcome"] = mc - validate_mappable_concept(mc, system, codes, mc_is_required=False) + validate_mappable_concept( + mc, system, code_pattern=code_pattern, mc_is_required=False + ) return values + @field_validator("specifiedBy") + @classmethod + def validate_specified_by(cls, v: Method | iriReference) -> Method | iriReference: + """Validate specifiedBy + + :param v: specifiedBy + :raises ValueError: If invalid specifiedBy values are provided + :return: Validated specifiedBy value + """ + if isinstance(v, Method): + if not v.reportedIn: + err_msg = "`reportedIn` is required." + raise ValueError(err_msg) + + cls.Criterion(v.methodType) + + return v + @model_validator(mode="after") def evidence_line_validator(cls, model: BaseModel) -> BaseModel: # noqa: N805 """Validate that the model is a ``EvidenceLine``. diff --git a/src/ga4gh/va_spec/base/validators.py b/src/ga4gh/va_spec/base/validators.py index 8e10efe..eaaf0ec 100644 --- a/src/ga4gh/va_spec/base/validators.py +++ b/src/ga4gh/va_spec/base/validators.py @@ -1,5 +1,7 @@ """Shared validator functions""" +import re + from ga4gh.core.models import MappableConcept from ga4gh.va_spec.base.enums import System @@ -7,14 +9,17 @@ def validate_mappable_concept( mc: MappableConcept | None, valid_system: System, - valid_codes: list[str], + valid_codes: list[str] | None = None, + code_pattern: str | None = None, mc_is_required: bool = False, ) -> MappableConcept | None: """Validate GKS Core Mappable Concept object :param mc: Mappable Concept object :param valid_system: The system that should be used - :param valid_codes: The codes that should be used + :param valid_codes: The codes that should be used for ``primaryCoding.code`` + :param code_pattern: The regex pattern that should be used for + ``primaryCoding.code`` :param mc_is_required: Whether or not `mc` is required :raises ValueError: If `mc` is invalid :return: Validated mappable concept @@ -30,8 +35,14 @@ def validate_mappable_concept( err_msg = f"`primaryCoding.system` must be '{valid_system.value}'." raise ValueError(err_msg) - if mc.primaryCoding.code.root not in valid_codes: + if valid_codes is not None and mc.primaryCoding.code.root not in valid_codes: err_msg = f"`primaryCoding.code` must be one of {valid_codes}." raise ValueError(err_msg) + if code_pattern is not None and not re.match( + code_pattern, mc.primaryCoding.code.root + ): + err_msg = f"`primaryCoding.code` does not match regex pattern {code_pattern}" + raise ValueError(err_msg) + return mc diff --git a/src/ga4gh/va_spec/ccv_2022/__init__.py b/src/ga4gh/va_spec/ccv_2022/__init__.py index 27bd06d..8c6c7a9 100644 --- a/src/ga4gh/va_spec/ccv_2022/__init__.py +++ b/src/ga4gh/va_spec/ccv_2022/__init__.py @@ -1,15 +1,11 @@ """Module to load and init namespace at package level.""" from .models import ( - EVIDENCE_OUTCOME_VALUES, - EvidenceOutcome, - VariantOncogenicityFunctionalImpactEvidenceLine, + VariantOncogenicityEvidenceLine, VariantOncogenicityStudyStatement, ) __all__ = [ - "EVIDENCE_OUTCOME_VALUES", - "EvidenceOutcome", - "VariantOncogenicityFunctionalImpactEvidenceLine", + "VariantOncogenicityEvidenceLine", "VariantOncogenicityStudyStatement", ] diff --git a/src/ga4gh/va_spec/ccv_2022/models.py b/src/ga4gh/va_spec/ccv_2022/models.py index 21ee9fd..96f4d36 100644 --- a/src/ga4gh/va_spec/ccv_2022/models.py +++ b/src/ga4gh/va_spec/ccv_2022/models.py @@ -22,33 +22,14 @@ from pydantic import BaseModel, Field, field_validator, model_validator -class EvidenceOutcome(str, Enum): - """Define constraints for evidence outcome values""" - - OS2 = "OS2" - OS2_MODERATE = "OS2_moderate" - OS2_SUPPORTING = "OS2_supporting" - OS2_NOT_MET = "OS2_not_met" - SBS2 = "SBS2" - SBS2_MODERATE = "SBS2_moderate" - SBS2_SUPPORTING = "SBS2_supporting" - SBS2_NOT_MET = "SBS2_not_met" - - -EVIDENCE_OUTCOME_VALUES = [v.value for v in EvidenceOutcome.__members__.values()] - - -class VariantOncogenicityFunctionalImpactEvidenceLine( - BaseModel, EvidenceLineValidatorMixin -): - """An Evidence Line that describes how information about the functional impact of a - variant on a gene or gene product was interpreted as evidence for or against the - variant's oncogenicity. +class VariantOncogenicityEvidenceLine(BaseModel, EvidenceLineValidatorMixin): + """An Evidence Line that describes how information about the specific evidence of a + variant was interpreted as evidence for or against the variant's oncogenicity. """ targetProposition: VariantOncogenicityProposition | None = Field( None, - description="A Variant Oncogenicity Proposition against which functional impact information was assessed, in determining the strength and direction of support this information provides as evidence.", + description="A Variant Oncogenicity Proposition against which evidence information was assessed, in determining the strength and direction of support this information provides as evidence.", ) strengthOfEvidenceProvided: MappableConcept | None = Field( None, @@ -56,9 +37,30 @@ class VariantOncogenicityFunctionalImpactEvidenceLine( ) specifiedBy: Method | iriReference = Field( ..., - description="The Clingen/CGC/VICC 2022 criterion that was applied to interpret variant functional impact information as evidence for or against the assessed variant's oncogenicity.", + description="The guidelines that were followed to assess the variant information as evidence for or against the assessed variant's oncogenicity.", ) + class Criterion(str, Enum): + """Define CCV 2022 criterion values""" + + OVS1 = "OVS1" + OS1 = "OS1" + OS2 = "OS2" + OS3 = "OS3" + OM1 = "OM1" + OM2 = "OM2" + OM3 = "OM3" + OM4 = "OM4" + OP1 = "OP1" + OP2 = "OP2" + OP3 = "OP3" + OP4 = "OP4" + SBVS1 = "SBVS1" + SBS1 = "SBS1" + SBS2 = "SBS2" + SBP1 = "SBP1" + SBP2 = "SBP2" + @field_validator("strengthOfEvidenceProvided") @classmethod def validate_strength_of_evidence_provided( @@ -71,7 +73,10 @@ def validate_strength_of_evidence_provided( :return: Validated strengthOfEvidenceProvided value """ return validate_mappable_concept( - v, System.CCV, STRENGTH_OF_EVIDENCE_PROVIDED_VALUES, mc_is_required=False + v, + System.CCV, + valid_codes=STRENGTH_OF_EVIDENCE_PROVIDED_VALUES, + mc_is_required=False, ) @model_validator(mode="before") @@ -83,9 +88,8 @@ def validate_evidence_outcome(cls, values: dict) -> dict: # noqa: N805 :return: Validated input values. If ``evidenceOutcome`` exists, then it will be validated and converted to a ``MappableConcept`` """ - return cls._validate_evidence_outcome( - values, System.CCV, EVIDENCE_OUTCOME_VALUES - ) + ccv_code_pattern = r"^((?:OVS1|SBVS1)(?:_(?:not_met|(?:strong|moderate|supporting)))?|(?:OS[1-3]|SBS[1-2])(?:_(?:not_met|(?:very_strong|moderate|supporting)))?|(?:OM[1-4])(?:_(?:not_met|(?:very_strong|strong|supporting)))?|(OP[1-4]|SBP[1-2])(?:_(?:not_met|very_strong|strong|moderate))?)$" + return cls._validate_evidence_outcome(values, System.CCV, ccv_code_pattern) class VariantOncogenicityStudyStatement(BaseModel, StatementValidatorMixin): @@ -119,7 +123,9 @@ def validate_strength(cls, v: MappableConcept | None) -> MappableConcept | None: :raises ValueError: If invalid strength values are provided :return: Validated strength value """ - return validate_mappable_concept(v, System.CCV, STRENGTHS, mc_is_required=False) + return validate_mappable_concept( + v, System.CCV, valid_codes=STRENGTHS, mc_is_required=False + ) @field_validator("classification") @classmethod @@ -131,5 +137,5 @@ def validate_classification(cls, v: MappableConcept) -> MappableConcept: :return: Validated classification value """ return validate_mappable_concept( - v, System.CCV, CCV_CLASSIFICATIONS, mc_is_required=True + v, System.CCV, valid_codes=CCV_CLASSIFICATIONS, mc_is_required=True ) diff --git a/src/ga4gh/va_spec/extras/builders/__init.py__ b/src/ga4gh/va_spec/extras/builders/__init.py__ new file mode 100644 index 0000000..e69de29 diff --git a/src/ga4gh/va_spec/extras/builders/acmg2015.py b/src/ga4gh/va_spec/extras/builders/acmg2015.py new file mode 100644 index 0000000..5ecf7b1 --- /dev/null +++ b/src/ga4gh/va_spec/extras/builders/acmg2015.py @@ -0,0 +1,57 @@ +"""Builders for the ACMG 2015 (v3) guidelines""" + +from ga4gh.core.models import Coding, MappableConcept +from ga4gh.va_spec.acmg_2015 import VariantPathogenicityEvidenceLine +from ga4gh.va_spec.base import ( + Direction, + Document, + EvidenceLine, + Method, + StrengthOfEvidenceProvided, +) + +ACMG_2015_SPEC_DOCUMENT = Document( + name="ACMG 2015 Guidelines", + pmid=25741868, + doi="10.1038/gim.2015.30", + urls=[ + "https://doi.org/10.1038/gim.2015.30", + "https://www.nature.com/articles/gim201530", + ], +) + + +def PS3( # noqa: N802 + criteria_met: bool = True, + strength: StrengthOfEvidenceProvided = StrengthOfEvidenceProvided.STRONG, +) -> EvidenceLine: + """Build an evidence line corresponding to the ACMG 2015 PS3 criterion""" + code = VariantPathogenicityEvidenceLine.Criterion.PS3.value + acmg2015_ps3_method = Method( + name=f"ACMG 2015 {code} Criterion", + methodType=code, + reportedIn=ACMG_2015_SPEC_DOCUMENT, + ) + + if not criteria_met: + strength_mc = None + direction = Direction.NEUTRAL + derived_code = f"{code}_not_met" + else: + strength_mc = MappableConcept( + primaryCoding=Coding(code=strength.value, system="ACMG 2015") + ) + direction = Direction.SUPPORTS + if strength == StrengthOfEvidenceProvided.STRONG: + derived_code = code + else: + derived_code = f"{code}_{strength.value}" + + return EvidenceLine( + strengthOfEvidenceProvided=strength_mc, + directionOfEvidenceProvided=direction, + specifiedBy=acmg2015_ps3_method, + evidenceOutcome=MappableConcept( + primaryCoding=Coding(code=derived_code, system="ACMG 2015") + ), + ) diff --git a/submodules/va_spec b/submodules/va_spec index 1ab0ef7..143d2d4 160000 --- a/submodules/va_spec +++ b/submodules/va_spec @@ -1 +1 @@ -Subproject commit 1ab0ef7fadd08fd1687007e59d24c5f7813aa749 +Subproject commit 143d2d425782d5aca54b5a0969481390539dfaa9 diff --git a/tests/validation/test_va_spec_models.py b/tests/validation/test_va_spec_models.py index 93eae0e..bd0f924 100644 --- a/tests/validation/test_va_spec_models.py +++ b/tests/validation/test_va_spec_models.py @@ -9,7 +9,7 @@ from ga4gh.va_spec import acmg_2015, base, ccv_2022 from ga4gh.va_spec.aac_2017.models import VariantTherapeuticResponseStudyStatement from ga4gh.va_spec.acmg_2015.models import ( - VariantPathogenicityFunctionalImpactEvidenceLine, + VariantPathogenicityEvidenceLine, VariantPathogenicityStatement, ) from ga4gh.va_spec.base import ( @@ -19,7 +19,7 @@ ) from ga4gh.va_spec.base.core import EvidenceLine, Method, StudyGroup, StudyResult from ga4gh.va_spec.ccv_2022.models import ( - VariantOncogenicityFunctionalImpactEvidenceLine, + VariantOncogenicityEvidenceLine, VariantOncogenicityStudyStatement, ) from pydantic import ValidationError @@ -294,7 +294,7 @@ def test_variant_pathogenicity_stmt(): def test_variant_pathogenicity_el(): - """Ensure VariantPathogenicityFunctionalImpactEvidenceLine model works as expected""" + """Ensure VariantPathogenicityEvidenceLine model works as expected""" params = { "type": "EvidenceLine", "specifiedBy": { @@ -306,6 +306,7 @@ def test_variant_pathogenicity_el(): "pmid": 25741868, "name": "ACMG Guidelines, 2015", }, + "methodType": "PS3", }, "directionOfEvidenceProvided": "supports", "evidenceOutcome": { @@ -316,7 +317,7 @@ def test_variant_pathogenicity_el(): "name": "ACMG 2015 PS3 Supporting Criterion Met", }, } - vp = VariantPathogenicityFunctionalImpactEvidenceLine(**params) + vp = VariantPathogenicityEvidenceLine(**params) assert isinstance(vp.specifiedBy, Method) assert vp.evidenceOutcome == MappableConcept( @@ -328,24 +329,24 @@ def test_variant_pathogenicity_el(): valid_params = deepcopy(params) valid_params["strengthOfEvidenceProvided"] = None - assert VariantPathogenicityFunctionalImpactEvidenceLine(**valid_params) + assert VariantPathogenicityEvidenceLine(**valid_params) invalid_params = deepcopy(params) del invalid_params["specifiedBy"]["reportedIn"] with pytest.raises(ValueError, match="`reportedIn` is required"): - VariantPathogenicityFunctionalImpactEvidenceLine(**invalid_params) + VariantPathogenicityEvidenceLine(**invalid_params) invalid_params = deepcopy(params) del invalid_params[ "directionOfEvidenceProvided" ] # directionOfEvidenceProvided is required for statement with pytest.raises(ValueError, match="Must be an `EvidenceLine`"): - VariantPathogenicityFunctionalImpactEvidenceLine(**invalid_params) + VariantPathogenicityEvidenceLine(**invalid_params) invalid_params = deepcopy(params) invalid_params["strengthOfEvidenceProvided"] = {"name": "test"} with pytest.raises(ValueError, match="`primaryCoding` is required."): - VariantPathogenicityFunctionalImpactEvidenceLine(**invalid_params) + VariantPathogenicityEvidenceLine(**invalid_params) invalid_params = deepcopy(params) invalid_params["strengthOfEvidenceProvided"] = { @@ -355,14 +356,22 @@ def test_variant_pathogenicity_el(): } } with pytest.raises(ValueError, match="`primaryCoding.system` must be"): - VariantPathogenicityFunctionalImpactEvidenceLine(**invalid_params) + VariantPathogenicityEvidenceLine(**invalid_params) invalid_params = deepcopy(params) invalid_params["strengthOfEvidenceProvided"] = { "primaryCoding": {"system": "ACMG Guidelines, 2015", "code": "PS3"} } with pytest.raises(ValueError, match="`primaryCoding.code` must be"): - VariantPathogenicityFunctionalImpactEvidenceLine(**invalid_params) + VariantPathogenicityEvidenceLine(**invalid_params) + + invalid_params = deepcopy(params) + invalid_params["specifiedBy"]["methodType"] = "OS1" + with pytest.raises( + ValueError, + match="'OS1' is not a valid VariantPathogenicityEvidenceLine.Criterion", + ): + VariantPathogenicityEvidenceLine(**invalid_params) def test_variant_onco_stmt(): @@ -415,8 +424,8 @@ def test_variant_onco_stmt(): def test_variant_onco_el(): - """Ensure VariantOncogenicityFunctionalImpactEvidenceLine model works as expected""" - vo = VariantOncogenicityFunctionalImpactEvidenceLine( + """Ensure VariantOncogenicityEvidenceLine model works as expected""" + vo = VariantOncogenicityEvidenceLine( type="EvidenceLine", specifiedBy={ "type": "Method", @@ -425,6 +434,7 @@ def test_variant_onco_el(): "pmid": 35101336, "name": "ClinGen/CGC/VICC Guidelines for Oncogenicity, 2022", }, + "methodType": "OS2", }, directionOfEvidenceProvided="supports", scoreOfEvidenceProvided=1, @@ -443,6 +453,14 @@ def test_variant_onco_el(): ), ) + vo_invalid_params = vo.model_copy(deep=True).model_dump() + vo_invalid_params["specifiedBy"]["methodType"] = "PS1" + with pytest.raises( + ValueError, + match="'PS1' is not a valid VariantOncogenicityEvidenceLine.Criterion", + ): + VariantOncogenicityEvidenceLine(**vo_invalid_params) + def test_examples(test_definitions): """Test VA Spec examples"""