f72d8b57a77755e64ef594ae2d4395a12c19712d
hiram
  Tue Mar 19 21:46:37 2019 -0700
now showing a sample getData URL for supported tracks refs #18869

diff --git src/hg/hubApi/hubApi.c src/hg/hubApi/hubApi.c
index f9a2b0c..a58944f 100644
--- src/hg/hubApi/hubApi.c
+++ src/hg/hubApi/hubApi.c
@@ -20,33 +20,87 @@
 static struct cart *cart;             /* CGI and other variables */
 static struct hash *oldVars = NULL;
 static struct hash *trackCounter = NULL;
 static long totalTracks = 0;
 static boolean measureTiming = FALSE;	/* set by CGI parameters */
 static boolean allTrackSettings = FALSE;	/* checkbox setting */
 static char **shortLabels = NULL;	/* public hub short labels in array */
 // struct hubPublic *publicHubList = NULL;
 static int publicHubCount = 0;
 static char *defaultHub = "Plants";
 static char *defaultDb = "ce11";
 static long enteredMainTime = 0;	/* will become = clock1000() on entry */
 		/* to allow calculation of when to bail out, taking too long */
 static long timeOutSeconds = 100;
 static boolean timedOut = FALSE;
+static char *urlPrefix = "";	/* initalized to support self references */
+static struct slName *supportedTypes = NULL;
+	/* will be initialized to a known supported set */
+
+static void initSupportedTypes()
+/* initalize the list of supported track types,
+ */
+{
+struct slName *el = newSlName("bed");
+slAddHead(&supportedTypes, el);
+el = newSlName("wig");
+slAddHead(&supportedTypes, el);
+el = newSlName("broadPeak");
+slAddHead(&supportedTypes, el);
+el = newSlName("narrowPeak");
+slAddHead(&supportedTypes, el);
+el = newSlName("bigBed");
+slAddHead(&supportedTypes, el);
+el = newSlName("bigWig");
+slAddHead(&supportedTypes, el);
+el = newSlName("bigNarrowPeak");
+slAddHead(&supportedTypes, el);
+el = newSlName("bigGenePred");
+slAddHead(&supportedTypes, el);
+el = newSlName("bigPsl");
+slAddHead(&supportedTypes, el);
+el = newSlName("bigBarChart");
+slAddHead(&supportedTypes, el);
+el = newSlName("bigInteract");
+slAddHead(&supportedTypes, el);
+el = newSlName("bigMaf");
+slAddHead(&supportedTypes, el);
+el = newSlName("bigChain");
+slAddHead(&supportedTypes, el);
+slNameSort(&supportedTypes);
+}
+
+static boolean isSupportedType(char *type)
+/* is given type in the supportedTypes list ? */
+{
+boolean ret = FALSE;
+if (startsWith("wigMaf", type))
+    return ret;
+struct slName *el;
+for (el = supportedTypes; el; el = el->next)
+    {
+    if (startsWith(el->name, type))
+	{
+	ret = TRUE;
+	break;
+	}
+    }
+return ret;
+}
 
 static int publicHubCmpCase(const void *va, const void *vb)
-/* Compare two slNames, ignore case. */
+/* Compare two shortLabels, ignore case. */
 {
 const struct hubPublic *a = *((struct hubPublic **)va);
 const struct hubPublic *b = *((struct hubPublic **)vb);
 return strcasecmp(a->shortLabel, b->shortLabel);
 }
 
 static void publicHubSortCase(struct hubPublic **pList)
 /* Sort slName list, ignore case. */
 {
 slSort(pList, publicHubCmpCase);
 }
 
 static struct hubPublic *hubPublicLoad(char **row)
 /* Load a hubPublic from row fetched with select * from hubPublic
  * from database.  Dispose of this with hubPublicFree(). */
