492 lines
14 KiB
Markdown
492 lines
14 KiB
Markdown
---
|
|
title: "Code Patterns for Variant Interpretation"
|
|
task: ""
|
|
lineage_type: import
|
|
upstream_source: https://github.com/mims-harvard/ToolUniverse/blob/e2520a96/skills/tooluniverse-variant-interpretation/CODE_PATTERNS.md
|
|
upstream_sha: e2520a96
|
|
imported_at: 2026-06-26
|
|
prompt_class: prompt
|
|
upstream_changes: accepted
|
|
author: upstream
|
|
validated: false
|
|
---
|
|
|
|
# Code Patterns for Variant Interpretation
|
|
|
|
Reusable code patterns for each phase of the variant interpretation workflow.
|
|
|
|
---
|
|
|
|
## Phase 2: Clinical Database Code Patterns
|
|
|
|
### 2.1 COSMIC Somatic Context
|
|
|
|
```python
|
|
def get_somatic_context(tu, gene_symbol, variant_aa):
|
|
"""Get somatic mutation context from COSMIC."""
|
|
|
|
# Search for specific mutation
|
|
cosmic = tu.tools.COSMIC_search_mutations(
|
|
operation="search",
|
|
terms=f"{gene_symbol} {variant_aa}",
|
|
max_results=20,
|
|
genome_build=38
|
|
)
|
|
|
|
# Get all gene mutations for context
|
|
gene_mutations = tu.tools.COSMIC_get_mutations_by_gene(
|
|
operation="get_by_gene",
|
|
gene=gene_symbol,
|
|
max_results=100
|
|
)
|
|
|
|
# Determine if it's a hotspot
|
|
mutation_counts = Counter(m['MutationAA'] for m in gene_mutations.get('results', []))
|
|
is_hotspot = variant_aa in [m[0] for m in mutation_counts.most_common(10)]
|
|
|
|
return {
|
|
'cosmic_hits': cosmic.get('results', []),
|
|
'is_somatic_hotspot': is_hotspot,
|
|
'cancer_types': [m['PrimarySite'] for m in cosmic.get('results', [])],
|
|
'total_cosmic_count': cosmic.get('total_count', 0)
|
|
}
|
|
```
|
|
|
|
### 2.2 OMIM Gene-Disease Context
|
|
|
|
```python
|
|
def get_omim_context(tu, gene_symbol):
|
|
"""Get OMIM gene-disease associations."""
|
|
|
|
search = tu.tools.OMIM_search(
|
|
operation="search",
|
|
query=gene_symbol,
|
|
limit=5
|
|
)
|
|
|
|
omim_data = []
|
|
for entry in search.get('data', {}).get('entries', []):
|
|
mim = entry.get('mimNumber')
|
|
|
|
details = tu.tools.OMIM_get_entry(
|
|
operation="get_entry",
|
|
mim_number=str(mim)
|
|
)
|
|
|
|
synopsis = tu.tools.OMIM_get_clinical_synopsis(
|
|
operation="get_clinical_synopsis",
|
|
mim_number=str(mim)
|
|
)
|
|
|
|
omim_data.append({
|
|
'mim_number': mim,
|
|
'title': details.get('data', {}).get('titles', {}),
|
|
'inheritance': synopsis.get('data', {}).get('inheritance'),
|
|
'clinical_features': synopsis.get('data', {})
|
|
})
|
|
|
|
return omim_data
|
|
```
|
|
|
|
### 2.3 DisGeNET Gene-Disease Evidence
|
|
|
|
```python
|
|
def get_disgenet_context(tu, gene_symbol, variant_rsid=None):
|
|
"""Get gene-disease associations from DisGeNET."""
|
|
|
|
gda = tu.tools.DisGeNET_search_gene(
|
|
operation="search_gene",
|
|
gene=gene_symbol,
|
|
limit=20
|
|
)
|
|
|
|
vda = None
|
|
if variant_rsid:
|
|
vda = tu.tools.DisGeNET_get_vda(
|
|
operation="get_vda",
|
|
variant=variant_rsid,
|
|
limit=20
|
|
)
|
|
|
|
return {
|
|
'gene_associations': gda.get('data', {}).get('associations', []),
|
|
'variant_associations': vda.get('data', {}).get('associations', []) if vda else []
|
|
}
|
|
```
|
|
|
|
### 2.4 ClinGen Gene Validity & Dosage Sensitivity
|
|
|
|
```python
|
|
def get_clingen_evidence(tu, gene_symbol):
|
|
"""
|
|
Get ClinGen gene validity and dosage sensitivity data.
|
|
CRITICAL for ACMG classification - establishes gene-disease validity.
|
|
"""
|
|
|
|
validity = tu.tools.ClinGen_search_gene_validity(gene=gene_symbol)
|
|
|
|
validity_data = []
|
|
if validity.get('data'):
|
|
for entry in validity.get('data', []):
|
|
validity_data.append({
|
|
'disease': entry.get('Disease Label'),
|
|
'classification': entry.get('Classification'),
|
|
'inheritance': entry.get('Inheritance'),
|
|
'mondo_id': entry.get('Disease ID (MONDO)')
|
|
})
|
|
|
|
dosage = tu.tools.ClinGen_search_dosage_sensitivity(gene=gene_symbol)
|
|
|
|
dosage_data = {}
|
|
if dosage.get('data'):
|
|
for entry in dosage.get('data', []):
|
|
dosage_data = {
|
|
'haploinsufficiency_score': entry.get('Haploinsufficiency Score'),
|
|
'triplosensitivity_score': entry.get('Triplosensitivity Score'),
|
|
'disease': entry.get('Disease')
|
|
}
|
|
break
|
|
|
|
actionability = tu.tools.ClinGen_search_actionability(gene=gene_symbol)
|
|
|
|
return {
|
|
'gene_validity': validity_data,
|
|
'dosage_sensitivity': dosage_data,
|
|
'actionability': actionability.get('data', {}),
|
|
'has_definitive_validity': any(v['classification'] == 'Definitive' for v in validity_data),
|
|
'is_haploinsufficient': dosage_data.get('haploinsufficiency_score') == '3'
|
|
}
|
|
```
|
|
|
|
### 2.5 SpliceAI Splice Variant Prediction
|
|
|
|
```python
|
|
def get_spliceai_prediction(tu, chrom, pos, ref, alt, genome="38"):
|
|
"""
|
|
Get SpliceAI splice effect predictions.
|
|
|
|
Delta scores: DS_AG (Acceptor gain), DS_AL (Acceptor loss),
|
|
DS_DG (Donor gain), DS_DL (Donor loss)
|
|
"""
|
|
|
|
variant = f"chr{chrom}-{pos}-{ref}-{alt}"
|
|
|
|
result = tu.tools.SpliceAI_predict_splice(
|
|
variant=variant,
|
|
genome=genome
|
|
)
|
|
|
|
if result.get('data'):
|
|
max_score = result['data'].get('max_delta_score', 0)
|
|
interpretation = result['data'].get('interpretation', '')
|
|
|
|
if max_score >= 0.8:
|
|
acmg = 'PP3 (strong) - high splice impact'
|
|
elif max_score >= 0.5:
|
|
acmg = 'PP3 (supporting) - moderate splice impact'
|
|
elif max_score >= 0.2:
|
|
acmg = 'PP3 (weak) - possible splice impact'
|
|
else:
|
|
acmg = 'BP7 (if synonymous) - splice benign'
|
|
|
|
return {
|
|
'max_delta_score': max_score,
|
|
'interpretation': interpretation,
|
|
'acmg_support': acmg,
|
|
'scores': result['data'].get('scores', [])
|
|
}
|
|
return None
|
|
|
|
def quick_splice_check(tu, variant, genome="38"):
|
|
"""Quick triage using max delta score only."""
|
|
result = tu.tools.SpliceAI_get_max_delta(variant=variant, genome=genome)
|
|
return result.get('data', {})
|
|
```
|
|
|
|
**When to Use SpliceAI**:
|
|
- Intronic variants near splice sites (+/-50bp)
|
|
- Synonymous variants (may still affect splicing)
|
|
- Exonic variants near splice junctions
|
|
- Variants creating cryptic splice sites
|
|
|
|
---
|
|
|
|
## Phase 2.5: Regulatory Context (Non-Coding Variants)
|
|
|
|
```python
|
|
def assess_regulatory_impact(tu, variant_position, gene_symbol):
|
|
"""Assess regulatory impact of non-coding variant."""
|
|
|
|
tf_binding = tu.tools.ChIPAtlas_enrichment_analysis(
|
|
gene=gene_symbol,
|
|
cell_type="all"
|
|
)
|
|
|
|
peaks = tu.tools.ChIPAtlas_get_peak_data(
|
|
gene=gene_symbol,
|
|
experiment_type="TF"
|
|
)
|
|
|
|
encode_data = tu.tools.ENCODE_search_experiments(
|
|
assay_title="ATAC-seq",
|
|
biosample="all"
|
|
)
|
|
|
|
binding_disrupted = check_motif_disruption(variant_position, peaks)
|
|
|
|
return {
|
|
'tf_binding': tf_binding,
|
|
'regulatory_peaks': peaks,
|
|
'encode_annotations': encode_data,
|
|
'likely_regulatory': binding_disrupted
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 3: Computational Predictions
|
|
|
|
### 3.1 CADD Scoring
|
|
|
|
```python
|
|
def get_cadd_score(tu, chrom, pos, ref, alt):
|
|
"""Get CADD deleteriousness score for a variant."""
|
|
|
|
result = tu.tools.CADD_get_variant_score(
|
|
chrom=str(chrom),
|
|
pos=pos,
|
|
ref=ref,
|
|
alt=alt,
|
|
version="GRCh38-v1.7"
|
|
)
|
|
|
|
if result.get('status') == 'success':
|
|
phred = result['data'].get('phred_score')
|
|
return {
|
|
'score': phred,
|
|
'interpretation': result['data'].get('interpretation'),
|
|
'acmg_support': 'PP3' if phred >= 20 else ('BP4' if phred < 15 else 'neutral')
|
|
}
|
|
return None
|
|
```
|
|
|
|
### 3.2 AlphaMissense Pathogenicity
|
|
|
|
```python
|
|
def get_alphamissense_score(tu, uniprot_id, variant):
|
|
"""
|
|
Get AlphaMissense pathogenicity score.
|
|
variant format: 'R123H' or 'p.R123H'
|
|
|
|
Thresholds: Pathogenic >0.564, Ambiguous 0.34-0.564, Benign <0.34
|
|
"""
|
|
|
|
result = tu.tools.AlphaMissense_get_variant_score(
|
|
uniprot_id=uniprot_id,
|
|
variant=variant
|
|
)
|
|
|
|
if result.get('status') == 'success' and result.get('data'):
|
|
score = result['data'].get('pathogenicity_score')
|
|
classification = result['data'].get('classification')
|
|
|
|
if classification == 'pathogenic':
|
|
acmg = 'PP3 (strong)'
|
|
elif classification == 'benign':
|
|
acmg = 'BP4 (strong)'
|
|
else:
|
|
acmg = 'neutral'
|
|
|
|
return {
|
|
'score': score,
|
|
'classification': classification,
|
|
'acmg_support': acmg
|
|
}
|
|
return None
|
|
```
|
|
|
|
### 3.3 EVE Evolutionary Prediction
|
|
|
|
```python
|
|
def get_eve_score(tu, chrom, pos, ref, alt):
|
|
"""Get EVE evolutionary pathogenicity score. Threshold: >0.5 = likely pathogenic."""
|
|
|
|
result = tu.tools.EVE_get_variant_score(
|
|
chrom=str(chrom), pos=pos, ref=ref, alt=alt
|
|
)
|
|
|
|
if result.get('status') == 'success':
|
|
eve_scores = result['data'].get('eve_scores', [])
|
|
if eve_scores:
|
|
best_score = eve_scores[0]
|
|
return {
|
|
'score': best_score.get('eve_score'),
|
|
'classification': best_score.get('classification'),
|
|
'gene': best_score.get('gene_symbol'),
|
|
'acmg_support': 'PP3' if best_score.get('eve_score', 0) > 0.5 else 'BP4'
|
|
}
|
|
return None
|
|
```
|
|
|
|
### 3.4 Integrated Prediction Strategy
|
|
|
|
```python
|
|
def comprehensive_pathogenicity_assessment(tu, variant_info):
|
|
"""Combine all prediction tools for robust classification."""
|
|
chrom = variant_info['chrom']
|
|
pos = variant_info['pos']
|
|
ref = variant_info['ref']
|
|
alt = variant_info['alt']
|
|
uniprot_id = variant_info.get('uniprot_id')
|
|
aa_change = variant_info.get('aa_change')
|
|
|
|
predictions = {}
|
|
|
|
cadd = get_cadd_score(tu, chrom, pos, ref, alt)
|
|
if cadd:
|
|
predictions['cadd'] = cadd
|
|
|
|
if uniprot_id and aa_change:
|
|
am = get_alphamissense_score(tu, uniprot_id, aa_change)
|
|
if am:
|
|
predictions['alphamissense'] = am
|
|
|
|
eve = get_eve_score(tu, chrom, pos, ref, alt)
|
|
if eve:
|
|
predictions['eve'] = eve
|
|
|
|
damaging_count = sum(1 for p in predictions.values()
|
|
if 'PP3' in p.get('acmg_support', ''))
|
|
benign_count = sum(1 for p in predictions.values()
|
|
if 'BP4' in p.get('acmg_support', ''))
|
|
|
|
if damaging_count >= 2 and benign_count == 0:
|
|
consensus = 'likely_damaging'
|
|
acmg = 'PP3 (multiple predictors concordant)'
|
|
elif benign_count >= 2 and damaging_count == 0:
|
|
consensus = 'likely_benign'
|
|
acmg = 'BP4 (multiple predictors concordant)'
|
|
else:
|
|
consensus = 'uncertain'
|
|
acmg = 'neutral (discordant predictions)'
|
|
|
|
return {
|
|
'predictions': predictions,
|
|
'consensus': consensus,
|
|
'acmg_recommendation': acmg
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 4: Structural Analysis (AlphaFold2)
|
|
|
|
```python
|
|
# 1. Get protein sequence
|
|
protein_seq = tu.tools.UniProt_get_sequence_by_accession(accession=uniprot_id)
|
|
|
|
# 2. Get/predict structure
|
|
try:
|
|
pdb_hits = tu.tools.PDBe_get_uniprot_mappings(uniprot_id=uniprot_id)
|
|
structure = tu.tools.PDB_get_structure(pdb_id=pdb_hits[0]['pdb_id'])
|
|
except:
|
|
structure = tu.tools.NvidiaNIM_alphafold2(
|
|
sequence=protein_seq['sequence'],
|
|
algorithm="mmseqs2"
|
|
)
|
|
|
|
# 3. Analyze variant position
|
|
# - Extract pLDDT at residue position
|
|
# - Calculate solvent accessibility
|
|
# - Check for nearby functional sites
|
|
```
|
|
|
|
**Structural Features to Report**:
|
|
- pLDDT at variant position
|
|
- Secondary structure (helix/sheet/coil)
|
|
- Solvent accessibility (buried/exposed)
|
|
- Distance to active site (if applicable)
|
|
- Interactions disrupted (H-bonds, salt bridges)
|
|
|
|
---
|
|
|
|
## Phase 4.5: Expression Context
|
|
|
|
```python
|
|
def validate_expression_context(tu, gene_symbol, phenotype_tissues):
|
|
"""Validate gene is expressed in phenotype-relevant tissues."""
|
|
|
|
sc_expression = tu.tools.CELLxGENE_get_expression_data(
|
|
gene=gene_symbol,
|
|
tissue=phenotype_tissues[0] if phenotype_tissues else "all"
|
|
)
|
|
|
|
gtex = tu.tools.GTEx_get_median_gene_expression(gene=gene_symbol)
|
|
|
|
relevant_expression = {
|
|
tissue: gtex.get(tissue, 0)
|
|
for tissue in phenotype_tissues
|
|
}
|
|
|
|
return {
|
|
'single_cell': sc_expression,
|
|
'gtex': relevant_expression,
|
|
'expressed_in_phenotype_tissue': any(v > 1 for v in relevant_expression.values())
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 5: Literature Search
|
|
|
|
```python
|
|
def comprehensive_literature_search(tu, gene, variant, phenotype):
|
|
"""Search across all literature sources."""
|
|
|
|
pubmed = tu.tools.PubMed_search_articles(
|
|
query=f'"{gene}" AND ("{variant}" OR functional)',
|
|
max_results=30
|
|
)
|
|
|
|
biorxiv = tu.tools.BioRxiv_list_recent_preprints(
|
|
query=f"{gene} {phenotype}",
|
|
limit=10
|
|
)
|
|
|
|
medrxiv = tu.tools.MedRxiv_get_preprint(
|
|
query=f"{gene} variant {phenotype}",
|
|
limit=10
|
|
)
|
|
|
|
key_papers = pubmed[:5]
|
|
for paper in key_papers:
|
|
citations = tu.tools.openalex_search_works(
|
|
query=paper['title'],
|
|
limit=1
|
|
)
|
|
paper['citation_count'] = citations[0].get('cited_by_count', 0) if citations else 0
|
|
|
|
return {
|
|
'pubmed': pubmed,
|
|
'preprints': biorxiv + medrxiv,
|
|
'key_papers_with_citations': key_papers
|
|
}
|
|
```
|
|
|
|
**Search Query Templates**:
|
|
```
|
|
# Gene + variant specific
|
|
"{GENE} AND ({HGVS_p} OR {AA_change})"
|
|
|
|
# Functional studies
|
|
"{GENE} AND (functional OR functional study OR mutagenesis)"
|
|
|
|
# Clinical reports
|
|
"{GENE} AND (case report OR patient) AND {phenotype}"
|
|
|
|
# Preprint-specific
|
|
"{GENE} genetics 2024" (for recent preprints)
|
|
```
|
|
|
|
**Warning**: Always flag preprints as NOT peer-reviewed in reports.
|