PGx: ClinPGx variant-based genes (PGX-008) and --guideline dpwg (PGX-025)
ci / test (3.12) (push) Failing after 10s
ci / test (3.13) (push) Failing after 11s

- fetch_sources.sh downloads ClinPGx clinicalVariants (CC BY-SA 4.0); pgx db build adds genotype-only
  variant-based genes (levels 1A-2B, level 3 for the parity list), rsIDs resolved from the manifest's probes:
  70 genes in the DB.
- pgx phenotype --guideline dpwg: CPIC mappings for CPIC genes (as ClinPGx curates DPWG), no phenotype for
  DPWG-only genes (flags dpwg_via_cpic_mapping / dpwg_mapping_not_available). K-029 updated.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01814GKVzZVL3FuF3DeiZXhv
This commit is contained in:
kemalandClaude Opus 5.5 committed 2026-09-29 17:01:36 +01:00
1 parent 4176ef33de
commit aee8caa39e
8 files changed
+161 -10

No files matched your search

+13 -5
View File
@@ -17,6 +17,11 @@ from simurg.core.log import Reporter
from simurg.pgx.phenotype import build_table, gene_phenotype
JSON_SCHEMA = "simurg.pgx/1"
# PGX-025 DPWG gene coverage. ClinPGx curates DPWG guidelines with the CPIC allele-function and phenotype mappings for
# genes CPIC covers [DOC: ClinPGx, "Genotype-phenotype mapping for curation of DPWG guidelines"], so those genes use the
# CPIC tables (flag dpwg_via_cpic_mapping); DPWG-only genes have no public machine-readable mapping here.
DPWG_GENES = ("CYP1A2", "CYP2B6", "CYP2C19", "CYP2C9", "CYP2D6", "CYP3A4", "CYP3A5", "DPYD", "NUDT15", "SLCO1B1",
"TPMT", "UGT1A1", "VKORC1", "F5") # fmt: skip
TSV_SCHEMA = "simurg.pgx_tsv/1"
@@ -65,9 +70,8 @@ def run_phenotype(o: PhenoOptions, rep: Reporter) -> int:
"""``simurg pgx phenotype`` (CLI-034)."""
from simurg.core.atomic import atomic_write_text
if o.guideline != "cpic":
raise SimurgError("E_PGX_GUIDELINE", f"guideline {o.guideline!r} is not in this PGx DB (CPIC only)",
hint="DPWG tables are not built yet (K-029)") # fmt: skip
if o.guideline not in ("cpic", "dpwg"):
raise SimurgError("E_PGX_GUIDELINE", f"unknown guideline {o.guideline!r}", hint="cpic or dpwg")
src = o.calls / "pgx_calls.json"
if not src.exists():
raise SimurgError("E_NO_INPUT", f"{src} not found", hint="run pgx star-allele call first")
@@ -77,9 +81,11 @@ def run_phenotype(o: PhenoOptions, rep: Reporter) -> int:
meta = db_meta(o.db)
con = sqlite3.connect(o.db)
tables = {}
for (gene,) in con.execute("SELECT DISTINCT gene FROM phenotypes WHERE guideline = ?", (o.guideline,)):
for (gene,) in con.execute("SELECT DISTINCT gene FROM phenotypes WHERE guideline = 'cpic'"):
if o.guideline == "dpwg" and gene not in DPWG_GENES:
continue
rows = con.execute("SELECT lookup_key, activity_score, phenotype FROM phenotypes WHERE gene = ? AND "
"guideline = ?", (gene, o.guideline)).fetchall() # fmt: skip
"guideline = 'cpic'", (gene,)).fetchall() # fmt: skip
alle = con.execute("SELECT allele, function, activity FROM alleles WHERE gene = ?", (gene,)).fetchall()
tables[gene] = build_table(gene, rows, alle)
tested = {g: [a for (a,) in con.execute("SELECT allele FROM alleles WHERE gene = ? AND defined = 1", (g,))]
@@ -104,6 +110,8 @@ def run_phenotype(o: PhenoOptions, rep: Reporter) -> int:
elif g.get("call_type") == "copy_number":
res, per = None, []
flags = list(g.get("flags", [])) + (res.flags if res else [])
if o.guideline == "dpwg" and g["gene"] in DPWG_GENES:
flags.append("dpwg_via_cpic_mapping" if t is not None else "dpwg_mapping_not_available")
top = g["candidates"][0] if g["candidates"] else {}
top_alleles = top.get("alleles", [])
entry = {"gene": g["gene"], "call_type": g.get("call_type", "star_allele"), "genotype": g["genotype"],