78988553dd9b460c26f0b9f21f15a1aacfad9dab
lrnassar
  Fri Aug 21 15:44:40 2026 -0700
Polish pass on the mouseDevTimecourse tracks after a Playwright QA sweep. refs #37001

Sentence-case the tissue names and the facet column titles, so the barChart
facet filter reads "Tissue / Spleen" rather than "tissue / spleen" and the
bigWig matrix reads "Spleen". Only the first character is upper-cased. Added
sentenceCaseTissues.sh, which does the .facets and .categories files and is
idempotent, since the hub still ships lower-case and this has to be replayed
after any refetch. The count and color column names are deliberately left
lower-case: barChartUi.c requires a field literally named "count" to load the
file at all, and facetedTable.c keys its merge logic on "count", "color" and
"val". Renaming the faceted columns means trackDb matches, so the stanzas now
read barChartFacets Tissue,Timepoint.

Set priority on the container children so the default-visible M21 TPM sorts
first and the signal composite sorts last. The composite needs an explicit
value; without one it inherits the superTrack's 0.6 and floats to the top.

Fix the All reads view, which was inert. Every all-reads subtrack shipped
parent off, so switching the view to full revealed nothing. The view's own
visibility already gates drawing, so the subtrack state should not encode the
view as well. The default image is unchanged at 78 unique-reads rep1 tracks,
and switching the view to full now yields 156. This also makes the Rep 2
toggle symmetric across the two views.

Rename the bigWig subGroup3 display label from Age to Timepoint, matching the
barChart facet and the .facets column. The group name stays "age" because
dimensions and sortOrder reference it by name.

Add relatedTracks cross-links between the mm10 container and Tabula Muris.
Not Tabula Muris Senis, which is not on the RR.

Description pages: reorder the mm10 subtrack list to match the new display
order, "sub tracks" to "subtracks", capitalise the colour legend tissue names,
and correct the mm39 Il11ra2 note - the gene appears three times, two of them
stacked at one position and sharing a details page, with the third 497 kb away.

Makedocs record the casing step, its ordering constraint relative to the
reorder and colour steps, and the count/color naming constraint.

diff --git src/hg/makeDb/scripts/mouseDevTimecourse/generateBigwigTrackDb.py src/hg/makeDb/scripts/mouseDevTimecourse/generateBigwigTrackDb.py
index cee34b025a5..ce1fe21a468 100755
--- src/hg/makeDb/scripts/mouseDevTimecourse/generateBigwigTrackDb.py
+++ src/hg/makeDb/scripts/mouseDevTimecourse/generateBigwigTrackDb.py
@@ -87,30 +87,36 @@
 
 
 def tissue_short(name):
     return TISSUE_SHORT.get(name, name)
 
 
 def tissue_key(name):
     return name.replace(' ', '_')
 
 
 def tissue_tag(name):
     """subGroup tag carrying the biological sort order, e.g. t14_forebrain."""
     return 't%02d_%s' % (TISSUE_ORDER.index(name) + 1, tissue_key(name))
 
 
+def tissue_label(name):
+    """Sentence-cased subGroup label; trackDb renders the underscores as spaces."""
+    key = tissue_key(name)
+    return key[:1].upper() + key[1:]
+
+
 def age_label(life_stage, age):
     if life_stage == 'embryonic':
         return 'e' + str(age)
     if life_stage == 'postnatal':
         if float(age) == 0:
             return 'P0'
         return 'P' + str(age)
     return life_stage + '_' + str(age)
 
 
 def age_key(life_stage, age):
     return age_label(life_stage, age).replace('.', '_')
 
 
 def accession(url):
@@ -120,31 +126,33 @@
 def hex_to_rgb(h):
     h = h.lstrip('#')
     return ','.join(str(int(h[i:i + 2], 16)) for i in (0, 2, 4))
 
 
 def load_colors():
     """Build a (tissue, timepoint_label) -> hex color map from the .facets file."""
     colors = {}
     with open(FACETS) as f:
         f.readline()
         for line in f:
             cols = line.rstrip('\n').split('\t')
             if len(cols) < 5:
                 continue
             tissue, timepoint, hex_color = cols[2], cols[3], cols[4]
-            colors[(tissue, timepoint)] = hex_color
+            # Keyed lower-case: the .facets tissue is sentence-cased for display
+            # but the biosample TSV supplies it lower-case.
+            colors[(tissue.lower(), timepoint)] = hex_color
     return colors
 
 
 def load_replicates():
     """Build a file accession -> ENCODE biological replicate number map."""
     reps = {}
     with open(REPLICATES) as f:
         f.readline()
         for line in f:
             line = line.rstrip('\n')
             if not line:
                 continue
             acc, rep = line.split('\t')
             reps[acc] = int(rep)
     return reps