@@ -152,46 +206,72 @@
     hashIncInt(countTracks, "superTrack child");
     hashIncInt(countTracks, stripType);
     hashIncInt(countTracks, "track count");
     }
 else if (isEmpty(tdb->type))
     hashIncInt(countTracks, "no type specified");
 else
     {
     hashIncInt(countTracks, stripType);
     hashIncInt(countTracks, "track count");
     }
 freeMem(stripType);
 // showCounts(countTracks);
 }
 
-static void showSubTracks(struct trackDb *tdb, struct hash *countTracks)
+static void sampleUrl(char *db, struct trackDb *tdb, char *chrom, long long chromSize)
+/* print out a sample getData URL */
+{
+long long start = chromSize / 4;
+long long end = start + 10000;
+if (end > chromSize)
+    end = chromSize;
+
+if (db)
+    {
+    if (tdb->parent)
+	hPrintf("<li>%s : %s subtrack of parent: %s <a href='%s/getData/track?db=%s&amp;chrom=%s&amp;track=%s&amp;start=%lld&amp;end=%lld' target=_blank>(sample getData)</a></li>\n", tdb->track, tdb->type, tdb->parent->track, urlPrefix, db, chrom, tdb->track, start, end );
+    else
+	hPrintf("<li>%s : %s <a href='%s/getData/track?db=%s&amp;chrom=%s&amp;track=%s&amp;start=%lld&amp;end=%lld' target=_blank>(sample getData)</a></li>\n", tdb->track, tdb->type, urlPrefix, db, chrom, tdb->track, start, end );
+    }
+}
+
+static void showSubTracks(char *db, struct trackDb *tdb, struct hash *countTracks,
+    char *chromName, long long chromSize)
 /* tdb has subtracks, show only subTracks, no details */
 {
 hPrintf("    <li><ul>\n");
 if (tdb->subtracks)
     {
     struct trackDb *tdbEl = NULL;
     for (tdbEl = tdb->subtracks; tdbEl; tdbEl = tdbEl->next)
 	{
         if (tdbIsCompositeView(tdbEl))
 	    hPrintf("<li>%s : %s : composite view of parent: %s</li>\n", tdbEl->track, tdbEl->type, tdbEl->parent->track);
 	else
+	    {
+        if (isSupportedType(tdbEl->type))
+	    {
+            sampleUrl(db, tdbEl, chromName, chromSize);
+//            hPrintf("<li>%s : %s : subtrack of parent: %s (with sample)</li>\n", tdbEl->track, tdbEl->type, tdbEl->parent->track, getData);
+	    }
+	else
             hPrintf("<li>%s : %s : subtrack of parent: %s</li>\n", tdbEl->track, tdbEl->type, tdbEl->parent->track);
+	    }
 	hashCountTrack(tdbEl, countTracks);
         if (tdbEl->subtracks)
-	    showSubTracks(tdbEl, countTracks);
+	    showSubTracks(db, tdbEl, countTracks, chromName, chromSize);
 	}
     }
 hPrintf("    </ul></li>\n");
 }
 
 static void trackSettings(struct trackDb *tdb, struct hash *countTracks)
 /* process the settingsHash for a trackDb, recursive when subtracks */
 {
 hPrintf("    <li><ul>\n");
 // if (tdb->children)  haven't yet seen a track with children ?
 //   hPrintf("    <li>%s: has children</li>\n", tdb->track);
 // else
 //   hPrintf("    <li>%s: NO children</li>\n", tdb->track);
 struct hashEl *hel;
 struct hashCookie hc = hashFirst(tdb->settingsHash);
@@ -302,32 +382,32 @@
         retVal = 1;
         }
 
     }
 errCatchEnd(errCatch);
 if (errCatch->gotError)
     {
     retVal = 1;
     dyStringPrintf(errors, "%s", errCatch->message->string);
     }
 errCatchFree(&errCatch);
 
 return retVal;
 }	/* static int bbiBriefMeasure() */
 
