Initial commit

This commit is contained in:
2025-11-28 11:52:04 +08:00
commit f74dc351f7
51 changed files with 2402 additions and 0 deletions

41
tests/test_acmg.py Normal file
View File

@@ -0,0 +1,41 @@
from genomic_consultant.acmg.tagger import ACMGConfig, tag_variant
from genomic_consultant.utils.models import Variant
def test_ba1_trumps():
cfg = ACMGConfig(ba1_af=0.05, bs1_af=0.01, pm2_af=0.0005, lof_genes=set())
v = Variant(chrom="1", pos=1, ref="A", alt="T", allele_frequency=0.2)
result = tag_variant(v, cfg)
assert result.suggested_class == "Benign"
assert any(e.tag == "BA1" for e in result.evidence)
def test_pvs1_pm2_likely_pathogenic():
cfg = ACMGConfig(lof_genes={"GENE1"}, pm2_af=0.0005, ba1_af=0.05, bs1_af=0.01)
v = Variant(
chrom="1",
pos=1,
ref="A",
alt="T",
gene="GENE1",
consequence="stop_gained",
allele_frequency=0.0001,
)
result = tag_variant(v, cfg)
assert result.suggested_class == "Likely pathogenic"
tags = {e.tag for e in result.evidence}
assert {"PVS1", "PM2"} <= tags
def test_bp7_supporting():
cfg = ACMGConfig(bp7_splice_ai_max=0.1)
v = Variant(
chrom="1",
pos=1,
ref="A",
alt="T",
consequence="synonymous_variant",
annotations={"splice_ai_delta_score": 0.05},
)
result = tag_variant(v, cfg)
assert any(e.tag == "BP7" for e in result.evidence)

16
tests/test_aggregate.py Normal file
View File

@@ -0,0 +1,16 @@
from pathlib import Path
from genomic_consultant.panels.aggregate import merge_mappings
import json
def test_merge_mappings(tmp_path: Path):
a = tmp_path / "a.json"
b = tmp_path / "b.json"
a.write_text('{"source":"A","phenotype_to_genes":{"HP:1":["G1","G2"]}}')
b.write_text('{"source":"B","phenotype_to_genes":{"HP:1":["G2","G3"],"HP:2":["G4"]}}')
out = tmp_path / "out.json"
merge_mappings([a, b], out)
data = json.loads(out.read_text())
assert sorted(data["phenotype_to_genes"]["HP:1"]) == ["G1", "G2", "G3"]
assert data["phenotype_to_genes"]["HP:2"] == ["G4"]

View File

@@ -0,0 +1,27 @@
from pathlib import Path
from genomic_consultant.orchestration.phase1_pipeline import run_phase1_pipeline
def test_phase1_pipeline_with_existing_tsv():
root = Path(__file__).resolve().parents[1]
tsv = root / "sample_data/example_annotated.tsv"
panel = root / "configs/panel.example.json"
acmg = root / "configs/acmg_config.example.yaml"
result = run_phase1_pipeline(
samples=None,
reference_fasta=None,
workdir=root / "runtime",
output_prefix="demo",
acmg_config_path=acmg,
max_af=0.05,
panel_path=panel,
phenotype_id=None,
phenotype_mapping=None,
report_format="markdown",
existing_tsv=tsv,
skip_call=True,
skip_annotate=True,
)
assert "Panel Report" in result.panel_report
assert result.tsv_path == tsv

18
tests/test_resolver.py Normal file
View File

@@ -0,0 +1,18 @@
from pathlib import Path
from genomic_consultant.panels.resolver import PhenotypeGeneResolver
def test_resolver_build_panel(tmp_path: Path):
data = {
"version": "test",
"source": "example",
"phenotype_to_genes": {"HP:0001": ["GENE1", "GENE2"]},
}
path = tmp_path / "map.json"
path.write_text('{"version":"test","source":"example","phenotype_to_genes":{"HP:0001":["GENE1","GENE2"]}}')
resolver = PhenotypeGeneResolver.from_json(path)
panel = resolver.build_panel("HP:0001")
assert panel is not None
assert panel.genes == ["GENE1", "GENE2"]
assert "phenotype_id" in panel.metadata

28
tests/test_store.py Normal file
View File

@@ -0,0 +1,28 @@
from genomic_consultant.store.query import GenomicStore
from genomic_consultant.utils.models import FilterConfig, Variant
def test_filter_by_gene_and_af():
store = GenomicStore(
variants=[
Variant(chrom="1", pos=1, ref="A", alt="T", gene="GENE1", allele_frequency=0.02),
Variant(chrom="1", pos=2, ref="G", alt="C", gene="GENE2", allele_frequency=0.0001),
]
)
res = store.get_variants_by_gene("ind", ["GENE2"], filters=FilterConfig(max_af=0.001))
assert len(res) == 1
assert res[0].gene == "GENE2"
def test_consequence_include_exclude():
store = GenomicStore(
variants=[
Variant(chrom="1", pos=1, ref="A", alt="T", gene="GENE1", consequence="missense_variant"),
Variant(chrom="1", pos=2, ref="G", alt="C", gene="GENE1", consequence="synonymous_variant"),
]
)
res = store.get_variants_by_gene(
"ind", ["GENE1"], filters=FilterConfig(consequence_includes=["missense"], consequence_excludes=["synonymous"])
)
assert len(res) == 1
assert res[0].consequence == "missense_variant"

33
tests/test_store_tsv.py Normal file
View File

@@ -0,0 +1,33 @@
from pathlib import Path
from genomic_consultant.store.query import GenomicStore
def test_from_tsv_with_extra_columns(tmp_path: Path):
content = "\t".join(
[
"#CHROM",
"POS",
"REF",
"ALT",
"SYMBOL",
"Consequence",
"Protein_position",
"PolyPhen",
"SIFT",
"CLIN_SIG",
"AF",
"gnomAD_AF",
"SpliceAI",
"CADD_PHRED",
]
) + "\n"
content += "1\t100\tA\tT\tGENE1\tmissense_variant\tp.X\t.\t.\tPathogenic\t0.0001\t0.0002\t0.05\t20.1\n"
path = tmp_path / "v.tsv"
path.write_text(content)
store = GenomicStore.from_tsv(path)
assert len(store.variants) == 1
v = store.variants[0]
assert v.annotations["splice_ai_delta_score"] == 0.05
assert v.annotations["cadd_phred"] == 20.1