09b542fe035a233ec45d2092e4a0ab38e99c3e32
hiram
  Mon Feb 11 09:50:37 2019 -0800
adding dataTime and dataTimeStamp to the outputs refs #18869

diff --git src/hg/hubApi/hubApi.c src/hg/hubApi/hubApi.c
index 2f75c10..d9b8178 100644
--- src/hg/hubApi/hubApi.c
+++ src/hg/hubApi/hubApi.c
@@ -88,31 +88,31 @@
 freeMem(a);
 }
 
 static void jsonTagValue(FILE *f, char *tag, char *value)
 /* output one json string: "tag":"value" appropriately quoted and encoded */
 {
 fprintf(f,"\"%s\":",tag);
 jsonStringPrint(f, value);
 }
 
 static struct jsonWrite *jsonStartOutput()
 /* begin json output */
 {
 struct jsonWrite *jw = jsonWriteNew();
 jsonWriteObjectStart(jw, NULL);
-jsonWriteString(jw, "version", "0.1");
+jsonWriteString(jw, "apiVersion", "0.1");
 jsonWriteString(jw, "source", "UCSantaCruz");
 return jw;
 }
 
 static void jsonErrAbort(char *format, ...)
 /* Issue an error message in json format. */
 {
 char errMsg[2048];
 va_list args;
 va_start(args, format);
 vsnprintf(errMsg, sizeof(errMsg), format, args);
 fputc('{',stdout);
 jsonTagValue(stdout, "error", errMsg);
 fputc('}',stdout);
 }
@@ -478,30 +478,34 @@
     hPrintf("    <li>%ld total tracks counted, %d different track types:</li>\n", totalTracks, trackCounter->elCount);
     hPrintf("    <ul>\n");
     struct hashEl *hel;
     struct hashCookie hc = hashFirst(trackCounter);
     while ((hel = hashNext(&hc)) != NULL)
 	{
 	hPrintf("    <li>%d - %s - total</li>\n", ptToInt(hel->val), hel->name);
 	}
     hPrintf("    </ul>\n");
     }
 hPrintf("</ul>\n");
 return retList;
 }	/*	static struct slName *genomeList ()	*/
 
 static char *urlFromShortLabel(char *shortLabel)
