bf62c01cee8dc176445aa131e2fd6ea847a4eb68
lrnassar
  Thu May 28 11:32:52 2026 -0700
Adding UMD TP53 variant track (umdTp53) on hg19 and hg38 under a new Locus-Specific superTrack. refs #37648

Shows variants from the UMD TP53 database (p53.fr, Soussi & Leroy) as a
bigBed 9+, one row per unique TP53 variant. Coloured by curator pathogenicity
classification. Filters: pathogenicity, variantClassification, variantType,
tumor frequency. Search by HGVS cDNA name and canonical protein change via
extraIndex + trix. Linkouts to COSMIC and dbSNP.

Otto pipeline polls p53.fr weekly; rebuilds only when the upstream zips
actually change. Cron-silent on no-op so unchanged weeks don't email.

Adds src/hg/utils/otto/umdTp53/ (build scripts + parser + .as schema),
src/hg/makeDb/trackDb/human/umdTp53.ra (track stanza),
src/hg/makeDb/trackDb/human/umdTp53.html (description page),
src/hg/makeDb/trackDb/human/locusSpec.ra (shared superTrack definition).

Includes locusSpec.ra and umdTp53.ra from human/trackDb.ra (alpha-gated).
Adds include of ../locusSpec.ra to human/hs1/trackDb.ra so the strict
trackDb checker resolves the parent in the same include chain as the
existing mucins children. Drops the redundant locusSpec stanza from
human/mucins.ra now that it lives in its own file.

diff --git src/hg/utils/otto/umdTp53/download.sh src/hg/utils/otto/umdTp53/download.sh
new file mode 100644
index 00000000000..4967858c1da
--- /dev/null
+++ src/hg/utils/otto/umdTp53/download.sh
@@ -0,0 +1,88 @@
+#!/bin/bash
+#
+# Check p53.fr for a new release of the UMD TP53 database; download the
+# variants + mutations TSV zips into a dated working dir if anything changed,
+# and unzip them. Silent on no-op so the cron entry point can stay quiet.
+#
+# Exits 0 with no output if nothing changed.
+# Exits 0 and prints the dated workdir to stdout if a new build is needed.
+# Exits non-zero on any download/extraction failure.
+#
+# Do not modify this script in /hive/data/outside/otto/umdTp53; edit the kent
+# source at src/hg/utils/otto/umdTp53/download.sh and `make install`.
+
+set -o errexit -o pipefail -o nounset
+umask 002
+
+WORKDIR="/hive/data/outside/otto/umdTp53"
+STATEDIR="${WORKDIR}/state"
+mkdir -p "${STATEDIR}"
+
+VARIANTS_URL="https://p53.fr/images/Database/UMD_variants_US.tsv.zip"
+MUTATIONS_URL="https://p53.fr/images/Database/UMD_mutations_US.tsv.zip"
+
+# Returns the server's Last-Modified header for the URL, or empty on failure.
+get_last_modified() {
+    curl -sIL --fail "$1" 2>/dev/null | tr -d '\r' | awk '/^[Ll]ast-[Mm]odified:/ { sub(/^[Ll]ast-[Mm]odified: */, ""); print; exit }'
+}
+
+variants_lm_new=$(get_last_modified "${VARIANTS_URL}" || true)
+mutations_lm_new=$(get_last_modified "${MUTATIONS_URL}" || true)
+
+variants_lm_old=""
+mutations_lm_old=""
+[ -f "${STATEDIR}/variants.last_modified" ] && variants_lm_old=$(cat "${STATEDIR}/variants.last_modified")
+[ -f "${STATEDIR}/mutations.last_modified" ] && mutations_lm_old=$(cat "${STATEDIR}/mutations.last_modified")
+
+# Fast path: if both Last-Modified strings match what we saw last time, exit silently.
+if [ -n "${variants_lm_new}" ] && [ -n "${mutations_lm_new}" ] \
+   && [ "${variants_lm_new}" = "${variants_lm_old}" ] \
+   && [ "${mutations_lm_new}" = "${mutations_lm_old}" ]; then
+    exit 0
+fi
+
+# Otherwise download and content-hash before deciding to rebuild.
+today=$(date +%F)
+DLDIR="${WORKDIR}/${today}"
+mkdir -p "${DLDIR}"
+cd "${DLDIR}"
+
+# --silent suppresses progress; --fail makes curl exit non-zero on 4xx/5xx.
+curl --silent --fail --output UMD_variants_US.tsv.zip "${VARIANTS_URL}" 1>&2
+curl --silent --fail --output UMD_mutations_US.tsv.zip "${MUTATIONS_URL}" 1>&2
+
+unzip -oq UMD_variants_US.tsv.zip 1>&2
+unzip -oq UMD_mutations_US.tsv.zip 1>&2
+
+# Strip macOS resource-fork artifacts that ship inside the zips
+rm -f ._UMD_*.tsv
+
+# Compute MD5 of the unzipped TSVs; if both match the stored hashes, nothing
+# actually changed (server-side metadata noise only).
+variants_md5_new=$(md5sum UMD_variants_US.tsv | awk '{print $1}')
+mutations_md5_new=$(md5sum UMD_mutations_US.tsv | awk '{print $1}')
+
+variants_md5_old=""
+mutations_md5_old=""
+[ -f "${STATEDIR}/variants.md5" ] && variants_md5_old=$(cat "${STATEDIR}/variants.md5")
+[ -f "${STATEDIR}/mutations.md5" ] && mutations_md5_old=$(cat "${STATEDIR}/mutations.md5")
+
+if [ "${variants_md5_new}" = "${variants_md5_old}" ] \
+   && [ "${mutations_md5_new}" = "${mutations_md5_old}" ]; then
+    # Update Last-Modified stamps so we don't redownload on every cron run, then exit silently.
+    [ -n "${variants_lm_new}" ] && echo "${variants_lm_new}" > "${STATEDIR}/variants.last_modified"
+    [ -n "${mutations_lm_new}" ] && echo "${mutations_lm_new}" > "${STATEDIR}/mutations.last_modified"
+    # The dated workdir is now redundant; remove it so the dir doesn't accumulate dupes.
+    cd "${WORKDIR}"
+    rm -rf "${DLDIR}"
+    exit 0
+fi
+
+# Real content change. Persist new state stamps and report the workdir for downstream steps.
+echo "${variants_md5_new}" > "${STATEDIR}/variants.md5"
+echo "${mutations_md5_new}" > "${STATEDIR}/mutations.md5"
+[ -n "${variants_lm_new}" ] && echo "${variants_lm_new}" > "${STATEDIR}/variants.last_modified"
+[ -n "${mutations_lm_new}" ] && echo "${mutations_lm_new}" > "${STATEDIR}/mutations.last_modified"
+
+# Stdout = the workdir path. The caller redirects this into a marker file.
+echo "${DLDIR}"