760d941586a240c1b4c4e1143f1fd2a3f8ff96c1
braney
  Fri Apr 7 11:26:45 2017 -0700
add the ability to specify a fielded tab-sep file for metadata in a
track hub

diff --git src/hg/lib/hui.c src/hg/lib/hui.c
index 3a146dd..3bc4444 100644
--- src/hg/lib/hui.c
+++ src/hg/lib/hui.c
@@ -35,30 +35,31 @@
 #include "snakeUi.h"
 #include "vcfUi.h"
 #include "vcf.h"
 #include "errCatch.h"
 #include "samAlignment.h"
 #include "makeItemsItem.h"
 #include "bedDetail.h"
 #include "pgSnp.h"
 #include "memgfx.h"
 #include "trackHub.h"
 #include "gtexUi.h"
 #include "genbank.h"
 #include "htmlPage.h"
 #include "longRange.h"
 #include "tagRepo.h"
+#include "fieldedTable.h"
 
 #define SMALLBUF 256
 #define MAX_SUBGROUP 9
 #define ADD_BUTTON_LABEL        "add"
 #define CLEAR_BUTTON_LABEL      "clear"
 #define JBUFSIZE 2048
 
 
 #define DEF_BUTTON "<IMG id=\"btn_%s\" src=\"../images/%s\" alt=\"%s\">\n"
 #define DEF_BUTTON_JS "setCheckBoxesThatContain('%s',true,false,'%s','','%s');" \
 	       "setCheckBoxesThatContain('%s',false,false,'%s','_defOff','%s');" 
 #define DEFAULT_BUTTON(nameOrId,anc,beg,contains) \
     printf(DEF_BUTTON,(anc),"defaults_sm.png","default"); \
     safef(id, sizeof id, "btn_%s", (anc)); \
     jsOnEventByIdF("click", id, DEF_BUTTON_JS,(nameOrId),(beg),(contains),(nameOrId),(beg),(contains)); 
@@ -180,30 +181,80 @@
     {
     char *encFile = cgiEncode(file);
     dyLink = dyStringCreate(VOCAB_LINK_WITH_FILE,encFile,encTerm,encValue,title,label);
     freeMem(encFile);
     }
 else
     dyLink = dyStringCreate(VOCAB_LINK,encTerm,encValue,title,label);
 if (suffix != NULL)
     dyStringAppend(dyLink,suffix);  // Don't encode since this may contain HTML
 
 freeMem(encTerm);
 freeMem(encValue);
 return dyStringCannibalize(&dyLink);
 }
 
