0c42aea56b751a2b4a58feb225a9659819a90994 markd Sat Oct 3 06:22:01 2026 -0700 Nest the TSS tracks on alpha only, leaving the release as it is. refs #35528 PRO-cap and ProCapNet sit inside a transcriptionStart superTrack on alpha. That needs #38460, which is on master but not in a release, and these tracks are already released, so each source is written twice with complementary release tags: the nested copy alpha, the flat copy beta,public. hgTrackDb takes the copy whose release matches and rejects two whose releases overlap. Verified that beta and public are untouched: a public build from this file is byte-identical to a public build from the released flat tree, same md5 over tableName, shortLabel, type, visibility, priority and settings. tdbQuery -check -strict passes on all three releases, and an alpha build has the container with both members parented while beta and public have neither. Drop the release tags and the flat copies once #38460 ships. The makedocs also record that the alpha build needs hgTrackDb and trackDbToTxt in /cluster/bin/x86_64 to carry the #38460 fix. make alpha does not install there, BINDIR defaults to ~/bin/$MACHTYPE, so they were put in by hand and the weekly utils build will overwrite them. diff --git src/hg/makeDb/outside/proCapNet/proCapNetTrackDb src/hg/makeDb/outside/proCapNet/proCapNetTrackDb index 676b91f9052..8393ab1e258 100755 --- src/hg/makeDb/outside/proCapNet/proCapNetTrackDb +++ src/hg/makeDb/outside/proCapNet/proCapNetTrackDb @@ -1,186 +1,212 @@ #!/usr/bin/env python3 """Generate the transcriptionStart.ra trackDb file for one assembly. Layout is a superTrack holding one composite per data source: proCapNet for the model predictions and sequence-contribution scores, encode4ProCap for the experimental PRO-cap signal. hs1 has predictions only. These are traditional composites, not faceted ones. A faceted composite is routed to facetedCompositeUi(), which returns before cfgByCfgType(), so the wiggle controls are never drawn; signal tracks need them. A container multiWig under a traditional composite still draws as one overlay row per cell line. What it does not get is an inline config block on the composite page, because compositeUiSubtracks() lists descendant leaves and a multiWig is not a leaf; each overlay is configured from its own hgTrackUi page instead. Subtrack names are <composite>_<cell>_<dataType>. STATUS: this only partly works and is expected to be replaced by a superTrack holding the multiWig overlays directly. hgTrackUi lists descendant leaves, not containers, so an overlay gets no inline config block and deselecting both strands of a cell line leaves an empty row (#38441). A superTrack gives each overlay its own page and no flattening, at the cost of the matrix and the sample class filter. """ import argparse from pycbio.sys import cli, fileOps from pycbio.tsv import TsvReader GBDB = "/gbdb/{db}/{track}" def parseArgs(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("db") parser.add_argument("experimentsTsv") parser.add_argument("outRa") return cli.parseOptsArgsWithLogging(parser) def stanza(indent, lines): pad = " " * indent return "".join(pad + line + "\n" for line in lines) + "\n" +def superStanza(): + """The container both data sources sit in, on alpha only. + + Nesting a superTrack inside a superTrack needs the fix in #38460, which is on + master but not in a release. These tracks are already released, so the + container and the nested form of its two members are alpha, and a flat + unnested form of those members is beta and public. Drop the release tags + here and in sourceSuperStanza once #38460 ships.""" + return stanza(0, [ + "track transcriptionStart", + "superTrack on show", + "group rna", + "shortLabel Transcription Initiation (TSS)", + "longLabel Transcription initiation (TSS)", + "release alpha", + ]) + def sourceSuperStanza(track, shortLabel, longLabel): - """One top-level superTrack per data source. + """One superTrack per data source, written twice: inside the + transcriptionStart container on alpha, and standing on its own for beta and + public, which run binaries that predate #38460 and would drop the container. - These were briefly nested under a transcriptionStart superTrack, which looks - legal and passes tdbQuery -check -strict but silently loses the outer level: - trackDbSuperMarkup() refuses to give a superTrack a parent, so hgTrackDb never - writes the outer one. See #38460.""" - return stanza(0, [ + hgTrackDb takes the copy whose release matches and rejects two whose releases + overlap, so the two sets of tags have to stay complementary. + + Every child of the container is written before any of their members, because + tdbQuery -strict rejects a file in which another track comes between a + superTrack and one of its children.""" + common = [ f"track {track}", "superTrack on show", "group rna", f"shortLabel {shortLabel}", f"longLabel {longLabel}", - ]) + ] + nested = stanza(4, common[:2] + ["parent transcriptionStart"] + common[2:] + + ["release alpha"]) + flat = stanza(0, common + ["release beta,public"]) + return nested + flat def strandOverlay(db, composite, exp, dataType, subDir, fileTag, shortLabel, longLabel, childLabel, priority, vis): """One cell line's two strands as an overlay, plus strand drawn up and minus strand drawn down. Both sources store the minus strand negated, so neither needs the trackDb negateValues setting. That setting used to be on the ProCapNet minus tracks, and it broke the composite's own negate control: that control sets one shared value for the composite, which replaced the per-track setting and sent both strands the same way, with no route back to the default short of a cart reset. fileTag is the part of the file name that says what the file holds, so a bigWig downloaded on its own still names its source: the model for the predictions, the ENCODE experiment accession for the measurements. Each overlay is a member of the transcriptionStart superTrack rather than a composite child, so it is a track in its own right: hgTrackUi gives it a full configuration page, and hiding it hides the whole overlay instead of leaving an empty row. It therefore carries its own wiggle settings, since there is no composite to inherit them from, and its own html, since a superTrack member does not inherit the container's description page.""" gbdb = GBDB.format(db=db, track=composite) + subDir out = stanza(0, [ f"track {composite}_{exp.cell}_{dataType}", f"superTrack {composite} {vis}", "container multiWig", "aggregate solidOverlay", "showSubtrackColorOnUi on", "type bigWig", "autoScale on", "alwaysZero on", "maxHeightPixels 100:40:8", "windowingFunction maximum", "configurable on", f"html {composite}", f"color {exp.color}", f"shortLabel {shortLabel}", f"longLabel {longLabel}", f"priority {priority}", ]) out += stanza(4, [ f"track {composite}_{exp.cell}_{dataType}_pos", f"parent {composite}_{exp.cell}_{dataType}", "type bigWig", f"bigDataUrl {gbdb}/{exp.cell}.{fileTag}.pos.bw", f"color {exp.color}", f"shortLabel {shortLabel} +", f"longLabel {childLabel}, plus strand", ]) out += stanza(4, [ f"track {composite}_{exp.cell}_{dataType}_neg", f"parent {composite}_{exp.cell}_{dataType}", "type bigWig", f"bigDataUrl {gbdb}/{exp.cell}.{fileTag}.neg.bw", f"color {exp.color}", f"altColor {exp.color}", f"shortLabel {shortLabel} -", f"longLabel {childLabel}, minus strand", ]) return out def contribStanza(db, exp, priority): gbdb = GBDB.format(db=db, track="proCapNet") return stanza(0, [ f"track proCapNet_{exp.cell}_contrib", "superTrack proCapNet hide", "type bigWig", f"bigDataUrl {gbdb}/contrib/{exp.cell}.proCapNet-contrib.bw", "logo on", "autoScale on", "alwaysZero on", "maxHeightPixels 100:40:8", "configurable on", "html proCapNet", f"color {exp.color}", f"shortLabel {exp.cell} Contribution", f"longLabel {exp.cell} ({exp.modelAcc}) ProCapNet sequence-contribution scores", f"priority {priority}", ]) def proCapNetTracks(db, experiments): "the prediction overlays, and on hg38 the contribution scores" out = "" for priority, exp in enumerate(experiments, 1): out += strandOverlay(db, "proCapNet", exp, "pred", "/pred", "proCapNet", f"{exp.cell} Predicted", f"{exp.cell} ({exp.modelAcc}) ProCapNet predicted PRO-cap," f" plus up minus down", f"{exp.cell} ({exp.modelAcc}) ProCapNet predicted PRO-cap", priority, "full") if db == "hg38": for priority, exp in enumerate(experiments, 11): out += contribStanza(db, exp, priority) return out def encode4ProCapTracks(db, experiments): "the measured PRO-cap overlays, ordered ahead of the predictions" out = "" for priority, exp in enumerate(experiments, 1): out += strandOverlay(db, "encode4ProCap", exp, "procap", "", exp.procapAcc, f"{exp.cell} PRO-cap", f"{exp.cell} ({exp.procapAcc}) PRO-cap transcription start" f" sites, plus up minus down", f"{exp.cell} ({exp.procapAcc}) PRO-cap transcription start sites", priority, "full") return out def proCapNetTrackDb(opts, args): experiments = list(TsvReader(args.experimentsTsv)) fileOps.ensureFileDir(args.outRa) with fileOps.AtomicFileOpen(args.outRa) as fh: print(f"# Generated by hg/makeDb/outside/proCapNet/proCapNetTrackDb for {args.db}.", file=fh) print("# Do not edit by hand, edit the script and regenerate.\n", file=fh) + fh.write(superStanza()) if args.db == "hg38": fh.write(sourceSuperStanza( "encode4ProCap", "PRO-cap", "PRO-cap nascent RNA transcription start sites from ENCODE 4")) fh.write(sourceSuperStanza("proCapNet", "ProCapNet", "ProCapNet predicted PRO-cap")) if args.db == "hg38": fh.write(encode4ProCapTracks(args.db, experiments)) fh.write(proCapNetTracks(args.db, experiments)) def main(): opts, args = parseArgs() with cli.ErrorHandler(noStackExcepts=(OSError, cli.PycbioException)): proCapNetTrackDb(opts, args) main()