diff --git a/src/ga4gh/va_spec/ccv_2022/__init__.py b/src/ga4gh/va_spec/ccv_2022/__init__.py index a84a82c..4f6d372 100644 --- a/src/ga4gh/va_spec/ccv_2022/__init__.py +++ b/src/ga4gh/va_spec/ccv_2022/__init__.py @@ -1,11 +1,19 @@ """Module to load and init namespace at package level.""" +from .derived_evidence import ( + CODE_PREFIX_TO_SCORE_MAP, + CODE_SUFFIX_TO_STRENGTH_MAP, + derive_onco_evidence_attributes, +) from .models import ( VariantOncogenicityEvidenceLine, VariantOncogenicityStatement, ) __all__ = [ + "CODE_PREFIX_TO_SCORE_MAP", + "CODE_SUFFIX_TO_STRENGTH_MAP", + "derive_onco_evidence_attributes", "VariantOncogenicityEvidenceLine", "VariantOncogenicityStatement", ] diff --git a/src/ga4gh/va_spec/ccv_2022/derived_evidence.py b/src/ga4gh/va_spec/ccv_2022/derived_evidence.py new file mode 100644 index 0000000..6812623 --- /dev/null +++ b/src/ga4gh/va_spec/ccv_2022/derived_evidence.py @@ -0,0 +1,86 @@ +"""Provide derived evidence attributes for an onco evidence code. + +Can be used to populate `evidenceOutcome`, `strengthOfEvidenceProvided`, and +`scoreOfEvidenceProvided` fields in `VariantOncogenicityEvidenceLine`. +""" + +from types import MappingProxyType + +from pydantic import BaseModel + +from ga4gh.core.models import Coding, MappableConcept, code +from ga4gh.va_spec.base import ( + StrengthOfEvidenceProvided, +) +from ga4gh.va_spec.base.enums import System +from ga4gh.va_spec.ccv_2022.models import VariantOncogenicityEvidenceLine + + +class EvidenceAttributes(BaseModel): + """Define derived evidence attributes for an onco evidence code.""" + + evidenceOutcome: MappableConcept + strengthOfEvidenceProvided: MappableConcept + scoreOfEvidenceProvided: int + + +# IMPORTANT: Don't change the order. Longer suffixes must be evaluated first. +CODE_SUFFIX_TO_STRENGTH_MAP = MappingProxyType( + { + "VS": StrengthOfEvidenceProvided.VERY_STRONG, + "S": StrengthOfEvidenceProvided.STRONG, + "M": StrengthOfEvidenceProvided.MODERATE, + "P": StrengthOfEvidenceProvided.SUPPORTING, + } +) + + +CODE_PREFIX_TO_SCORE_MAP = MappingProxyType( + { + "OVS": 8, + "SBVS": -8, + "OS": 4, + "SBS": -4, + "OM": 2, + "SBM": -2, + "OP": 1, + "SBP": -1, + } +) + + +def derive_onco_evidence_attributes( + evidence: VariantOncogenicityEvidenceLine.Criterion, +) -> EvidenceAttributes: + """Derive evidence attributes given a CCV 2022 evidence code. + + :param evidence: CCV 2022 evidence code + :return: Derived evidence attributes (evidenceOutcome, strengthOfEvidenceProvided, + scoreOfEvidenceProvided) + """ + evidence_code = evidence.value + normalized_evidence_code = evidence_code.rstrip("1234") + + code_suffix = next( + suffix + for suffix in CODE_SUFFIX_TO_STRENGTH_MAP + if normalized_evidence_code.endswith(suffix) + ) + code_prefix = next( + prefix + for prefix in CODE_PREFIX_TO_SCORE_MAP + if normalized_evidence_code.startswith(prefix) + ) + system = System.CCV + + return EvidenceAttributes( + evidenceOutcome=MappableConcept( + primaryCoding=Coding(code=code(evidence_code), system=system) + ), + strengthOfEvidenceProvided=MappableConcept( + primaryCoding=Coding( + code=code(CODE_SUFFIX_TO_STRENGTH_MAP[code_suffix]), system=system + ) + ), + scoreOfEvidenceProvided=CODE_PREFIX_TO_SCORE_MAP[code_prefix], + ) diff --git a/tests/test_ccv_derived_evidence.py b/tests/test_ccv_derived_evidence.py new file mode 100644 index 0000000..7a78aae --- /dev/null +++ b/tests/test_ccv_derived_evidence.py @@ -0,0 +1,116 @@ +"""Test that CCV 2022 derived evidence is working correctly""" + +import pytest + +from ga4gh.va_spec.ccv_2022 import ( + VariantOncogenicityEvidenceLine, + derive_onco_evidence_attributes, +) + + +@pytest.mark.parametrize( + ("criterion", "expected_strength", "expected_score"), + [ + ( + VariantOncogenicityEvidenceLine.Criterion.OP1, + "supporting", + 1, + ), + ( + VariantOncogenicityEvidenceLine.Criterion.OP2, + "supporting", + 1, + ), + ( + VariantOncogenicityEvidenceLine.Criterion.OP3, + "supporting", + 1, + ), + ( + VariantOncogenicityEvidenceLine.Criterion.OP4, + "supporting", + 1, + ), + ( + VariantOncogenicityEvidenceLine.Criterion.OM1, + "moderate", + 2, + ), + ( + VariantOncogenicityEvidenceLine.Criterion.OM2, + "moderate", + 2, + ), + ( + VariantOncogenicityEvidenceLine.Criterion.OM3, + "moderate", + 2, + ), + ( + VariantOncogenicityEvidenceLine.Criterion.OM4, + "moderate", + 2, + ), + ( + VariantOncogenicityEvidenceLine.Criterion.OS1, + "strong", + 4, + ), + ( + VariantOncogenicityEvidenceLine.Criterion.OS2, + "strong", + 4, + ), + ( + VariantOncogenicityEvidenceLine.Criterion.OS3, + "strong", + 4, + ), + ( + VariantOncogenicityEvidenceLine.Criterion.OVS1, + "very strong", + 8, + ), + ( + VariantOncogenicityEvidenceLine.Criterion.SBP1, + "supporting", + -1, + ), + ( + VariantOncogenicityEvidenceLine.Criterion.SBP2, + "supporting", + -1, + ), + ( + VariantOncogenicityEvidenceLine.Criterion.SBS1, + "strong", + -4, + ), + ( + VariantOncogenicityEvidenceLine.Criterion.SBS2, + "strong", + -4, + ), + ( + VariantOncogenicityEvidenceLine.Criterion.SBVS1, + "very strong", + -8, + ), + ], +) +def test_derive_onco_evidence_attributes( + criterion, + expected_strength, + expected_score, +): + """Test that derive_onco_evidence_attributes works correctly""" + onco_evidence_attrs = derive_onco_evidence_attributes(criterion) + + assert ( + onco_evidence_attrs.evidenceOutcome.primaryCoding.code.root == criterion.value + ) + assert ( + onco_evidence_attrs.strengthOfEvidenceProvided.primaryCoding.code.root + == expected_strength + ) + assert onco_evidence_attrs.scoreOfEvidenceProvided == expected_score