a56d88e05670b759ff3b32829542537ccc790c57
lrnassar
  Tue Apr 28 19:18:20 2026 -0700
Address CR feedback on insight + tp53 hub scripts. refs #37418

Drop duplicated bash() wrappers in favor of subprocess.run / check_output
with list args, eliminating shell=True, embedded-quote concerns, and
stderr-into-stdout merging. Centralize common operations as
run_sort_bed/run_liftOver in tp53FuncLib alongside existing run_bedToBigBed.

Switch HTML escaping to stdlib html.escape() consistently. insightHCIPriors
mouseover (previously unescaped) now escapes HGVS fields, addressing the
specific c.123A>G case Jonathan flagged. Replace invalid </br> tags with
<br> across all five affected mouseover sites.

diff --git src/hg/makeDb/scripts/insight/insightClinDomains.py src/hg/makeDb/scripts/insight/insightClinDomains.py
index 3cd4301be7c..6e6fc894944 100644
--- src/hg/makeDb/scripts/insight/insightClinDomains.py
+++ src/hg/makeDb/scripts/insight/insightClinDomains.py
@@ -10,40 +10,30 @@
 NCBI RefSeq transcript coordinates from hgsql for both hg38 and hg19.
 
 Transcripts used:
   MLH1: NM_000249.4  (chr3, + strand)
   MSH2: NM_000251.3  (chr2, + strand)
   MSH6: NM_000179.3  (chr2, + strand)
   PMS2: NM_000535.7  (chr7, - strand)
 
 Author: Generated for InSiGHT VCEP
 Date: 2025
 """
 
 import subprocess
 import os
 
-def bash(cmd):
-    """Run the cmd in bash subprocess"""
-    try:
-        rawBashOutput = subprocess.run(cmd, check=True, shell=True,
-                                       stdout=subprocess.PIPE, universal_newlines=True, stderr=subprocess.STDOUT)
-        bashStdout = rawBashOutput.stdout
-    except subprocess.CalledProcessError as e:
-        raise RuntimeError("command '{}' return with error (code {}): {}".format(e.cmd, e.returncode, e.output))
-    return(bashStdout)
-
 # ============================================================================
 # Configuration
 # ============================================================================
 OUTPUT_DIR = "/hive/users/lrnassar/insightHub/clinDomains"
 
 # Transcripts to query
 TRANSCRIPTS = {
     'MLH1': 'NM_000249.4',
     'MSH2': 'NM_000251.3',
     'MSH6': 'NM_000179.3',
     'PMS2': 'NM_000535.7',
 }
 
 # Domain data from proteinDomains.txt (gene, domain_name, aa_start, aa_end)
 DOMAINS = [
@@ -97,31 +87,31 @@
    uint thickEnd;      "Same as chromEnd"
    uint reserved;      "RGB value (use R,G,B string in input file)"
    string geneSymbol;  "Gene symbol"
    string NMaccession; "NCBI NM isoform accession"
    string AAlocation;  "Amino acid location of domain"
    string _mouseOver;  "Field only used as mouseOver"
    )"""
 
 # ============================================================================
 # Functions for coordinate computation
 # ============================================================================
 
 def get_transcript_info(db, accession):
     """Query hgsql to get transcript information from ncbiRefSeq"""
     query = f"SELECT name, chrom, strand, txStart, txEnd, cdsStart, cdsEnd, exonStarts, exonEnds FROM ncbiRefSeq WHERE name='{accession}'"
