21dd365125a7e84eb22f4e1c8db91e76a7ef2063
max
  Mon Sep 21 05:27:57 2026 -0700
hg38 methaDory: link the Genes and loci table's studies to the Studies table

Each locus row lists the studies behind it as plain text, e.g. "ArefEshghi2020,
Bend2019, Breen2020, Levy2022", with no way to jump to what those studies are.
studyTable() now writes an id="study-<key>" anchor on each Studies table row,
and locusTable() turns each name in the loci table's Studies column into a
link to that anchor. Both tables come from the same StudyID set (methaDoryToBed
.py), so every study referenced by a locus has a matching row to land on;
verified against studySummary.tsv/locusSummary.tsv before regenerating.

diff --git src/hg/makeDb/scripts/episignatures/makeHtmlTables.py src/hg/makeDb/scripts/episignatures/makeHtmlTables.py
index 94a2e35b04a..8d3c54c28d7 100644
--- src/hg/makeDb/scripts/episignatures/makeHtmlTables.py
+++ src/hg/makeDb/scripts/episignatures/makeHtmlTables.py
@@ -28,52 +28,70 @@
     for pmid in pmidField.split(","):
         pmid = pmid.strip()
         if not pmid:
             continue
         if pmid not in authorYear:
             raise ValueError("no author/year in pubAuthorYear.tsv for id %s" % pmid)
         if pmid.isdigit():
             out.append('<a href="https://pubmed.ncbi.nlm.nih.gov/%s/" target="_blank">%s</a>'
                        % (pmid, authorYear[pmid]))
         else:
             out.append('<a href="%s" target="_blank">%s</a>'
                        % (html.escape(pmid), authorYear[pmid]))
     return ", ".join(out) if out else "&nbsp;"
 
 
+def studyAnchor(study):
+    """The #fragment a study's row in the Studies table is addressed by, also
+    used as the anchor's id so the Genes and loci table can link to it."""
+    return "study-" + study
+
+
 def studyTable(fname, out):
     authorYear = loadAuthorYear()
     out.write('<table class="stdTbl">\n')
     out.write("<tr><th>Study</th><th>PubMed</th><th>Episignatures</th>"
               "<th>Disorders</th><th>Probes reported</th><th>Distinct probes</th></tr>\n")
     with open(fname) as fh:
         for r in csv.DictReader(fh, delimiter="\t"):
-            out.write("<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td>"
+            out.write('<tr id="%s"><td>%s</td><td>%s</td><td>%s</td><td>%s</td>'
                       "<td>%s</td><td>%s</td></tr>\n"
-                      % (html.escape(r["study"]), pubLinks(r["pmid"], authorYear),
+                      % (html.escape(studyAnchor(r["study"]), quote=True),
+                         html.escape(r["study"]), pubLinks(r["pmid"], authorYear),
                          r["signatures"], html.escape(r["disorders"]) or "&nbsp;",
                          "{:,}".format(int(r["probeRows"])),
                          "{:,}".format(int(r["uniqProbes"]))))
     out.write("</table>\n")
 
 
+def studyLinks(studiesField):
+    out = []
+    for study in studiesField.split(","):
+        study = study.strip()
+        if not study:
+            continue
+        out.append('<a href="#%s">%s</a>'
+                   % (html.escape(studyAnchor(study), quote=True), html.escape(study)))
+    return ", ".join(out) if out else "&nbsp;"
+
+
 def locusTable(fname, out):
     out.write('<table class="stdTbl">\n')
     out.write("<tr><th>Gene or locus</th><th>Disorders</th><th>Episignatures</th>"
               "<th>Studies</th><th>Probes reported</th><th>Distinct probes</th></tr>\n")
     with open(fname) as fh:
         for r in csv.DictReader(fh, delimiter="\t"):
             out.write("<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td>"
                       "<td>%s</td><td>%s</td></tr>\n"
                       % (html.escape(r["locus"]),
                          html.escape(r["disorders"]) or "&nbsp;",
-                         r["signatures"], html.escape(r["studies"]),
+                         r["signatures"], studyLinks(r["studies"]),
                          "{:,}".format(int(r["probeRows"])),
                          "{:,}".format(int(r["uniqProbes"]))))
     out.write("</table>\n")
 
 
 which, fname = sys.argv[1], sys.argv[2]
 if which == "studies":
     studyTable(fname, sys.stdout)
 else:
     locusTable(fname, sys.stdout)