PGx phenotypes: pgx phenotype|annotate (PGX-024..028, CLI-034)

CPIC lookups by allele function counts or activity values (duplications N x activity, *5/*0 = 0, total-score
fallback), Indeterminate when feasible solutions disagree or an allele is not in the guideline; per-sample
simurg.pgx/1 JSON and diplotype_annotations.tsv with PGX-026 metadata lines.
This commit is contained in:
kemal committed 2026-09-29 16:03:27 +01:00
1 parent c2b45f8a76
commit 9d3242828e
7 files changed
+338 -1

No files matched your search

+36
View File
@@ -0,0 +1,36 @@
"""Diplotype → phenotype (spec PGX-024, PGX-025)."""
from __future__ import annotations
import json
from simurg.pgx.phenotype import build_table, gene_phenotype, phenotype
ACT_ROWS = [(json.dumps({"1.0": 2}), "2.0", "Normal Metabolizer"), (json.dumps({"0.0": 1, "1.0": 1}), "1.0",
"Intermediate Metabolizer"), (json.dumps({"0.0": 2}), "0.0", "Poor Metabolizer"),
(json.dumps({"1.0": 1, "2.0": 1}), "3.0", "Ultrarapid Metabolizer")] # fmt: skip
ACT_ALLELES = [("*1", "Normal function", "1.0"), ("*4", "No function", "0.0"), ("*10", "Decreased function", "0.25")]
FUNC_ROWS = [(json.dumps({"Normal function": 2}), "n/a", "Normal Metabolizer"),
(json.dumps({"Normal function": 1, "No function": 1}), "n/a", "Intermediate Metabolizer")] # fmt: skip
FUNC_ALLELES = [("*1", "Normal function", None), ("*2", "No function", None)]
def test_activity_genes() -> None:
t = build_table("CYP2D6", ACT_ROWS, ACT_ALLELES)
assert phenotype(t, ("*1", "*4")).phenotype == "Intermediate Metabolizer"
r = phenotype(t, ("*1x2", "*1")) # duplication: 2 × activity
assert r.phenotype == "Ultrarapid Metabolizer" and r.activity_score == 3.0
assert phenotype(t, ("*1", "*5")).phenotype == "Intermediate Metabolizer" # *5 = whole-gene deletion
r = phenotype(t, ("*1", "*10")) # key absent → largest tabulated total ≤ 1.25
assert r.phenotype == "Intermediate Metabolizer" and r.activity_score == 1.25
r = phenotype(t, ("*1", "*99")) # PGX-024: unknown allele never crashes
assert r.phenotype == "Indeterminate" and "phenotype_not_in_guideline" in r.flags
def test_function_genes_and_disagreement() -> None: # PGX-025
t = build_table("CYP2C19", FUNC_ROWS, FUNC_ALLELES)
assert phenotype(t, ("*2", "*1")).phenotype == "Intermediate Metabolizer"
res, per = gene_phenotype(t, [("*1", "*1"), ("*1", "*2")])
assert res.phenotype == "Indeterminate" and per == ["Normal Metabolizer", "Intermediate Metabolizer"]
res, per = gene_phenotype(t, [("*1", "*2"), ("*2", "*1")])
assert res.phenotype == "Intermediate Metabolizer" and per == []