-    result = bash(f'hgsql {db} -Ne "{query}"')
+    result = subprocess.check_output(["hgsql", db, "-Ne", query], text=True)
 
     if not result.strip():
         raise ValueError(f"Transcript {accession} not found in {db}.ncbiRefSeq")
 
     fields = result.strip().split('\t')
 
     # Parse exon starts and ends (comma-separated, trailing comma)
     exon_starts = [int(x) for x in fields[7].rstrip(',').split(',')]
     exon_ends = [int(x) for x in fields[8].rstrip(',').split(',')]
 
     return {
         'name': fields[0],
         'chrom': fields[1],
         'strand': fields[2],
         'txStart': int(fields[3]),
@@ -221,31 +211,31 @@
         accession = tx['name']
 
         cds_regions = build_cds_regions(tx)
 
         if strand == '+':
             segments = aa_to_genomic_plus(aa_start, aa_end, cds_regions)
         else:
             segments = aa_to_genomic_minus(aa_start, aa_end, cds_regions)
 
         for seg_start, seg_end, exon_num in segments:
             if seg_start >= seg_end:
                 print(f"  WARNING: Skipping invalid segment for {gene} {domain}: {seg_start}-{seg_end}")
                 continue
 
             aa_loc = f"{aa_start}-{aa_end}"
-            mouse_over = f"<b>Domain: </b>{domain}</br><b>Gene: </b>{gene}</br><b>Transcript: </b>{accession}</br><b>Amino acid loc:</b> {aa_loc}"
+            mouse_over = f"<b>Domain: </b>{domain}<br><b>Gene: </b>{gene}<br><b>Transcript: </b>{accession}<br><b>Amino acid loc:</b> {aa_loc}"
             bed_line = f"{chrom}\t{seg_start}\t{seg_end}\t{domain}\t0\t.\t{seg_start}\t{seg_end}\t230,3,131\t{gene}\t{accession}\t{aa_loc}\t{mouse_over}"
             bed_lines.append(bed_line)
 
     return bed_lines
 
 def create_track(db, output_dir):
     """Create BED and bigBed files for a given genome assembly"""
     print(f"\n{'='*70}")
     print(f"Processing {db}")
     print(f"{'='*70}")
 
     # Query transcript info from hgsql
     print(f"\nQuerying transcript coordinates from {db}.ncbiRefSeq...")
     transcripts_info = {}
     for gene, accession in TRANSCRIPTS.items():
@@ -253,40 +243,43 @@
         transcripts_info[gene] = get_transcript_info(db, accession)
 
     # Generate BED entries
     print("\nGenerating BED entries...")
     bed_lines = generate_bed_entries(db, transcripts_info)
     print(f"  Generated {len(bed_lines)} domain segments")
 
     # Write BED file
     bed_file = os.path.join(output_dir, f"InSiGHTclinDomains_{db}.bed")
     print(f"\nWriting BED file: {bed_file}")
     with open(bed_file, 'w') as f:
         f.write('\n'.join(bed_lines) + '\n')
 
     # Sort BED file
     print("Sorting BED file...")
-    bash(f"sort -k1,1 -k2,2n {bed_file} -o {bed_file}")
+    subprocess.run(["sort", "-k1,1", "-k2,2n", bed_file, "-o", bed_file], check=True)
 
     # Create bigBed
     as_file = os.path.join(output_dir, "InSiGHTclinDomains.as")
     bb_file = os.path.join(output_dir, f"InSiGHTclinDomains{db.capitalize()}.bb")
     chrom_sizes = f"/cluster/data/{db}/chrom.sizes"
 
     print(f"\nCreating bigBed file: {bb_file}")
     try:
-        bash(f"bedToBigBed -as={as_file} -type=bed9+4 -tab {bed_file} {chrom_sizes} {bb_file}")
+        subprocess.run(
+            ["bedToBigBed", "-as=" + as_file, "-type=bed9+4", "-tab",
+             bed_file, chrom_sizes, bb_file],
+            check=True)
         print(f"  Successfully created: {bb_file}")
     except Exception as e:
         print(f"  ERROR creating bigBed: {e}")
 
     return bed_file, bb_file
 
 # ============================================================================
 # Main execution
 # ============================================================================
 if __name__ == "__main__":
     print("=" * 70)
     print("InSiGHT VCEP Clinically Relevant Protein Domains Track Generator")
     print("=" * 70)
 
     # Create output directory if needed