+char *tabSepMetaAsHtmlTable(char *tabSepMeta, struct trackDb *tdb,boolean showLongLabel,boolean showShortLabel)
+/* Return a string which is an HTML table of the tags for this track which are stored as a fielded table. */
+{
+// If there's no file, there's no data.
+if (tabSepMeta == NULL)
+    return "";
+
+// If the trackDb entry doesn't have a foreign key, there's no data.
+char *metaTag = trackDbSetting(tdb, "meta");
+if (metaTag == NULL)
+    return "";
+
+static char *cachedTableName = NULL;
+static struct hash *cachedHash = NULL;
+static struct fieldedTable *cachedTable = NULL;
+
+// Cache this table because there's a good chance we'll get called again with it.
+if ((cachedTableName == NULL) || differentString(tabSepMeta, cachedTableName))
+    {
+    char *requiredFields[] = {"meta"};
+    cachedTable = fieldedTableFromTabFile(tabSepMeta, NULL, requiredFields, sizeof requiredFields / sizeof (char *));
+    cachedHash = fieldedTableUniqueIndex(cachedTable, requiredFields[0]);
+    cachedTableName = cloneString(tabSepMeta);
+    }
+
+// Look for this tag in the metadata.
+struct fieldedRow *fr = hashFindVal(cachedHash, metaTag);
+if (fr == NULL)
+    return "";
+
+struct dyString *dyTable = dyStringCreate("<table style='display:inline-table;'>");
+if (showLongLabel)
+    dyStringPrintf(dyTable,"<tr valign='bottom'><td colspan=2 nowrap>%s</td></tr>",tdb->longLabel);
+if (showShortLabel)
+    dyStringPrintf(dyTable,"<tr valign='bottom'><td align='right' nowrap><i>shortLabel:</i></td>"
+			   "<td nowrap>%s</td></tr>",tdb->shortLabel);
+
+int ii;
+for(ii=0; ii < cachedTable->fieldCount; ii++)
+    {
+    char *fieldName = cachedTable->fields[ii];
+    char *fieldVal = fr->row[ii];
+    if (!sameString(fieldName, "meta")  && !isEmpty(fieldVal))
+        dyStringPrintf(dyTable,"<tr valign='bottom'><td align='right' nowrap><i>%s:</i></td>"
+                           "<td nowrap>%s</td></tr>",fieldName, fieldVal);
+    }
+dyStringAppend(dyTable,"</table>");
+return dyStringCannibalize(&dyTable);
+}
+
 char *tagStormAsHtmlTable(char *tagStormFile, struct trackDb *tdb,boolean showLongLabel,boolean showShortLabel)
 /* Return a string which is an HTML table of the tags for this track. */
 {
 char *metaTag = trackDbSetting(tdb, "meta");
 if (metaTag == NULL)
     return "";
 struct slPair *pairs = tagRepoPairs(tagStormFile, "meta", metaTag);
 
 if (pairs == NULL)
     return "";
 
 struct dyString *dyTable = dyStringCreate("<table style='display:inline-table;'>");
 
 if (showLongLabel)
     dyStringPrintf(dyTable,"<tr valign='bottom'><td colspan=2 nowrap>%s</td></tr>",tdb->longLabel);
@@ -212,30 +263,35 @@
 			   "<td nowrap>%s</td></tr>",tdb->shortLabel);
 
 for(; pairs; pairs = pairs->next)
     {
     if (!sameString(pairs->name, "meta")  && !isEmpty((char *)pairs->val))
         dyStringPrintf(dyTable,"<tr valign='bottom'><td align='right' nowrap><i>%s:</i></td>"
                            "<td nowrap>%s</td></tr>",pairs->name, (char *)pairs->val);
     }
 dyStringAppend(dyTable,"</table>");
 return dyStringCannibalize(&dyTable);
 }
 
 char *metadataAsHtmlTable(char *db,struct trackDb *tdb,boolean showLongLabel,boolean showShortLabel)
 // If metadata from metaDb exists, return string of html with table definition
 {
+char *tabSepMeta = trackDbSetting(tdb, "metaTab");
+
+if (tabSepMeta)
+    return tabSepMetaAsHtmlTable(tabSepMeta, tdb, showLongLabel, showShortLabel);
+
 char *tagStormFile = trackDbSetting(tdb, "metaDb");
 
 if (tagStormFile)
     return tagStormAsHtmlTable(tagStormFile, tdb, showLongLabel, showShortLabel);
 
 const struct mdbObj *safeObj = metadataForTable(db,tdb,NULL);
 if (safeObj == NULL || safeObj->vars == NULL)
     return NULL;
 
 //struct dyString *dyTable = dyStringCreate("<table id='mdb_%s'>",tdb->table);
 struct dyString *dyTable = dyStringCreate("<table style='display:inline-table;'>");
 if (showLongLabel)
     dyStringPrintf(dyTable,"<tr valign='bottom'><td colspan=2 nowrap>%s</td></tr>",tdb->longLabel);
 if (showShortLabel)
     dyStringPrintf(dyTable,"<tr valign='bottom'><td align='right' nowrap><i>shortLabel:</i></td>"
@@ -302,54 +358,55 @@
 		}
 	    }
 	dyStringPrintf(dyTable,"<tr valign='bottom'><td align='right' nowrap><i>%s:</i></td>"
 			       "<td nowrap>%s</td></tr>",mdbVar->var,mdbVar->val);
 	}
     }
 dyStringAppend(dyTable,"</table>");
 //mdbObjsFree(&mdbObj); // spill some memory
 return dyStringCannibalize(&dyTable);
 }
 
 boolean compositeMetadataToggle(char *db,struct trackDb *tdb,char *title,
     boolean embeddedInText,boolean showLongLabel)
 // If metadata from metaTbl exists, create a link that will allow toggling it's display
 {
-char *tagStormFile = trackDbSetting(tdb, "metaDb");
-if (tagStormFile == NULL)
+boolean hasMetaInHub = (trackDbSetting(tdb, "metaDb") != NULL) ||  (trackDbSetting(tdb, "metaTab") != NULL);
+if (!hasMetaInHub)
     {
     const struct mdbObj *safeObj = metadataForTable(db,tdb,NULL);
     if (safeObj == NULL || safeObj->vars == NULL)
         return FALSE;
     }
 
 char id[256];
 safef(id, sizeof id, "div_%s_link", tdb->track);
 printf("%s<A id='%s' HREF='#a_meta_%s' "
    "title='Show metadata details...'>%s<img src='../images/downBlue.png'/></A>",
    (embeddedInText?"&nbsp;":"<P>"),id,tdb->track, (title?title:""));
 jsOnEventByIdF("click", id, "return metadataShowHide(\"%s\",%s,true);", 
     tdb->track, showLongLabel?"true":"false");
 printf("<DIV id='div_%s_meta' style='display:none;'>%s</div>",tdb->track, metadataAsHtmlTable(db,tdb,showLongLabel,FALSE));
 return TRUE;
 }
 
 void extraUiLinks(char *db,struct trackDb *tdb)
 // Show metadata, and downloads, schema links where appropriate
 {
 char *tagStormFile = trackDbSetting(tdb, "metaDb");
-boolean hasMetadata = (tagStormFile != NULL) || (!tdbIsComposite(tdb) && !trackHubDatabase(db)
+char *tabSepFile = trackDbSetting(tdb, "metaTab");
+boolean hasMetadata = (tagStormFile != NULL) || (tabSepFile != NULL) || (!tdbIsComposite(tdb) && !trackHubDatabase(db)
                         && metadataForTable(db, tdb, NULL) != NULL);
 if (hasMetadata)
     printf("<b>Metadata:</b><br>%s\n", metadataAsHtmlTable(db, tdb, FALSE, FALSE));
 
 boolean schemaLink = (!tdbIsDownloadsOnly(tdb) && !trackHubDatabase(db)
 	      && isCustomTrack(tdb->table) == FALSE)
 	      && (hTableOrSplitExists(db, tdb->table));
 boolean downloadLink = (trackDbSetting(tdb, "wgEncode") != NULL && !tdbIsSuperTrack(tdb));
 int links = 0;
 if (schemaLink)
     links++;
 if (downloadLink)
     links++;
 
 if (links > 0)