PGx: all CPIC gene kinds — G6PD, variant-based, HLA tags, *0 CN genes (PGX-006/007/021)
ci / test (3.12) (push) Failing after 9s
ci / test (3.13) (push) Failing after 11s

- DB build resolves the PGX-007 tag variants from the manifest's probes; coverage over all gene kinds.
- Variant-based genes reported per variant (rs.. REF/ALT | Reference) with the CPIC lowest-activity diplotype;
  tagging genes as positive/negative with the population caveat; GSTM1/GSTT1/UGT2B17 *0 from targeted CN.
- Case-insensitive phenotype keys (MT-RNR1). GeT-RM check extended to VKORC1 and the CN genes:
  131/144 gene calls (docs/validation/V2_pgx_star.md).

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 16:24:33 +01:00
1 parent 4dafce3f18
commit 58dbd8f514
10 files changed
+336 -23

No files matched your search

+37 -1
View File
@@ -43,6 +43,20 @@ def truth_options(text: str) -> list[set[str]] | None:
return out
def _gstm1(t: str) -> int: # GeT-RM GSTM1: DEL/DEL = no copy; *A/*B assays do not see a heterozygous deletion
return 0 if t.replace(" ", "").upper() == "DEL/DEL" else 1
def _gstt1(t: str) -> int: # "0" null, "(*A/0)" one null allele, "(*A/*A)" two copies
t = t.replace(" ", "")
return 0 if t in ("0", "0/0") else (1 if "/0" in t or "0/" in t else 2)
def _ugt2b17(t: str) -> int: # *2 = whole-gene deletion
return 2 - t.count("*2")
CN_TRUTH = {"GSTM1": _gstm1, "GSTT1": _gstt1, "UGT2B17": _ugt2b17} # capped: GSTM1 at 1, the others at 2
LEGACY_NOMENCLATURE = {"UGT1A1", "SLCO1B1"} # GeT-RM uses pre-2023 names (*60, SLCO1B1 *1B/*17/*21) not in CPIC
@@ -84,8 +98,30 @@ def main() -> int:
t = truth.get((ind, g["gene"]))
if t is None:
continue
opts = truth_alternatives(t)
st = per_gene[g["gene"]]
if g["gene"] in CN_TRUTH: # whole-gene deletion genes: compare copy-number classes (PGX-006)
want_cn = CN_TRUTH[g["gene"]](t)
cn = g.get("copy_number")
got_cn = None if cn is None else (min(cn, 2) if g["gene"] != "GSTM1" else min(cn, 1))
st["n"] += 1
ok = want_cn is not None and want_cn == got_cn
st["top"] += ok
st["equivalent"] += ok
if not ok:
detail.append({"individual": ind, "gene": g["gene"], "truth": t, "call": g["genotype"],
"within_equivalents": False}) # fmt: skip
continue
if g["gene"] == "VKORC1": # c.-1639G>A (minus strand) = rs9923231 C>T: count A = count T
want = t.count("A")
got = g["genotype"].split("/")[-1].count("T") if g["genotype"] != "Reference" else 0
st["n"] += 1
st["top"] += want == got
st["equivalent"] += want == got
if want != got:
detail.append({"individual": ind, "gene": "VKORC1", "truth": t, "call": g["genotype"],
"within_equivalents": False}) # fmt: skip
continue
opts = truth_alternatives(t)
if not opts:
st["not_comparable"] += 1
continue