5a39815b8ab378d38285b805538121c591f82e07
kate
  Tue Jan 20 13:01:44 2015 -0800
Add 'subGroupMetaTables' setting to trackDb.  This setting allows specification of a metadata table (minimally description+url) describing terms in a subgroup (e.g. hgFixed.wgEncodeCell for the 'cellType' subgroup in ENCODE tracks.   This ia aimed at providing basic metadata functionality with lower overhead than the cv.ra + metaDb table used during ENCODE2. refs #14353
diff --git src/hg/lib/hui.c src/hg/lib/hui.c
index fb55966..e4f8e89 100644
--- src/hg/lib/hui.c
+++ src/hg/lib/hui.c
@@ -4174,30 +4174,88 @@
     else
         printf("<TD colspan=%d>&nbsp;&nbsp;&nbsp;&nbsp;",columnCount);
 
     // Count of subtracks is filled in by javascript.
     if (subCount > 5)
         printf("<span class='subCBcount'></span>\n");
 
     // Restriction policy needs a link
     if (restrictions && sortOrder != NULL)
         printf("</TD><TH><A HREF='%s' TARGET=BLANK style='font-size:.9em;'>Restriction Policy</A>",
                ENCODE_DATA_RELEASE_POLICY);
 
     printf("</TD></TR></TFOOT>\n");
     }
 }