-static void countOneTdb(struct trackDb *tdb, char *bigDataIndex,
-    struct hash *countTracks)
+static void countOneTdb(char *db, struct trackDb *tdb, char *bigDataIndex,
+    struct hash *countTracks, char *chromName, long long chromSize)
 {
 char *bigDataUrl = trackDbSetting(tdb, "bigDataUrl");
 // char *compositeTrack = trackDbSetting(tdb, "compositeTrack");
 boolean compositeContainer = tdbIsComposite(tdb);
 boolean compositeView = tdbIsCompositeView(tdb);
 // char *superTrack = trackDbSetting(tdb, "superTrack");
 boolean superChild = tdbIsSuperTrackChild(tdb);
 boolean depthSearch = cartUsualBoolean(cart, "depthSearch", FALSE);
 hashCountTrack(tdb, countTracks);
 
 if (depthSearch && bigDataUrl)
     {
     long chromCount = 0;
     long itemCount = 0;
     struct dyString *errors = newDyString(1024);
@@ -345,80 +425,89 @@
         else
             hPrintf("    <li>%s : %s : %ld chroms : %ld count</li>\n", tdb->track, tdb->type, chromCount, itemCount);
         }
     }
 else
     {
     if (compositeContainer)
         hPrintf("    <li>%s : %s : composite track container has %d subtracks</li>\n", tdb->track, tdb->type, slCount(tdb->subtracks));
     else if (compositeView)
         hPrintf("    <li>%s : %s : composite view of parent: %s</li>\n", tdb->track, tdb->type, tdb->parent->track);
     else if (superChild)
         hPrintf("    <li>%s : %s : superTrack child of parent: %s</li>\n", tdb->track, tdb->type, tdb->parent->track);
     else if (! depthSearch && bigDataUrl)
         hPrintf("    <li>%s : %s : %s</li>\n", tdb->track, tdb->type, bigDataUrl);
     else
+	{
+        if (isSupportedType(tdb->type))
+            sampleUrl(db, tdb, chromName, chromSize);
+	else
 	    hPrintf("    <li>%s : %s</li>\n", tdb->track, tdb->type);
         }
+    }
 if (allTrackSettings)
     {
     hPrintf("    <li><ul>\n");
     trackSettings(tdb, countTracks); /* show all settings */
     hPrintf("    </ul></li>\n");
     }
 else if (tdb->subtracks)
     {
-    showSubTracks(tdb, countTracks);
+    showSubTracks(db, tdb, countTracks, chromName, chromSize);
     }
 return;
