PGx CN: scan-date batch warning (PCN-006), CN-consistent BAF evidence (PCN-010, --bafs-in-cn),
noise-outlier exclusion + robustness test (PCN-017/EDGE-026), platform from the CSV manifest (CLI-032) Also: star-allele calls are checked for one manifest/build per run (PGX-011), duplications at the CN cap are named xN (PGX-022), and tandem names attach supporting variants to their full copy.
This commit is contained in:
1 parent
ed5e92b859
commit
1cb7b59559
8 files changed
+216
-25
No files matched your search
@@ -11,6 +11,7 @@ import contextlib
|
||||
import gzip
|
||||
import json
|
||||
import math
|
||||
import re
|
||||
import sqlite3
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass, field
|
||||
@@ -205,7 +206,9 @@ def solution_detail(m: GeneModel, obs: Observations, recs: list[dict[str, Any] |
|
||||
hap_of = {m.haplotypes[i]: i for i in s.copies}
|
||||
reason = {NO_CALL: "no_call", MASKED: "masked", NOT_COVERED: "not_on_array"}
|
||||
for name in names:
|
||||
core = name.split("x")[0] if "x" in name and name.split("x")[-1].isdigit() else name
|
||||
unit = name.split("+")[-1] # "*68+*4" → the full copy "*4"; "*2x2"/"*1xN" → "*2"/"*1"
|
||||
mx = re.fullmatch(r"(.+?)x(\d+|N)", unit)
|
||||
core = mx.group(1) if mx else unit
|
||||
h = hap_of.get(core)
|
||||
sup, unres = [], []
|
||||
if h is not None:
|
||||
@@ -300,6 +303,7 @@ def run_star_call(o: StarOptions, rep: Reporter) -> int:
|
||||
unres_rows: list[dict[str, Any]] = []
|
||||
not_cov_rows: dict[tuple[str, str], dict[str, Any]] = {}
|
||||
calls_json = []
|
||||
first_ident: tuple[str, str] | None = None
|
||||
for sid, path in sorted(snv.items()):
|
||||
recs, meta = read_vcf_sites(path, wanted)
|
||||
opts = meta.get("simurg_options", "")
|
||||
@@ -313,6 +317,12 @@ def run_star_call(o: StarOptions, rep: Reporter) -> int:
|
||||
or kv.get("include_loci", "none").lower() != "none"
|
||||
): # PGX-011
|
||||
raise SimurgError("E_PGX_VCF_UNSUITABLE", f"{path}: made with unsquashed duplicates or a locus filter")
|
||||
ident = (meta.get("simurg_manifest", "").split(";")[0], meta.get("simurg_genome_build", ""))
|
||||
if first_ident is None:
|
||||
first_ident = ident
|
||||
elif ident != first_ident and not o.allow_foreign_vcf: # PGX-011 (4)
|
||||
raise SimurgError("E_PGX_VCF_UNSUITABLE", f"{path}: manifest/build {ident} differs from {first_ident}",
|
||||
hint="call one manifest and build per run") # fmt: skip
|
||||
cn = {} if o.no_cn or sid not in cnv else read_cn_vcf(cnv[sid])
|
||||
masked = masks.get(meta.get("simurg_manifest", "").split(";")[0], set())
|
||||
sample_name = sid
|
||||
@@ -375,6 +385,12 @@ def call_one_gene(
|
||||
feasible_ratio=o.feasible_ratio, structure=structure) # fmt: skip
|
||||
if not sols:
|
||||
return None, [], [], [], []
|
||||
if gcn is not None and gcn >= 5: # PGX-022: CN 5 means "5 or more" → the duplication count is unknown (xN)
|
||||
import re as _re
|
||||
|
||||
for s_ in sols:
|
||||
s_.haplotypes = tuple(_re.sub(r"x\d+$", "xN", h) for h in s_.haplotypes)
|
||||
s_.members = [tuple(_re.sub(r"x\d+$", "xN", h) for h in mm) for mm in s_.members]
|
||||
top = sols[0]
|
||||
details = [solution_detail(m, obs, vrec, s_, top, o.low_gs, MAX_EDIT_DISTANCE) for s_ in sols]
|
||||
flags = []
|
||||
|
||||
Reference in new issue
Block a user