b508259e8ce75a2aa1f919b8152befea99a2e1df
chmalee
  Fri Jan 27 10:31:59 2023 -0800
Prototype download track data button. An hg.conf controlled button that
allows users to download all of the track data in the current window
without leaving hgTracks, refs #30024

diff --git src/hg/hubApi/getData.c src/hg/hubApi/getData.c
index 14fce17..f0ed181 100644
--- src/hg/hubApi/getData.c
+++ src/hg/hubApi/getData.c
@@ -432,65 +432,73 @@
 	if (itemsDone >= maxItemsOutput)
 	    reachedMaxItems = TRUE;
     }
     else
 	itemsDone += wigDataOutput(jw, bwf, chrom, start, end);
 
 itemsReturned += itemsDone;
 }
 
 static void getHubTrackData(char *hubUrl)
 /* return data from a hub track, optionally just one chrom data,
  *  optionally just one section of that chrom data
  */
 {
 char *genome = cgiOptionalString("genome");
-char *track = cgiOptionalString("track");
+char *trackArg = cgiOptionalString("track");
 char *chrom = cgiOptionalString("chrom");
 char *start = cgiOptionalString("start");
 char *end = cgiOptionalString("end");
 
 if (isEmpty(genome))
     apiErrAbort(err400, err400Msg, "missing genome=<name> for endpoint '/getData/track'  given hubUrl='%s'", hubUrl);
-if (isEmpty(track))
+if (isEmpty(trackArg))
     apiErrAbort(err400, err400Msg, "missing track=<name> for endpoint '/getData/track'  given hubUrl='%s'", hubUrl);
 
 struct trackHub *hub = errCatchTrackHubOpen(hubUrl);
 struct trackHubGenome *hubGenome = findHubGenome(hub, genome, "/getData/track",
   hubUrl);
 
 struct trackDb *tdb = obtainTdb(hubGenome, NULL);
 
 if (NULL == tdb)
     apiErrAbort(err400, err400Msg, "failed to find a track hub definition in genome=%s for endpoint '/getData/track'  given hubUrl='%s'", genome, hubUrl);
 
+struct jsonWrite *jw = apiStartOutput();
+jsonWriteString(jw, "hubUrl", hubUrl);
+jsonWriteString(jw, "genome", genome);
+
+// allow optional comma sep list of tracks
+char *tracks[100];
+int numTracks = chopByChar(trackArg, ',', tracks, sizeof(tracks));
+int i = 0;
+for (i = 0; i < numTracks; i++)
+    {
+    char *track = cloneString(tracks[i]);
     struct trackDb *thisTrack = findTrackDb(track, tdb);
     if (NULL == thisTrack)
         apiErrAbort(err400, err400Msg, "failed to find specified track=%s in genome=%s for endpoint '/getData/track'  given hubUrl='%s'", track, genome, hubUrl);
     if (trackHasNoData(thisTrack))
         apiErrAbort(err400, err400Msg, "container track '%s' does not contain data, use the children of this container for data access", track);
     if (! isSupportedType(thisTrack->type))
         apiErrAbort(err415, err415Msg, "track type '%s' for track=%s not supported at this time", thisTrack->type, track);
 
     char *bigDataUrl = trackDbSetting(thisTrack, "bigDataUrl");
     struct bbiFile *bbi = bigFileOpen(thisTrack->type, bigDataUrl);
     if (NULL == bbi)
         apiErrAbort(err400, err400Msg, "track type %s management not implemented yet TBD track=%s in genome=%s for endpoint '/getData/track'  given hubUrl='%s'", thisTrack->type, track, genome, hubUrl);
 
-struct jsonWrite *jw = apiStartOutput();
-jsonWriteString(jw, "hubUrl", hubUrl);
-jsonWriteString(jw, "genome", genome);
     unsigned chromSize = 0;
     struct bbiChromInfo *chromList = NULL;
     if (isNotEmpty(chrom))
         {
         chromSize = bbiChromSize(bbi, chrom);
         if (0 == chromSize)
         apiErrAbort(err400, err400Msg, "can not find specified chrom=%s in bigBed file URL '%s', track=%s genome=%s for endpoint '/getData/track' given hubUrl='%s'", chrom, bigDataUrl, track, genome, hubUrl);
         jsonWriteNumber(jw, "chromSize", (long long)chromSize);
         }
     else
         {
         chromList = bbiChromList(bbi);
         jsonWriteNumber(jw, "chromCount", (long long)slCount(chromList));
         }
 
@@ -532,71 +540,73 @@
         else
         itemsDone += bbiDataOutput(jw, bbi, chrom, uStart, uEnd, fiList,
             thisTrack, itemsDone);
         itemsReturned += itemsDone;
         jsonWriteListEnd(jw);
         }
     else if (startsWith("bigWig", thisTrack->type))
         {
         if (jsonOutputArrays || debug)
         wigColumnTypes(jw);
         jsonWriteObjectStart(jw, track);
         bigWigData(jw, bbi, chrom, uStart, uEnd);
         jsonWriteObjectEnd(jw);
         }
     bbiFileClose(&bbi);
+    }
 apiFinishOutput(0, NULL, jw);
 }	/*	static void getHubTrackData(char *hubUrl)	*/
 
 static void getTrackData()
 /* return data from a track, optionally just one chrom data,
  *  optionally just one section of that chrom data
  */
 {
 char *db = cgiOptionalString("genome");
 char *chrom = cgiOptionalString("chrom");
 char *start = cgiOptionalString("start");
 char *end = cgiOptionalString("end");
 /* 'track' name in trackDb often refers to a SQL 'table' */
 char *trackArg = cgiOptionalString("track");
-char *sqlTable = cloneString(trackArg); /* might be something else */
+//char *sqlTable = cloneString(trackArg); /* might be something else */
      /* depends upon 'table' setting in track db, or split table business */
 
 unsigned chromSize = 0;	/* maybe set later */
 unsigned uStart = 0;
 unsigned uEnd = chromSize;	/* maybe set later */
 if ( ! (isEmpty(start) || isEmpty(end)) )
     {
     uStart = sqlUnsigned(start);
     uEnd = sqlUnsigned(end);
     if (uEnd < uStart)
 	apiErrAbort(err400, err400Msg, "given start coordinate %u is greater than given end coordinate", uStart, uEnd);
     }
 
 if (isEmpty(db))
     apiErrAbort(err400, err400Msg, "missing URL variable genome=<ucscDb> name for endpoint '/getData/track");
 if (isEmpty(trackArg))
     apiErrAbort(err400, err400Msg, "missing URL variable track=<trackName> name for endpoint '/getData/track");
 
 /* database existence has already been checked before now, might
  * have disappeared in the mean time (well, not really . . .)
  */
 struct sqlConnection *conn = hAllocConnMaybe(db);
 if (NULL == conn)
     apiErrAbort(err400, err400Msg, "can not find genome 'genome=%s' for endpoint '/getData/track", db);
 
 struct jsonWrite *jw = apiStartOutput();
+jsonWriteString(jw, "genome", db);
 
 // load the tracks
 struct trackDb *tdbList = NULL;
 cartTrackDbInitForApi(NULL, db, &tdbList, NULL, FALSE);
 
 // allow optional comma sep list of tracks
 char *tracks[100];
 int numTracks = chopByChar(trackArg, ',', tracks, sizeof(tracks));
 int i = 0;
 for (i = 0; i < numTracks; i++)
     {
     char *track = cloneString(tracks[i]);
     char *sqlTable = cloneString(track);
     struct trackDb *thisTrack = tdbForTrack(db, track, &tdbList);
 
@@ -652,31 +662,30 @@
             char *defaultChrom = hDefaultChrom(db);
             char fullTableName[256];
             safef(fullTableName, sizeof(fullTableName), "%s_%s", defaultChrom, hti->rootName);
             splitSqlTable = cloneString(fullTableName);
             }
         }
 
     if (! hTableOrSplitExists(db, sqlTable))
         {
         if (! bigDataUrl)
             apiErrAbort(err400, err400Msg, "can not find specified 'track=%s' for endpoint: /getData/track?genome=%s;track=%s", track, db, track);
         else
             tableTrack = FALSE;
         }
 
-    jsonWriteString(jw, "genome", db);
     if (tableTrack)
         {
         char *dataTime = NULL;
         if (hti && hti->isSplit)
             dataTime = sqlTableUpdate(conn, splitSqlTable);
         else
             dataTime = sqlTableUpdate(conn, sqlTable);
         time_t dataTimeStamp = sqlDateToUnixTime(dataTime);
         replaceChar(dataTime, ' ', 'T');	/*	ISO 8601	*/
         jsonWriteString(jw, "dataTime", dataTime);
         jsonWriteNumber(jw, "dataTimeStamp", (long long)dataTimeStamp);
         if (differentStringNullOk(sqlTable,track))
             jsonWriteString(jw, "sqlTable", sqlTable);
         }
     if (thisTrack)