af113249f30db748bae4c4c79cc270c548eb46d4
hiram
  Tue May 14 14:13:29 2019 -0700
now providing an itemCount for each track in the list tracks function refs #18869

diff --git src/hg/hubApi/list.c src/hg/hubApi/list.c
index 292d80e..bf4b9fb 100644
--- src/hg/hubApi/list.c
+++ src/hg/hubApi/list.c
@@ -321,110 +321,179 @@
 	jsonWriteString(jw, "track", table);
     jsonWriteNumber(jw, "dataTimeStamp", (long long)dataTimeStamp);
     freeMem(dataTime);
     jsonWriteNumber(jw, "chromCount", (long long)slCount(ciList));
     jsonWriteObjectStart(jw, "chromosomes");
     for ( ; el != NULL; el = el->next )
 	{
         jsonWriteNumber(jw, el->chrom, (long long)el->size);
 	}
     jsonWriteObjectEnd(jw);	/* chromosomes */
     apiFinishOutput(0, NULL, jw);
     }
 hFreeConn(&conn);
 }
 
-static void recursiveTrackList(struct jsonWrite *jw, struct trackDb *tdb)
+static long long bbiItemCount(char *bigDataUrl, char *type)
+/* check the bigDataUrl to see what the itemCount is there */
+{
+long long itemCount = 0;
+struct errCatch *errCatch = errCatchNew();
+if (errCatchStart(errCatch))
+    {
+    if (allowedBigBedType(type))
+        {
+        struct bbiFile *bbi = NULL;
+        bbi = bigBedFileOpen(bigDataUrl);
+        itemCount = bigBedItemCount(bbi);
+        bbiFileClose(&bbi);
+        }
+    else if (startsWithWord("bigWig", type))
+        {
+        struct bbiFile *bwf = bigWigFileOpen(bigDataUrl);
+        struct bbiSummaryElement sum = bbiTotalSummary(bwf);
+        itemCount = sum.validCount;
+        bbiFileClose(&bwf);
+        }
+    }
+errCatchEnd(errCatch);
+errCatchFree(&errCatch);
+return itemCount;
+}
+
+static long long dataItemCount(char *db, struct trackDb *tdb)
+/* determine how many items are in this data set */
+{
+boolean isContainer = tdbIsComposite(tdb) || tdbIsCompositeView(tdb);
+if (trackDbSetting(tdb, "container"))
+    isContainer = TRUE;
+long long itemCount = 0;
+if (isContainer)	/* containers have no data items */
+    return itemCount;
+if (sameWord("downloadsOnly", tdb->type))
+    return itemCount;
+
+char *bigDataUrl = trackDbSetting(tdb, "bigDataUrl");
+if (isNotEmpty(bigDataUrl))
+    itemCount = bbiItemCount(bigDataUrl, tdb->type);
+else
+    {
+    /* prepare for getting table row count, find table name */
+    /* the trackDb might have a specific table defined */
+    char *tableName = trackDbSetting(tdb, "table");
+    if (isEmpty(tableName))
+	tableName = trackDbSetting(tdb, "track");
+    if (isNotEmpty(tableName))
+	{
+	struct sqlConnection *conn = hAllocConnMaybe(db);
+	if (conn)
+	    {
+	    char query[2048];
+	   sqlSafef(query, sizeof(query), "select count(*) from %s", tableName);
+	    itemCount = sqlQuickNum(conn, query);
+	    hFreeConn(&conn);
+	    }
+	}
+    }
+return itemCount;
+}
+
+static void recursiveTrackList(struct jsonWrite *jw, struct trackDb *tdb,
+    char *db)
 /* output trackDb tags only for real tracks, not containers,
  * recursive when subtracks exist
  */
 {
 boolean isContainer = tdbIsComposite(tdb) || tdbIsCompositeView(tdb);
 
 /* do *NOT* print containers when 'trackLeavesOnly' requested */
 if (! (trackLeavesOnly && isContainer) )
     {
+    boolean protectedData = FALSE;
+    long long itemCount = 0;
+    if (trackDbSetting(tdb, "tableBrowser"))
+	protectedData = TRUE;
+    else
+	itemCount = dataItemCount(db, tdb);
     jsonWriteObjectStart(jw, tdb->track);
     if (tdbIsComposite(tdb))
         jsonWriteString(jw, "compositeContainer", "TRUE");
     if (tdbIsCompositeView(tdb))
         jsonWriteString(jw, "compositeViewContainer", "TRUE");
     jsonWriteString(jw, "shortLabel", tdb->shortLabel);
     jsonWriteString(jw, "type", tdb->type);
     jsonWriteString(jw, "longLabel", tdb->longLabel);
+    jsonWriteNumber(jw, "itemCount", itemCount);
     if (tdb->parent)
         jsonWriteString(jw, "parent", tdb->parent->track);
     if (tdb->settingsHash)
         {
-	boolean protectedData = FALSE;
-	if (trackDbSetting(tdb, "tableBrowser"))
-	    protectedData = TRUE;
         struct hashEl *hel;
         struct hashCookie hc = hashFirst(tdb->settingsHash);
         while ((hel = hashNext(&hc)) != NULL)
             {
             if (sameWord("track", hel->name))
 		continue;	// already output in header
             if (sameWord("tableBrowser", hel->name))
 		jsonWriteBoolean(jw, "protectedData", TRUE);
             else if (isEmpty((char *)hel->val))
 		jsonWriteString(jw, hel->name, "empty");
             else if (protectedData && sameWord(hel->name, "bigDataUrl"))
 		jsonWriteString(jw, hel->name, "protectedData");
 	    else
 		jsonWriteString(jw, hel->name, (char *)hel->val);
             }
         }
 
     if (tdb->subtracks)
 	{
 	struct trackDb *el = NULL;
 	for (el = tdb->subtracks; el != NULL; el = el->next )
-	    recursiveTrackList(jw, el);
+	    recursiveTrackList(jw, el, db);
 	}
 
     jsonWriteObjectEnd(jw);
     }
 else if (tdb->subtracks)
     {
     struct trackDb *el = NULL;
     for (el = tdb->subtracks; el != NULL; el = el->next )
-	recursiveTrackList(jw, el);
+	recursiveTrackList(jw, el, db);
     }
 }	/*	static void recursiveTrackList()	*/
 
 static void trackDbJsonOutput(char *db, FILE *f)
 /* return track list from specified UCSC database name */
 {
 struct sqlConnection *conn = hAllocConnMaybe(db);
 if (NULL == conn)
     apiErrAbort(err400, err400Msg, "can not find 'genome=%s' for endpoint '/list/tracks", db);
 
 char *dataTime = sqlTableUpdate(conn, "trackDb");
 time_t dataTimeStamp = sqlDateToUnixTime(dataTime);
 replaceChar(dataTime, ' ', 'T');	/* ISO 8601 */
 hFreeConn(&conn);
 struct trackDb *tdbList = obtainTdb(NULL, db);
 struct jsonWrite *jw = apiStartOutput();
 jsonWriteString(jw, "genome", db);
 jsonWriteString(jw, "dataTime", dataTime);
 jsonWriteNumber(jw, "dataTimeStamp", (long long)dataTimeStamp);
 freeMem(dataTime);
 struct trackDb *el = NULL;
 for (el = tdbList; el != NULL; el = el->next )
     {
-    recursiveTrackList(jw, el);
+    recursiveTrackList(jw, el, db);
     }
 apiFinishOutput(0, NULL, jw);
 }	/*	static void trackDbJsonOutput(char *db, FILE *f)	*/
 
 void apiList(char *words[MAX_PATH_INFO])
 /* 'list' function words[1] is the subCommand */
 {
 if (sameWord("publicHubs", words[1]))
     jsonPublicHubs();
 else if (sameWord("ucscGenomes", words[1]))
     jsonDbDb();
 else if (sameWord("hubGenomes", words[1]))
     {
     char *extraArgs = verifyLegalArgs(argsListHubGenomes); /* only one allowed */
     if (extraArgs)
@@ -487,31 +556,31 @@
         if (isEmpty(genome))
 	    apiErrAbort(err400, err400Msg, "must supply genome='someName' the name of a genome in a hub for /list/tracks\n");
 	if (isEmpty(hubUrl))
             apiErrAbort(err400, err400Msg, "must supply hubUrl='http:...' some URL to a hub for /list/tracks");
 	}
     struct trackHub *hub = errCatchTrackHubOpen(hubUrl);
     struct trackHubGenome *hubGenome = findHubGenome(hub, genome,
 	"/list/tracks", hubUrl);
     struct trackDb *tdbList = obtainTdb(hubGenome, NULL);
     struct jsonWrite *jw = apiStartOutput();
     jsonWriteString(jw, "hubUrl", hubUrl);
     jsonWriteObjectStart(jw, hubGenome->name);
     struct trackDb *el = NULL;
     for (el = tdbList; el != NULL; el = el->next )
 	    {
-	    recursiveTrackList(jw, el);
+	    recursiveTrackList(jw, el, db);
 	    }
     jsonWriteObjectEnd(jw);
     apiFinishOutput(0, NULL, jw);
     }
 else if (sameWord("chromosomes", words[1]))
     {
     char *extraArgs = verifyLegalArgs(argsListChromosomes);
     if (extraArgs)
 	apiErrAbort(err400, err400Msg, "extraneous arguments found for function /list/chromosomes '%s'", extraArgs);
 
     char *hubUrl = cgiOptionalString("hubUrl");
     char *genome = cgiOptionalString("genome");
     char *db = cgiOptionalString("genome");
     if (isEmpty(hubUrl) && isNotEmpty(db))
 	{