0df5f6317fd6e0033a31aaf56b928f574823075c
chmalee
  Wed Jun 3 11:06:54 2026 -0700
Recommended track sets now can load from a file in htdocs/data/recTrackSets/db/sessionName. The settings are overlayed onto the current cart and no reload of the page is required anymore, refs #37281

diff --git src/hg/hgTracks/recTrackSets.c src/hg/hgTracks/recTrackSets.c
index 34abb126725..77d09680dc3 100644
--- src/hg/hgTracks/recTrackSets.c
+++ src/hg/hgTracks/recTrackSets.c
@@ -4,52 +4,55 @@
 /* Copyright (C) 2020 The Regents of the University of California 
  * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */
 
 #include "common.h"
 #include "dystring.h"
 #include "hCommon.h"
 #include "hgConfig.h"
 #include "htmshell.h"
 #include "hash.h"
 #include "web.h"
 #include "ra.h"
 #include "hgTracks.h"
 #include "hgFind.h"
 #include "obscure.h"
 #include "net.h"
+#include "cheapcgi.h"
+#include "cart.h"
 
 
 /* Recommended track sets are Special 'curated' sessions, created by browser team, e.g. for clinical users
  *      This is expected to be a very limited number (under 10 ?)
  *      The list references sessions in namedSessionDb table, by userName and sessionName
  *      (unfortunately not required to be unique, so depending on curator to 
  *      just make one (code willl pick the first one)
  */      
 struct recTrackSet
     {
     struct recTrackSet *next;
     char *label;        // short label for display on browser and dialogs
     char *userName;     // field in named sessions table
     char *sessionName;  // field in named sessions table (CGI encoded)
     char *description;  // descriptive phrase or sentence.  Display uses this
                                 // instead of description in session settings to allow
                                 // updating by other than session author (e.g. QA)
     };
 
 #define REC_TRACK_SETS_FILE  "recTrackSets"
 #define REC_TRACK_SETS_DIR  "inc"
 #define REC_TRACK_SETS_EXT  "tab"
+#define REC_TRACK_SETS_DATA_DIR  "data/recTrackSets"
 
 char *recTrackSetsFile()
 /* Generate path to file specifying menu of recommended track sets.
  *      eg, DOCUMENT_ROOT/inc/recTrackSets.hg19.tab */
 {
 char *root = hDocumentRoot();
 char buf[200];
 safef(buf, sizeof buf, "%s/%s/%s.%s.%s", 
         root, REC_TRACK_SETS_DIR, REC_TRACK_SETS_FILE, database, REC_TRACK_SETS_EXT);
 return cloneString(buf);
 }
 
 boolean recTrackSetsEnabled()
 /* Return TRUE if feature is available */
 {
@@ -84,30 +87,78 @@
     recTrackSet->sessionName = cloneString(row[2]);
     recTrackSet->description = cloneString(row[3]);
     slAddHead(&recTrackSets, recTrackSet);
     }
 slReverse(&recTrackSets);
 lineFileClose(&lf);
 return recTrackSets;
 }
 
 int recTrackSetsForDb()
 /* Return number of recommended track sets for this database */
 {
 return slCount(loadRecTrackSets());
 }
 
+static char *recTrackSetContentsFile(char *sessionName)
+/* Generate path to the htdocs file holding cart settings for one recommended track set,
+ *      eg DOCUMENT_ROOT/data/recTrackSets/hg38/Non_Coding_SNVs_hg38.  The file name is
+ *      the cgi-encoded session name, matching the sessionName column of the .tab file. */
+{
+char *encName = cgiEncodeFull(sessionName);
+char buf[1024];
+safef(buf, sizeof buf, "%s/%s/%s/%s",
+        hDocumentRoot(), REC_TRACK_SETS_DATA_DIR, database, encName);
+freeMem(encName);
+return cloneString(buf);
+}
+
+boolean loadRecTrackSetFromFile(struct cart *cart, char *sessionName)
+/* If a contents file exists in htdocs for this recommended track set, merge its
+ * settings into the current cart and return TRUE.  Return FALSE if no file, so the
+ * caller can fall back to loading the session from hgcentral. */
+{
+char *file = recTrackSetContentsFile(sessionName);
+if (!fileExists(file))
+    {
+    freeMem(file);
+    return FALSE;
+    }
+struct lineFile *lf = lineFileOpen(file, TRUE);
+struct dyString *dy = dyStringNew(0);
+char *line;
+while (lineFileNext(lf, &line, NULL))
+    {
+    line = skipLeadingSpaces(line);
+    if (isEmpty(line) || line[0] == '#')
+        continue;
+    if (strchr(line, '=') == NULL)
+        errAbort("Malformed recommended track set file %s, line %d has no '=': %s",
+                file, lf->lineIx, line);
+    if (dy->stringSize > 0)
+        dyStringAppendC(dy, '&');
+    dyStringAppend(dy, line);
+    }
+lineFileClose(&lf);
+// Same merge as a named session: existing cart values win except for hidden tracks,
+// so the set's visible tracks apply on top of the caller's hide-all.
+cartParseOverHashExt(cart, dy->string, TRUE);
+dyStringFree(&dy);
+freeMem(file);
+return TRUE;
+}
+
 boolean hasRecTrackSet(struct cart *cart)
 /* Check if currently loaded session is in the recommended track set */
 {
 if (!recTrackSetsEnabled())
     return FALSE;
 struct recTrackSet *ts, *recTrackSets = loadRecTrackSets();
 if (!recTrackSets)
     return FALSE;
 char *session = cartOptionalString(cart, hgsOtherUserSessionName);
 char *user = cartOptionalString(cart, hgsOtherUserName);
 if (!session || !user)
     return FALSE;
 for (ts = recTrackSets; ts; ts = ts->next)
     {
     if (sameString(replaceChars(ts->sessionName, "%20", " "), session) &&