+/* this is not a fair way to get the URL since shortLabel's are not
+ * necessarily unique.  This is temporary.  TBD: need to always use URL
+ * and then get the shortLabel
+ */
 {
 char hubUrl[1024];
 char query[1024];
 struct sqlConnection *conn = hConnectCentral();
 // Build a query to select the hubUrl for the given shortLabel
 sqlSafef(query, sizeof(query), "select hubUrl from %s where shortLabel='%s'",
       hubPublicTableName(), shortLabel);
 if (! sqlQuickQuery(conn, query, hubUrl, sizeof(hubUrl)))
     hubUrl[0] = 0;
 hDisconnectCentral(&conn);
 return cloneString(hubUrl);
 }
 
 static void dbDbJsonData(struct jsonWrite *jw, struct dbDb *el)
 /* Print out dbDb table element in JSON format.
@@ -544,32 +548,39 @@
     {
     jsonErrAbort("ERROR: can not 'desc' table '%s'\n", table);
     return TRUE;
     }
 while ((row = sqlNextRow(sr)) != NULL)
     jsonWriteString(jw, NULL, row[0]);
 sqlFreeResult(&sr);
 hDisconnectCentral(&conn);
 jsonWriteListEnd(jw);
 return FALSE;
 }
 
 static void jsonPublicHubs()
 /* output the hubPublic SQL table */
 {
+struct sqlConnection *conn = hConnectCentral();
+char *dataTime = sqlTableUpdate(conn, hubPublicTableName());
+hDisconnectCentral(&conn);
+time_t dataTimeStamp = sqlDateToUnixTime(dataTime);
 struct hubPublic *el = publicHubList;
 struct jsonWrite *jw = jsonStartOutput();
+jsonWriteString(jw, "dataTime", dataTime);
+jsonWriteNumber(jw, "dataTimeStamp", (long long)dataTimeStamp);
+freeMem(dataTime);
 tableColumns(jw, hubPublicTableName());
 jsonWriteListStart(jw, "publicHubData");
 for ( ; el != NULL; el = el->next )
     {
     hubPublicJsonData(jw, el);
     }
 jsonWriteListEnd(jw);
 jsonWriteObjectEnd(jw);
 fputs(jw->dy->string,stdout);
 }
 
 static int dbDbCmpName(const void *va, const void *vb)
 /* Compare two dbDb elements: name, ignore case. */
 {
 const struct dbDb *a = *((struct dbDb **)va);
@@ -588,136 +599,162 @@
 char **row;
 while ((row = sqlNextRow(sr)) != NULL)
     {
     el = dbDbLoad(row);
     slAddHead(&dbList, el);
     }
 sqlFreeResult(&sr);
 hDisconnectCentral(&conn);
 slSort(&dbList, dbDbCmpName);
 return dbList;
 }
 
 static void jsonDbDb()
 /* output the dbDb SQL table */
 {
+struct sqlConnection *conn = hConnectCentral();
+char *dataTime = sqlTableUpdate(conn, "dbDb");
+hDisconnectCentral(&conn);
+time_t dataTimeStamp = sqlDateToUnixTime(dataTime);
 struct dbDb *dbList = ucscDbDb();
 struct dbDb *el;
 struct jsonWrite *jw = jsonStartOutput();
+jsonWriteString(jw, "dataTime", dataTime);
+jsonWriteNumber(jw, "dataTimeStamp", (long long)dataTimeStamp);
+freeMem(dataTime);
 tableColumns(jw, "dbDb");
 jsonWriteListStart(jw, "ucscGenomes");
 for ( el=dbList; el != NULL; el = el->next )
     {
     dbDbJsonData(jw, el);
     }
 jsonWriteListEnd(jw);
 jsonWriteObjectEnd(jw);
 fputs(jw->dy->string,stdout);
 }
 
 static void chromInfoJsonOutput(FILE *f, char *db)
 /* for given db, if there is a track, list the chromosomes in that track,
  * for no track, simply list the chromosomes in the sequence
  */
 {
-char *track = cgiOptionalString("track");
-if (track)
-    {
+char *table = cgiOptionalString("track");
 struct sqlConnection *conn = hAllocConn(db);
-    if (! sqlTableExists(conn, track))
-	jsonErrAbort("ERROR: endpoint: /list/chromosomes?db=%&table=%s ERROR table does not exist", db, track);
-    if (sqlColumnExists(conn, track, "chrom"))
+/* in trackDb language: track == table */
+if (table)
     {
+    if (! sqlTableExists(conn, table))
+	jsonErrAbort("ERROR: endpoint: /list/chromosomes?db=%&table=%s ERROR table does not exist", db, table);
+    if (sqlColumnExists(conn, table, "chrom"))
+	{
+	char *dataTime = sqlTableUpdate(conn, table);
+	time_t dataTimeStamp = sqlDateToUnixTime(dataTime);
         struct jsonWrite *jw = jsonStartOutput();
 	jsonWriteString(jw, "genome", db);
-	jsonWriteString(jw, "track", track);
+	jsonWriteString(jw, "track", table);
+	jsonWriteString(jw, "dataTime", dataTime);
+	jsonWriteNumber(jw, "dataTimeStamp", (long long)dataTimeStamp);
+	freeMem(dataTime);
         struct slPair *list = NULL;
 	char query[2048];
-        sqlSafef(query, sizeof(query), "select distinct chrom from %s", track);
+        sqlSafef(query, sizeof(query), "select distinct chrom from %s", table);
 	struct sqlResult *sr = sqlGetResult(conn, query);
 	char **row;
 	while ((row = sqlNextRow(sr)) != NULL)
     	{
             int size = hChromSize(db, row[0]);
 	    slAddHead(&list, slPairNew(row[0], intToPt(size)));
     	}
 	sqlFreeResult(&sr);
         slPairIntSort(&list);
         slReverse(&list);
         jsonWriteNumber(jw, "chromCount", (long long)slCount(list));
 	jsonWriteObjectStart(jw, "chromosomes");
         struct slPair *el = list;
         for ( ; el != NULL; el = el->next )
             jsonWriteNumber(jw, el->name, (long long)ptToInt(el->val));
 	jsonWriteObjectEnd(jw);	/* chromosomes */
         jsonWriteObjectEnd(jw);	/* top level */
         fputs(jw->dy->string,stdout);
 	}
     else
 	{
-	jsonErrAbort("ERROR: table '%s' is not a position table, no chromosomes for genome: '%s'", track, db);
+	jsonErrAbort("ERROR: table '%s' is not a position table, no chromosomes for genome: '%s'", table, db);
 	}
-    hFreeConn(&conn);
     }
 else
     {
+    char *dataTime = sqlTableUpdate(conn, "chromInfo");
+    time_t dataTimeStamp = sqlDateToUnixTime(dataTime);
     struct chromInfo *ciList = createChromInfoList(NULL, db);
     struct chromInfo *el = ciList;
     struct jsonWrite *jw = jsonStartOutput();
     jsonWriteString(jw, "genome", db);
+    jsonWriteString(jw, "dataTime", dataTime);
+    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 */
     jsonWriteObjectEnd(jw);	/* top level */
     fputs(jw->dy->string,stdout);
     }
+hFreeConn(&conn);
 }
 
 static void trackDbJsonOutput(char *db, FILE *f)
 /* return track list from specified UCSC database name */
 {
+struct sqlConnection *conn = hAllocConn(db);
+char *dataTime = sqlTableUpdate(conn, "trackDb");
+time_t dataTimeStamp = sqlDateToUnixTime(dataTime);
+hFreeConn(&conn);
 struct trackDb *tdbList = hTrackDb(db);
 struct trackDb *el;
 struct jsonWrite *jw = jsonStartOutput();
 jsonWriteString(jw, "db", db);
+jsonWriteString(jw, "dataTime", dataTime);
+jsonWriteNumber(jw, "dataTimeStamp", (long long)dataTimeStamp);
+freeMem(dataTime);
 jsonWriteListStart(jw, "tracks");
 for (el = tdbList; el != NULL; el = el->next )
     jsonWriteString(jw, NULL, el->track);
 jsonWriteListEnd(jw);
 jsonWriteObjectEnd(jw);
 fputs(jw->dy->string,stdout);
 }	/*	static void trackDbJsonOutput(char *db, FILE *f)	*/
 
 #define MAX_PATH_INFO 32
 static 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 *hubUrl = cgiOptionalString("hubUrl");
     if (isEmpty(hubUrl))
 	jsonErrAbort("ERROR: must supply hubUrl='http:...' some URL to a hub for /list/genomes\n");
 
     struct trackHub *hub = trackHubOpen(hubUrl, "");
+registrationTime
     if (hub->genomeList)
 	{
         struct jsonWrite *jw = jsonStartOutput();
 	jsonWriteString(jw, "hubUrl", hubUrl);
         jsonWriteListStart(jw, "genomes");
 	struct slName *theList = genomeList(hub, NULL, NULL);
 	slNameSort(&theList);
 	struct slName *el = theList;
 	for ( ; el ; el = el->next )
 	    {
 	    jsonWriteString(jw, NULL, el->name);
 	    }
 	jsonWriteListEnd(jw);
 	jsonWriteObjectEnd(jw);
         fputs(jw->dy->string,stdout);
@@ -794,30 +831,31 @@
 static void apiFunctionSwitch(char *pathInfo)
 /* given a pathInfo string: /command/subCommand/etc...
  *  parse that and decide on which function to acll
  */
 {
 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]
  */
 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)
     jsonErrAbort("ERROR: no endpoint commands found ?\n");
 
+/* TBD: need to protect this hash find from 'not found' error */
 void (*apiFunction)(char **) = hashMustFindVal(apiFunctionHash, words[0]);
 
 (*apiFunction)(words);
 
 }	/*	static void apiFunctionSwitch(char *pathInfo)	*/
 
 static void tracksForUcscDb(char * ucscDb)
 {
 hPrintf("<p>Tracks in UCSC genome: '%s'<br>\n", ucscDb);
 struct trackDb *tdbList = hTrackDb(ucscDb);
 struct trackDb *track;
 hPrintf("<ul>\n");
 for (track = tdbList; track != NULL; track = track->next )
     {
     hPrintf("<li>%s</li>\n", track->track);