-}	/*	static void countOneTdb(struct trackDb *tdb,
-	 *	    char *bigDataIndex, struct hash *countTracks)
+}	/*	static void countOneTdb(char *db, struct trackDb *tdb,
+	 *	char *bigDataIndex, struct hash *countTracks,
+	 *	char *chromName, long long chromSize)
 	 */
 
 static void hubTrackList(struct trackDb *topTrackDb, struct trackHubGenome *genome)
 /* process the track list in a hub to show all tracks */
 {
 hPrintf("    <li><ul>\n");
 if (topTrackDb)
     {
     struct hash *countTracks = hashNew(0);
     struct trackDb *tdb = NULL;
     for ( tdb = topTrackDb; tdb; tdb = tdb->next )
 	{
 	char *bigDataIndex = NULL;
 	char *relIdxUrl = trackDbSetting(topTrackDb, "bigDataIndex");
 	if (relIdxUrl != NULL)
 	    bigDataIndex = trackHubRelativeUrl(genome->trackDbFile, relIdxUrl);
-	countOneTdb(tdb, bigDataIndex, countTracks);
+	countOneTdb(NULL, tdb, bigDataIndex, countTracks, NULL, 0);
 	if (timeOutReached())
 	    break;
 	}	/*	for ( tdb = topTrackDb; tdb; tdb = tdb->next )	*/
     hPrintf("    <li>%d different track types</li>\n",countTracks->elCount - 1);
     /* add this single genome count to the overall multi-genome counts */
     if (countTracks->elCount)
 	{
         hPrintf("        <li><ol>\n");
 	struct hashEl *hel, *helList = hashElListHash(countTracks);
 	slSort(&helList, hashElCmpIntValDesc);
 	for (hel = helList; hel; hel = hel->next)
 	    {
 	    if (sameOk("track count", hel->name))
 		continue;
             int prevCount = ptToInt(hashFindVal(trackCounter, hel->name));
 	    if (differentStringNullOk("track count", hel->name))
 		totalTracks += ptToInt(hel->val);
 	    hashReplace(trackCounter, hel->name, intToPt(prevCount + ptToInt(hel->val)));
+	    if (isSupportedType(hel->name))
+		hPrintf("        <li>%d - %s - supported</li>\n", ptToInt(hel->val), hel->name);
+	    else
 		hPrintf("        <li>%d - %s</li>\n", ptToInt(hel->val), hel->name);
 	    }
         hPrintf("        </ol></li>\n");
 	}
     }
 else
     hPrintf("    <li>no trackTopDb</li>\n");
 
 hPrintf("    </ul><li>\n");
 }	/*	static struct trackDb *hubTrackList()	*/
 
 static struct trackDb *assemblySettings(struct trackHubGenome *genome)
 /* display all the assembly 'settingsHash' */
 {
 struct trackDb *tdb = obtainTdb(genome, NULL);
@@ -565,85 +654,84 @@
  *  parse that and return a function pointer and the parsed words
  * Returns NULL if not recognized
  */
 {
 char *tmp = cloneString(pathInfo);
 /* skip the first leading slash to simplify chopByChar parsing */
 tmp += 1;
 int wordCount = chopByChar(tmp, '/', words, MAX_PATH_INFO);
 if (wordCount < 1)
     return NULL;
 
 struct hashEl *hel = hashLookup(apiFunctionHash, words[0]);
 return hel;
 }
 
-#ifdef NOT
-static void apiFunctionSwitch(struct hashEl *hel, char *words[MAX_PATH_INFO])
-/* given a pathInfo string: /command/subCommand/etc...
- *  parse that and decide on which function to acll
+static long long largestChrom(char *db, char **nameReturn)
+/* return the length and get the chrom name for the largest chrom
+ * from chromInfo table.  For use is sample getData URLs
  */
 {
-hPrintDisable();	/* turn off all normal HTML output, doing JSON output */
-
-/* the leading slash has been removed from the pathInfo, therefore, the
- * chop will have the first word in words[0]
- */
-#ifdef NOT
-char *words[MAX_PATH_INFO];/*expect no more than MAX_PATH_INFO number of words*/
-int wordCount = chopByChar(pathInfo, '/', words, ArraySize(words));
-if (wordCount < 2)
-    apiErrAbort("unknown endpoint command: '/%s'", pathInfo);
-
-struct hashEl *hel = hashLookup(apiFunctionHash, words[0]);
-if (hel == NULL)
-    apiErrAbort("no such command: '%s' for endpoint '/%s'", words[0], pathInfo);
-#endif
-void (*apiFunction)(char **) = hel->val;
-// void (*apiFunction)(char **) = hashMustFindVal(apiFunctionHash, words[0]);
-
-(*apiFunction)(words);
-
-}	/*	static void apiFunctionSwitch(char *pathInfo)	*/
-#endif
+char query[1024];
+struct sqlConnection *conn = hAllocConn(db);
+// Build a query to select the hubUrl for the given shortLabel
+sqlSafef(query, sizeof(query), "select chrom,size from chromInfo order by size desc limit 1");
+struct sqlResult *sr = sqlGetResult(conn, query);
+char **row = sqlNextRow(sr);
+long long length = 0;
+if (row)
+   {
+   *nameReturn = cloneString(row[0]);
+   length = sqlLongLong(row[1]);
+   }
+sqlFreeResult(&sr);
+hFreeConn(&conn);
+return length;
+}
 
 static void tracksForUcscDb(char *db)
 /* scan the specified database for all tracks */
 {
 struct hash *countTracks = hashNew(0);
-hPrintf("<p>Tracks in UCSC genome: '%s'<br>\n", db);
+char *chromName = NULL;
+long long chromSize = largestChrom(db, &chromName);
+hPrintf("<p>Tracks in UCSC genome: '%s', longest chrom: %s:%lld<br>\n", db, chromName, chromSize);
 struct trackDb *tdbList = obtainTdb(NULL, db);
 struct trackDb *tdb;
 hPrintf("<ul>\n");
+hPrintf("<li>%s:%lld</li>\n", chromName, chromSize);
 for (tdb = tdbList; tdb != NULL; tdb = tdb->next )
     {
-    countOneTdb(tdb, NULL, countTracks);
+    countOneTdb(db, tdb, NULL, countTracks, chromName, chromSize);
     if (timeOutReached())
 	break;
     }
 int trackCount = ptToInt(hashFindVal(countTracks, "track count"));
 /* elCount - 1 since the 'track count' element isn't a track */
 hPrintf("    <li>%d total tracks counted, %d different track types</li>\n", trackCount, countTracks->elCount - 1);
 if (countTracks->elCount)
     {
     hPrintf("        <ol>\n");
     struct hashEl *hel, *helList = hashElListHash(countTracks);
     slSort(&helList, hashElCmpIntValDesc);
     for (hel = helList; hel; hel = hel->next)
 	{
 	if (sameOk("track count", hel->name))
 	    continue;
+	if (isSupportedType(hel->name))
+	    hPrintf("        <li>%d - %s - supported</li>\n", ptToInt(hel->val), hel->name);
+	else
 	    hPrintf("        <li>%d - %s</li>\n", ptToInt(hel->val), hel->name);
 	}
     hPrintf("        </ol>\n");
     }
 hPrintf("</ul>\n");
 hPrintf("</p>\n");
 }	// static void tracksForUcscDb(char * db)
 
 static void introductionText()
 /* output explanation text */
 {
 char *scriptUri = getenv("SCRIPT_URI");
 
 hPrintf("<h1>JSON data API interface to U.C. Santa Cruz genome browser data</h1>\n");
 hPrintf("<h2>Data access URL: <em>%s</em></h2>\n", scriptUri);
@@ -662,44 +750,56 @@
 hPrintf("<li>/getData/sequence - return sequence from specified hub or database genome</li>\n");
 hPrintf("<li>/getData/track - return data from specified track in hub or database genome</li>\n");
 hPrintf("</ol></li>\n");
 hPrintf("</ul>\n");
 hPrintf("<h3>Parameters to endpoint functions:</h3>\n");
 hPrintf("<ul>\n");
 hPrintf("<li>hubUrl=&lt;url&gt; - specify track hub or assembly hub URL</li>\n");
 hPrintf("<li>genome=&lt;name&gt; - specify genome assemby in track or assembly hub</li>\n");
 hPrintf("<li>db=&lt;ucscDb&gt; - specify database (aka genome assembly) in UCSC genome browser</li>\n");
 hPrintf("<li>track=&lt;trackName&gt; - specify data track in hub or UCSC database genome assembly</li>\n");
 hPrintf("<li>chrom=&lt;chrN&gt; - specify chromosome name for sequence or track data</li>\n");
 hPrintf("<li>start=&lt;123&gt; - specify start coordinate (0 relative) for data from track or sequence retrieval</li>\n");
 hPrintf("<li>end=&lt;456&gt; - specify end coordinate (1 relative) for data from track or sequence retrieval</li>\n");
 hPrintf("<li>(see also: <a href='http://genome.ucsc.edu/blog/the-ucsc-genome-browser-coordinate-counting-systems/' target=_blank>UCSC browser coordinate counting systems)</a></li>");
 hPrintf("</ul>\n");
+hPrintf("<h3>Supported track types, at this time, for getData functions:</h3>\n");
+hPrintf("<ul>\n");
+static struct slName *el;
+for (el = supportedTypes; el; el = el->next)
+    {
+    hPrintf("<li>%s</li>\n", el->name);
+    }
+hPrintf("</ul>\n");
 }
 