+/********************/
+/* Metadata for subgroups */
+
+struct meta {
+    char *term;
+    char *description;
+    char *url;
+};
+
+static struct hash *metaForAllSubGroupsGet(struct trackDb *parentTdb, struct cart *cart)
+/* Get description and URL for all subgroupMetaTables. Returns a hash of hashes */
+{
+char *setting = trackDbSetting(parentTdb, "subGroupMetaTables");
+if (!setting)
+    return NULL;
+struct slPair *metaTables = slPairFromString(setting);
+struct hash *tableHash = hashNew(0);
+struct slPair *metaTable;
+struct sqlResult *sr;
+char **row;
+char query[256];
+char *database = cartString(cart, "db");
+char *db = database;
+for (metaTable = metaTables; metaTable != NULL; metaTable = metaTables->next)
+    {
+    char *tableName = chopPrefix(cloneString(metaTable->val));
+    if (differentString(tableName, metaTable->val))
+        {
+        chopSuffix(metaTable->val);
+        db = metaTable->val;
+        }
+    struct sqlConnection *conn = hAllocConn(db);
+    boolean hasUrl = FALSE;
+    struct hash *subgroupHash = hashNew(0);
+    hashAdd(tableHash, metaTable->name, subgroupHash);
+    if (hHasField(db, tableName, "url"))
+        {
+        sqlSafef(query, sizeof(query), "select term, description, url from %s", tableName);
+        hasUrl = TRUE;
+        }
+    else
+        sqlSafef(query, sizeof(query), "select term, description from %s", tableName);
+    sr = sqlGetResult(conn, query);
+    while ((row = sqlNextRow(sr)) != NULL)
+        {
+        struct meta *meta = NULL;
+        AllocVar(meta);
+        meta->term = cloneString(row[0]);
+        meta->description = cloneString(row[1]);
+        if (hasUrl)
+            meta->url = cloneString(row[2]);
+        hashAdd(subgroupHash, meta->term, meta);
+        }
+sqlFreeResult(&sr);
+    hFreeConn(&conn);
+    }
+return tableHash;
+}
 
 static void printSubtrackTableBody(struct trackDb *parentTdb, struct slRef *subtrackRefList,
                                     struct subtrackConfigSettings *settings, struct cart *cart)
 /* Print list of subtracks */
 {
 sortOrder_t *sortOrder = settings->sortOrder;
 boolean useDragAndDrop = settings->useDragAndDrop;
 boolean restrictions = settings->restrictions;
 struct dyString *dyHtml = newDyString(SMALLBUF);
 char buffer[SMALLBUF];
 char *db = cartString(cart, "db");
 
 // The subtracks need to be sorted by priority but only sortable and dragable will have
 // non-default (cart) priorities to sort on
 boolean preSorted = FALSE;
@@ -4205,30 +4263,31 @@
     {
     // preserves user's prev sort/drags
     preSorted = tdbRefSortPrioritiesFromCart(cart, &subtrackRefList);
     printf("<TBODY class='%saltColors'>\n", (sortOrder != NULL ? "sortable " : "") );
     }
 else
     {
     slSort(&subtrackRefList, trackDbRefCmp);  // straight from trackDb.ra
     preSorted = TRUE;
     puts("<TBODY>");
     }
 
 // Finally the big "for loop" to list each subtrack as a table row.
 printf("\n<!-- ----- subtracks list ----- -->\n");
 membersForAll_t* membersForAll = membersForAllSubGroupsGet(parentTdb,NULL);
+struct hash *subgroupMetaHash = metaForAllSubGroupsGet(parentTdb, cart);
 struct slRef *subtrackRef;
 
 /* Color handling ?? */
 //char *colors[2]   = { COLOR_BG_DEFAULT,
 //                      COLOR_BG_ALTDEFAULT };
 char *colors[2]   = { "bgLevel1",
                       "bgLevel1" };
 int colorIx = settings->bgColorIx;
 
 for (subtrackRef = subtrackRefList; subtrackRef != NULL; subtrackRef = subtrackRef->next)
     {
     struct trackDb *subtrack = subtrackRef->val;
     int ix;
 
     // Determine whether subtrack is checked, visible, configurable, has group membership, etc.
@@ -4374,39 +4433,62 @@
             }
         }
     printf("</TD>");
 
     // If sortable, then there must be a column per sortable dimension
     if (sortOrder != NULL)
         {
         int sIx=0;
         for (sIx=0; sIx <sortOrder->count; sIx++)
             {
             char *col = sortOrder->column[sIx];
             ix = stringArrayIx(col, membership->subgroups, membership->count);
                                 // TODO: Sort needs to expand from subGroups to labels as well
             if (ix >= 0)
                 {
+                char *term = membership->membership[ix];
+                char *title = membership->titles[ix];
                 char *titleRoot=NULL;
-                if (cvTermIsEmpty(col, membership->titles[ix]))
+                if (cvTermIsEmpty(col, title))
                     titleRoot = cloneString(" &nbsp;");
                 else
-                    titleRoot = labelRoot(membership->titles[ix],NULL);
+                    titleRoot = labelRoot(title, NULL);
                 // Each sortable column requires hidden goop (in the "abbr" field currently)
                 // which is the actual sort on value
-                printf("<TD id='%s_%s' abbr='%s' align='left'>&nbsp;",
-                       subtrack->track, col, membership->membership[ix]);
+                printf("<TD id='%s_%s' abbr='%s' align='left'>", subtrack->track, col, term);
+                printf("&nbsp");
+                boolean printed = FALSE;
+                if (subgroupMetaHash)
+                    {
+                    struct hash *colHash = hashFindVal(subgroupMetaHash, col);
+                    if (colHash)
+                        {
+                        struct meta *meta = hashFindVal(colHash, term);
+                        if (meta != NULL)
+                            {
+                            if (meta->url == NULL || strlen(meta->url) == 0)
+                                /* add pointer to suggest hover */
+                                printf("<A title='%s' style='cursor: pointer;'>%s</A>", 
+                                            meta->description, titleRoot);
+                            else
+                                printf("<A target='_blank' class='cv' title='%s' href='%s'>%s</A>\n", 
+                                        meta->description, meta->url, titleRoot);
+                            printed = TRUE;
+                            }
+                        }
+                    }
+                if (!printed)
                     printf("%s",titleRoot);
                 puts("</TD>");
                 freeMem(titleRoot);
                 }
             else if (sameString(col, SUBTRACK_COLOR_SUBGROUP))
                 {
                 char *hue = subtrackColorToCompare(subtrack);
                 printf("<TD id='%s_%s' abbr='%s' bgcolor='#%02X%02X%02X'>"
                         "&nbsp;&nbsp;&nbsp;&nbsp;</TD>",
                     subtrack->track, col, hue, 
                         subtrack->colorR, subtrack->colorG, subtrack->colorB);
                 }
             }
         }
     else  // Non-sortable tables do not have sort by columns but will display a short label