Skip to content

Commit

Permalink
Init pp3 and bp4 implementation (#115)
Browse files Browse the repository at this point in the history
* init pp3 and bp4

* add empty test
  • Loading branch information
gromdimon authored May 28, 2024
1 parent cead5cb commit 6671789
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 11 deletions.
11 changes: 11 additions & 0 deletions docs/acmg_seqvars_implementation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,14 @@ BP7
:undoc-members:
:show-inheritance:
:private-members:

-----------
PP3 and BP4
-----------

.. automodule:: src.criteria.auto_pp3_bp4
:members:
:inherited-members:
:undoc-members:
:show-inheritance:
:private-members:
22 changes: 11 additions & 11 deletions src/auto_acmg.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@

#: Criteria, which have no automatic prediction yet.
NOT_IMPLEMENTED_CRITERIA = [
"PS2", # De novo (both parents not tested)
"PS3", # Well-established in vitro or in vivo functional studies
"PS4", # Increased prevalence in affected individuals vs. controls
"PM3", # Recessive disorder (zygosity unknown)
"PM6", # Assumed de novo (both parents not tested)
"PP1", # Cosegregation with disease in multiple affected family members
"PP4", # Patient's phenotype or family history specificity
"BS3", # Well-established in vitro or in vivo functional studies
"BS4", # Lack of segregation in affected family members
"BP2", # Observed in healthy individuals (zygosity unknown)
"BP5", # Case with an alternate molecular basis for disease
"PS2", # De novo (both parents not tested)
"PS3", # Well-established in vitro or in vivo functional studies
"PS4", # Increased prevalence in affected individuals vs. controls
"PM3", # Recessive disorder (zygosity unknown)
"PM6", # Assumed de novo (both parents not tested)
"PP1", # Cosegregation with disease in multiple affected family members
"PP4", # Patient's phenotype or family history specificity
"BS3", # Well-established in vitro or in vivo functional studies
"BS4", # Lack of segregation in affected family members
"BP2", # Observed in healthy individuals (zygosity unknown)
"BP5", # Case with an alternate molecular basis for disease
]


Expand Down
16 changes: 16 additions & 0 deletions src/criteria/auto_acmg.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from src.criteria.auto_pm1 import AutoPM1
from src.criteria.auto_pm4_bp3 import AutoPM4BP3
from src.criteria.auto_pp2_bp1 import AutoPP2BP1
from src.criteria.auto_pp3_bp4 import AutoPP3BP4
from src.criteria.auto_ps1_pm5 import AutoPS1PM5
from src.defs.annonars_variant import AnnonarsVariantResponse
from src.defs.auto_acmg import ACMGCriteria
Expand Down Expand Up @@ -139,4 +140,19 @@ def predict(self) -> Optional[ACMGCriteria]:
except AutoAcmgBaseException as e:
logger.error("Failed to predict BP7 criteria. Error: {}", e)

# PP3 and BP4
try:
logger.info("Predicting PP3 and BP4 criteria.")
pp3_bp4 = AutoPP3BP4(
self.seqvar, self.genome_release, variant_info.result, config=self.config
)
pp3_bp4_prediction = pp3_bp4.predict()
if not pp3_bp4_prediction:
logger.error("Failed to predict PP3 and BP4 criteria.")
else:
self.prediction.PP3 = pp3_bp4_prediction.PP3
self.prediction.BP4 = pp3_bp4_prediction.BP4
except AutoAcmgBaseException as e:
logger.error("Failed to predict PP3 and BP4 criteria. Error: {}", e)

return self.prediction
110 changes: 110 additions & 0 deletions src/criteria/auto_pp3_bp4.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
"""Implementation of the PP3 and BP4 criteria."""

from typing import List, Optional

from loguru import logger

from src.api.annonars import AnnonarsClient
from src.core.config import Config
from src.defs.annonars_variant import VariantResult
from src.defs.auto_acmg import PP3BP4
from src.defs.exceptions import AutoAcmgBaseException, MissingDataError
from src.defs.genome_builds import GenomeRelease
from src.defs.seqvar import SeqVar


class AutoPP3BP4:
"""Class for automatic PP3 and BP4 prediction."""

def __init__(
self,
seqvar: SeqVar,
genome_release: GenomeRelease,
variant_info: VariantResult,
*,
config: Config,
):
"""Initialize the class."""
self.seqvar = seqvar
self.genome_release = genome_release
self.variant_info = variant_info
self.config = config
self.annonars_client = AnnonarsClient(api_base_url=config.api_base_url_annonars)
self.prediction: PP3BP4 | None = None

@staticmethod
def _get_best_pathogenic_score(variant_info: VariantResult) -> Optional[float]:
"""Get the best pathogenic score available."""
# Avoid linting errors
assert variant_info.cadd is not None and variant_info.dbnsfp is not None
scores = [
# variant_info.mutpred2,
variant_info.cadd.PolyPhenVal,
# variant_info.revel,
# variant_info.bayesdel,
# variant_info.vest4,
# variant_info.phylop,
]
scores = [score for score in scores if score is not None]
return max(scores) if scores else None # type: ignore

@staticmethod
def _get_best_benign_score(variant_info: VariantResult) -> Optional[float]:
"""Get the best benign score available."""
# Avoid linting errors
assert variant_info.cadd is not None and variant_info.dbnsfp is not None
scores = [
# variant_info.dbnsfp.REVEL_rankscore,
# variant_info.dbnsfp.MutPred_rankscore,
variant_info.cadd.PolyPhenVal,
# variant_info.bayesdel,
# variant_info.vest4,
# variant_info.phylop,
]
scores = [score for score in scores if score is not None]
return min(scores) if scores else None # type: ignore

def _predict_spliceai(self, variant_info: VariantResult) -> Optional[float]:
"""Predict splice site alterations using SpliceAI."""
# TODO: Implement this method.
return None

def predict(self) -> Optional[PP3BP4]:
"""Predict PP3 and BP4 criteria."""
self.prediction = PP3BP4()

try:
if self.seqvar.chrom == "MT":
self.prediction.PP3 = False
self.prediction.BP4 = False
return self.prediction

if not self.variant_info or not self.variant_info.cadd or not self.variant_info.dbnsfp:
logger.error("Missing CADD or DBNSFP data.")
raise MissingDataError("Missing CADD or DBNSFP data.")

best_pathogenic_score = self._get_best_pathogenic_score(self.variant_info)
best_benign_score = self._get_best_benign_score(self.variant_info)
spliceai_score = self._predict_spliceai(self.variant_info)

# Evaluate PP3
if best_pathogenic_score and best_pathogenic_score > 0.8:
self.prediction.PP3 = True
elif spliceai_score and spliceai_score > 0.8:
self.prediction.PP3 = True
else:
self.prediction.PP3 = False

# Evaluate BP4
if best_benign_score and best_benign_score < 0.2:
self.prediction.BP4 = True
elif spliceai_score and spliceai_score < 0.2:
self.prediction.BP4 = True
else:
self.prediction.BP4 = False

except AutoAcmgBaseException as e:
logger.error("Failed to predict PP3 and BP4 criteria. Error: {}", e)
self.prediction = None

return self.prediction
1 change: 1 addition & 0 deletions src/defs/annonars_variant.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class VariantQuery(BaseModel):

class Cadd(BaseModel):
ConsDetail: Optional[str] = None
PolyPhenVal: Optional[float] = None


class GnomadExomes(BaseModel):
Expand Down
7 changes: 7 additions & 0 deletions src/defs/auto_acmg.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,10 @@ class BP7(BaseModel):
"""BP7 criteria prediction."""

BP7: bool = False


class PP3BP4(BaseModel):
"""PP3 and BP4 criteria prediction."""

PP3: bool = False
BP4: bool = False
1 change: 1 addition & 0 deletions tests/criteria/test_auto_ba1_bs1_bs2_pm2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pass
1 change: 1 addition & 0 deletions tests/criteria/test_auto_bp7.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pass
1 change: 1 addition & 0 deletions tests/criteria/test_auto_pp2_bp1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pass
1 change: 1 addition & 0 deletions tests/criteria/test_auto_pp3_bp4.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pass

0 comments on commit 6671789

Please sign in to comment.