-static void showExamples(char *url, struct trackHubGenome *hubGenome, char *ucscDb)
+static void initUrlPrefix()
+/* set up urlPrefix for self referenes */
 {
-char *urlPrefix = "";
 char *httpHost = getenv("HTTP_HOST");
 
-if (! startsWith("hgwdev-api", httpHost)) {
+if (! startsWith("hgwdev-api", httpHost))
+    {
     if (startsWith("hgwdev",httpHost) || startsWith("genome-test", httpHost))
 	{
 	urlPrefix = "../cgi-bin/hubApi";
 	}
     }
+}
 
+static void showExamples(char *url, struct trackHubGenome *hubGenome, char *ucscDb)
+{
 hPrintf("<h2>Example URLs to return json data structures:</h2>\n");
 
 hPrintf("<h3>listing functions</h3>\n");
 hPrintf("<ol>\n");
 hPrintf("<li><a href='%s/list/publicHubs' target=_blank>list public hubs</a> <em>%s/list/publicHubs</em></li>\n", urlPrefix, urlPrefix);
 hPrintf("<li><a href='%s/list/ucscGenomes' target=_blank>list database genomes</a> <em>%s/list/ucscGenomes</em></li>\n", urlPrefix, urlPrefix);
 hPrintf("<li><a href='%s/list/hubGenomes?hubUrl=%s' target=_blank>list genomes from specified hub</a> <em>%s/list/hubGenomes?hubUrl=%s</em></li>\n", urlPrefix, url, urlPrefix, url);
 hPrintf("<li><a href='%s/list/tracks?hubUrl=%s&amp;genome=%s' target=_blank>list tracks from specified hub and genome</a> <em>%s/list/tracks?hubUrl=%s&amp;genome=%s</em></li>\n", urlPrefix, url, hubGenome->name, urlPrefix, url, hubGenome->name);
 hPrintf("<li><a href='%s/list/tracks?db=%s' target=_blank>list tracks from specified UCSC database</a> <em>%s/list/tracks?db=%s</em></li>\n", urlPrefix, ucscDb, urlPrefix, ucscDb);
 hPrintf("<li><a href='%s/list/chromosomes?db=%s' target=_blank>list chromosomes from specified UCSC database</a> <em>%s/list/chromosomes?db=%s</em></li>\n", urlPrefix, ucscDb, urlPrefix, ucscDb);
 hPrintf("<li><a href='%s/list/chromosomes?db=%s&amp;track=gap' target=_blank>list chromosomes from specified track from UCSC databaset</a> <em>%s/list/chromosomes?db=%s&amp;track=gap</em></li>\n", urlPrefix, ucscDb, urlPrefix, ucscDb);
 hPrintf("</ol>\n");
 
 hPrintf("<h3>getData functions</h3>\n");
 hPrintf("<ol>\n");
@@ -736,30 +836,32 @@
 }
 
 static void doMiddle(struct cart *theCart)
 /* Set up globals and make web page */
 {
 cart = theCart;
 measureTiming = hPrintStatus() && isNotEmpty(cartOptionalString(cart, "measureTiming"));
 measureTiming = TRUE;
 char *database = NULL;
 char *genome = NULL;
 
 cgiVarSet("ignoreCookie", "1");
 
 getDbAndGenome(cart, &database, &genome, oldVars);
 initGenbankTableNames(database);
+initSupportedTypes();
+initUrlPrefix();
 
 char *docRoot = cfgOptionDefault("browser.documentRoot", DOCUMENT_ROOT);
 
 int timeout = cartUsualInt(cart, "udcTimeout", 300);
 if (udcCacheTimeout() < timeout)
     udcSetCacheTimeout(timeout);
 knetUdcInstall();
 
 char *pathInfo = getenv("PATH_INFO");
 /* nothing on incoming path, then display the WEB page instead */
 if (sameOk("/",pathInfo))
     pathInfo = NULL;
 
 boolean commandError = FALSE;
 /*expect no more than MAX_PATH_INFO number of words*/