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) &&