3522a9acc8256a35230b07f2e7ab3fc08c827f33 mspeir Sat Sep 5 17:19:20 2026 -0700 singleCellSignalsPeaks: correct the hg38 description page counts and cite BrainVar, refs #38219 From the v503 code review. Commit 3aac3982aa2 dropped 5 hg38 subtracks and updated the makeDoc, but the dataset list on the description page was missed, so the page disagreed with both the .ra and the facet menu: - Risk Loci in Alzheimer's and Parkinson's: 1 peak subtrack, not 2 (neuro-degen-atac/peaks.bb was the one dropped) - BrainVar: 9 signal subtracks, not 13, and the text still described tracks "for all nuclei together" -- the 4 combined-stage tracks are exactly the ones dropped, so every remaining BrainVar track carries a life stage Counts re-derived from the .ra by type (bigWig = signal, else peak): the other 7 hg38 datasets and all 9 mm10 datasets were already right, 929 and 587 total. BrainVar was also the only hg38 dataset with no citation, while its methods text had grown quite specific. It now cites Werling et al. 2020, which described the cohort, with the caveat that the single-nucleus data shown here were not part of that paper -- the same distinction the Cell Browser desc.conf makes. Citing it bare would credit a bulk RNA-seq and WGS study for 10x Multiome data. References are alphabetical, so hg38 is now 10 and mm10 still 7. copySingleCellSignalsPeaksFiles.py: colors_json() reads the palette in a with block, and a malformed R,G,B row now raises the SystemExit the rest of the file uses, naming the file, line number and offending field, instead of a bare ValueError or TypeError from int() or the %02X format. Output is unchanged. makeDoc: the mm10 cell-class note now records that the bare "Progenitor" row in celltype-class.tsv is live rather than leftover -- build_stanzas' class_key() collapses plurals, so BrainVar's "Progenitors" looks up under the singular key and takes its class and color from that one row. Retiring or qualifying the row would grey out that track. A non-neural label needs a specific cell type added instead, which is how "Nephron progenitors" comes out Stromal. Also fixed a contradiction there: the file claimed celltype-class.tsv is built by build_celltype_crosswalks.py a hundred lines above the note saying, correctly, that it is hand-curated and not generated. The submitters confirmed BrainVar is 100 bp tiles, so the 1 kb in their methods text is an error and the page is right; recorded in the hg38 makeDoc. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> diff --git src/hg/makeDb/scripts/singleCellSignalsPeaks/copySingleCellSignalsPeaksFiles.py src/hg/makeDb/scripts/singleCellSignalsPeaks/copySingleCellSignalsPeaksFiles.py index cbf63d44b87..8674d5fff4e 100644 --- src/hg/makeDb/scripts/singleCellSignalsPeaks/copySingleCellSignalsPeaksFiles.py +++ src/hg/makeDb/scripts/singleCellSignalsPeaks/copySingleCellSignalsPeaksFiles.py @@ -92,36 +92,42 @@ if not os.path.isfile(pal): pal = os.path.join(build, "celltype-crosswalks", "celltype-palette.tsv") return pal def colors_json(pal): """Render the palette as the faceted UI's colorSettingsUrl JSON. facetedComposite.js wants {facetName: {facetValue: cssColor}} and draws a swatch beside each checkbox of any facet named in it. The facet name must be the metadata column ("Cell_class") and each key must be the column value verbatim -- the lookup is an exact string match, so a case or spacing difference silently drops the swatch. The whole palette is emitted, not just the classes this assembly uses: extra keys are ignored by the JS, and it keeps hg38 and mm10 sharing one class->color mapping. """ colors = {} - for line in open(pal): + with open(pal) as fh: + for lineNo, line in enumerate(fh, 1): f = line.rstrip("\n").split("\t") if len(f) < 2 or not f[0].strip(): continue + try: rgb = [int(x) for x in f[1].split(",")] colors[f[0]] = "#%02X%02X%02X" % tuple(rgb) + except (ValueError, TypeError): + raise SystemExit("ERROR: %s line %d has %r where an R,G,B triple of " + "integers was expected, so the facet color swatch for " + "%r cannot be written." % (pal, lineNo, f[1], f[0])) if not colors: raise SystemExit("ERROR: no class/color rows read from %s, so the facet color " "swatches cannot be written." % pal) return json.dumps({"Cell_class": colors}, indent=4, sort_keys=True) + "\n" def load_relpath_to_abs(manifest, asm): m = {} with open(manifest) as fh: hdr = fh.readline().rstrip("\n").split("\t") ai, ui, asmi = hdr.index("abs_path"), hdr.index("track_url"), hdr.index("assembly") for line in fh: f = line.rstrip("\n").split("\t") if len(f) <= max(ai, ui, asmi) or f[asmi] != asm: continue m[urlparse(f[ui]).path.lstrip("/")] = f[ai]