@@ -166,44 +174,50 @@
             rows.append(dict(zip(header, line.split('\t'))))
 
     unknown = sorted({t for t in (r['biosample_term_name'] for r in rows)
                       if t not in TISSUE_ORDER})
     if unknown:
         sys.exit('tissue missing from TISSUE_ORDER: %s' % ', '.join(unknown))
 
     ages = sorted(
         {(row['mouse_life_stage'], row['age']) for row in rows},
         key=lambda p: (p[0], float(p[1])),
     )
 
     # Composite parent stanza
     print('    track developmentTimecourseSignalMm10')
     print('    parent mouseDevTimecourse')
+    # Sorts after the four bigBarChart siblings, which take 1-4. Without an
+    # explicit value this inherits the superTrack's 0.6 and floats to the top.
+    print('    priority 5')
     print('    compositeTrack on')
     print('    type bigWig')
     print('    shortLabel Timecourse Signal')
     print('    longLabel ENCODE mouse development time course bulk RNA-seq signal')
     print('    visibility hide')
     print('    group regulation')
     print('    html developmentTimecourseSignalMm10')
     print('    subGroup1 view Views unique=Unique_reads all=All_reads')
 
-    tissue_grp = ' '.join(tissue_tag(t) + '=' + tissue_key(t) for t in TISSUE_ORDER)
+    tissue_grp = ' '.join(tissue_tag(t) + '=' + tissue_label(t) for t in TISSUE_ORDER)
     print('    subGroup2 tissue Tissue ' + tissue_grp)
 
     age_grp = ' '.join(age_key(ls, age) + '=' + age_label(ls, age) for ls, age in ages)
-    print('    subGroup3 age Age ' + age_grp)
+    # Display label is Timepoint, matching the bigBarChart facet filter and the
+    # .facets column. The group name stays 'age' because dimensions and sortOrder
+    # reference it by name.
+    print('    subGroup3 age Timepoint ' + age_grp)
 
     print('    subGroup4 rep Replicate rep1=Rep_1 rep2=Rep_2')
     print('    dimensions dimX=age dimY=tissue dimA=rep')
     print('    dimensionAchecked rep1')
     print('    sortOrder view=+ tissue=+ age=+ rep=+')
     print('    dragAndDrop subTracks')
     print('    noInherit on')
     print()
 
     views = (
         ('unique', 'Unique', 'Unique reads', 'signal_of_unique_reads', 'full'),
         ('all', 'All', 'All reads', 'signal_of_all_reads', 'hide'),
     )
 
     for view_key, view_cap, view_label, url_col, view_visibility in views:
@@ -225,41 +239,44 @@
 
             t_tag = tissue_tag(tissue)
             a_key = age_key(ls, age)
             a_lbl = age_label(ls, age)
             t_short = tissue_short(tissue)
 
             url = row[url_col]
             acc = accession(url)
             big_data_url = '/gbdb/mm10/mouseDevTimecourse/' + acc + '.bigWig'
 
             if acc not in replicates:
                 sys.exit('%s has no replicate number in %s; rerun '
                          'fetchReplicateNumbers.py' % (acc, REPLICATES))
             rep_num = replicates[acc]
 
-            hex_color = colors.get((tissue, a_lbl))
+            hex_color = colors.get((tissue.lower(), a_lbl))
             if hex_color is None:
                 sys.exit('no color in %s for (%s, %s)' % (FACETS, tissue, a_lbl))
             rgb = hex_to_rgb(hex_color)
 
             track = 'developmentTimecourseSignalMm10_' + acc
 
-            # Default on for rep1 + unique-reads (78 subtracks visible when the
-            # composite is enabled). rep2 and all-reads remain off; users can
-            # enable them from the trackUi page.
-            parent_state = 'on' if (rep_num == 1 and view_key == 'unique') else 'off'
+            # Check rep1 in both views, and leave rep2 unchecked. Whether the
+            # all-reads subtracks actually draw is controlled by their view's
+            # visibility, which is 'hide'; so the default image is unchanged at 78
+            # unique-reads rep1 tracks. Encoding the view here as well would leave
+            # the All reads view with nothing checked, and switching it to full
+            # would then appear to do nothing.
+            parent_state = 'on' if rep_num == 1 else 'off'
 
             # Title Case on the tissue and the replicate marker; e14.5 / P0 stay
             # as written since they are standard developmental stage notation.
             short = (t_short.capitalize() + ' ' + a_lbl + ' R' + str(rep_num)
                      + ' ' + VIEW_CODE[view_key])
             if len(short) > MAX_SHORT_LABEL:
                 sys.exit('shortLabel %d chars, limit %d: %s'
                          % (len(short), MAX_SHORT_LABEL, short))
             clipped = short[:LEFT_LABEL_WIDTH]
             if clipped in seen_labels and seen_labels[clipped] != short:
                 sys.exit('shortLabels "%s" and "%s" are identical in the first %d '
                          'characters, so hgTracks draws them the same. Shorten the '
                          'tissue abbreviation in TISSUE_SHORT.'
                          % (seen_labels[clipped], short, LEFT_LABEL_WIDTH))
             seen_labels[clipped] = short