3632060685c8b5ca508635ff45b0c27bf0df0dc1 lrnassar Fri Aug 21 10:32:48 2026 -0700 Harden Cardiomyopathy VCEP scripts against silent data-source failures. refs #38139 - cmpVCEPProvisionalClass: a SpliceAI read failure (wrong/renamed bigBed path) now stops the script with a clear error instead of returning an empty dict, which had silently fired BP7 for every synonymous variant with the mouseover showing "no record" as if it were a measurement. Also stop suppressing bigBedToBed's stderr. - cmpVCEPWalsh2019: compare the ClinVar transcript by accession without the version so a ClinVar version bump does not silently drop every matched row for a gene. diff --git src/hg/makeDb/scripts/cardiomyopathyVCEP/cmpVCEPProvisionalClass.py src/hg/makeDb/scripts/cardiomyopathyVCEP/cmpVCEPProvisionalClass.py index d1449537f69..f3f7219875e 100644 --- src/hg/makeDb/scripts/cardiomyopathyVCEP/cmpVCEPProvisionalClass.py +++ src/hg/makeDb/scripts/cardiomyopathyVCEP/cmpVCEPProvisionalClass.py @@ -286,49 +286,56 @@ m = PROT_MISSENSE_RE.search(h) if m and m.group(3) in AA3TO1: codon = int(m.group(2)) alt_aa1 = AA3TO1[m.group(3)] break if codon is not None and alt_aa1 is not None: ref.append({'gkey': gkey, 'gene': gene, 'codon': codon, 'alt_aa1': alt_aa1}) print(f' EvRepo P/LP missense reference: {len(ref)} entries', file=sys.stderr) return ref def batch_spliceai(regions): """Per gene region: (chrom, pos1, ref, alt) -> max SpliceAI delta (bed9+4: AIscore=col9, name='ref>alt').""" sa = {} for chrom, start, end in regions: + # A read failure here (e.g. a wrong/renamed SpliceAI path) must stop the script, not be + # skipped: an empty result would give every synonymous variant sa_score 0.0, firing BP7 for + # all of them with the mouseover showing "no record" as if it were a measurement. Let + # bigBedToBed's own error reach stderr (no DEVNULL) so the real cause is visible. try: out = subprocess.check_output(['bigBedToBed', f'-chrom={chrom}', f'-start={start}', - f'-end={end}', SPLICEAI_BB, 'stdout'], - text=True, stderr=subprocess.DEVNULL) - except subprocess.CalledProcessError: - continue + f'-end={end}', SPLICEAI_BB, 'stdout'], text=True) + except (subprocess.CalledProcessError, OSError) as e: + sys.exit(f'ERROR: bigBedToBed failed on {SPLICEAI_BB} for {chrom}:{start}-{end} ({e}); ' + f'cannot compute BP7. Fix the SpliceAI path, or pass --no-spliceai to skip it.') for line in out.splitlines(): f = line.split('\t') if len(f) < 10 or '>' not in f[3]: continue ref, alt = f[3].split('>', 1) pos1 = int(f[2]) # chromEnd == 1-based SNV pos try: score = float(f[9]) except ValueError: continue k = (chrom, pos1, ref, alt) if score > sa.get(k, -1): sa[k] = score + if not sa: + sys.exit(f'ERROR: no SpliceAI records read from {SPLICEAI_BB}; refusing to emit BP7 for every ' + f'synonymous variant. Check the file and path, or pass --no-spliceai to skip it.') print(f' SpliceAI entries: {len(sa)}', file=sys.stderr) return sa TRUNCATING_SO = {'stop_gained', 'frameshift_variant'} def variant_kind(so): """Most-relevant consequence label for display.""" for k in ('stop_gained', 'frameshift_variant', 'stop_lost', 'splice_acceptor_variant', 'splice_donor_variant', 'missense_variant', 'inframe_deletion', 'inframe_insertion', 'initiator_codon_variant', 'splice_region_variant', 'synonymous_variant', 'intron_variant', '5_prime_UTR_variant', '3_prime_UTR_variant'): if k in so: return k