530842acf922e67838eab9fa153b7b7c0b90e9c2
braney
  Thu Apr 18 12:31:31 2024 -0700
allow track hubs to make groups

diff --git src/hg/lib/trackHub.c src/hg/lib/trackHub.c
index 1cd8bbf..65b9b02 100644
--- src/hg/lib/trackHub.c
+++ src/hg/lib/trackHub.c
@@ -43,42 +43,89 @@
 #include "bPlusTree.h"
 #include "hgFind.h"
 #include "hubConnect.h"
 #include "trix.h"
 #include "vcf.h"
 #include "vcfUi.h"
 #include "htmshell.h"
 #include "bigBedFind.h"
 #include "customComposite.h"
 #include "interactUi.h"
 #include "bedTabix.h"
 #include "hic.h"
 #include "hui.h"
 #include "chromAlias.h"
 #include "trashDir.h"
+#include "hgConfig.h"
 
 #ifdef USE_HAL
 #include "halBlockViz.h"
 #endif
 
+struct grp *trackHubGrps = NULL;   // global with grps loaded from track hubs
 static struct hash *hubCladeHash;  // mapping of clade name to hub pointer
 static struct hash *hubAssemblyHash; // mapping of assembly name to genome struct
 static struct hash *hubAssemblyUndecoratedHash; // mapping of undecorated assembly name to genome struct
 static struct hash *hubOrgHash;   // mapping from organism name to hub pointer
 static struct trackHub *globalAssemblyHubList; // list of trackHubs in the user's cart
 static struct hash *trackHubHash;
 
+static boolean hubsCanAddGroups()
+/* can track hubs have their own groups? */
+{
+static boolean canHubs = FALSE;
+static boolean canHubsSet = FALSE;
+
+if (!canHubsSet)
+    {
+    canHubs = cfgOptionBooleanDefault("trackHubsCanAddGroups", FALSE);
+    canHubsSet = TRUE;
+    }
+
+return canHubs;
+}
+
+static void tdbListAddHubToGroup(char *hubName, struct trackDb *tdbList)
+/* Prepend hub name to  group name for every tdb. */
+{
+struct trackDb *tdb;
+for (tdb = tdbList; tdb != NULL; tdb = tdb->next)
+    {
+    char buffer[4096];
+
+    char *grp = trackDbSetting(tdb, "group");
+    safef(buffer, sizeof buffer, "%s_%s", hubName, grp);
+    tdb->grp = cloneString(buffer);
+    hashReplace(tdb->settingsHash, "group", tdb->grp);
+    }
+}
+
+
+static void grpListAddHubName(struct grp *grpList, struct trackHub *hub)
+/* Add the hub name to the groups defined by a hub. */
+{
+char buffer[4096];
+
+for (; grpList; grpList = grpList->next)
+    {
+    safef(buffer, sizeof buffer, "%s_%s", hub->name, grpList->name);
+    grpList->name = cloneString(buffer);
+    safef(buffer, sizeof buffer, "Hub: %s : %s", hub->shortLabel, grpList->label);
+    grpList->label = cloneString(buffer);
+    }
+}
+
 char *trackHubRelativeUrl(char *hubUrl, char *path)
 /* Return full path (in URL form if it's a remote hub) given
  * path possibly relative to hubUrl. Do a freeMem of result
  * when done. */
 {
 /* If path itself is a URL then just return a copy of it. */
 if (hasProtocol(path))
     return cloneString(path);
 
 /* If it's a remote hub, let html path expander handle it. */
 if (hasProtocol(hubUrl) && !startsWith("/gbdb",path))
     return expandUrlOnBase(hubUrl, path);
 
 /* If we got to here hub is local, and so is path.  Do standard
  * path parsing. */
@@ -670,30 +717,41 @@
 	el->defaultPos  = hashFindVal(ra, "defaultPos");
 	if (el->defaultPos == NULL)
 	    errAbort("must have 'defaultPos' set in assembly hub in stanza ending line %d of %s",
 		     lf->lineIx, lf->fileName);
 	el->twoBitPath = trackHubRelativeUrl(url, twoBitPath);
         if (twoBitBptUrl != NULL)
             el->twoBitBptUrl = trackHubRelativeUrl(url, twoBitBptUrl);
 
 	char *htmlPath = hashFindVal(ra, "htmlPath");
 	if (htmlPath != NULL)
 	    hashReplace(ra, "htmlPath",trackHubRelativeUrl(url, htmlPath));
 	if (groups != NULL)
 	    el->groups = trackHubRelativeUrl(url, groups);
 	addAssembly(genome, el, hub);
 	}
+    else
+        {
+	if ((groups != NULL) && hubsCanAddGroups())
+            {
+	    el->groups = trackHubRelativeUrl(url, groups);
+            struct grp *list = readGroupRa(el->groups);
+            grpListAddHubName(list, hub);
+
+            trackHubGrps = slCat(trackHubGrps, list);
+            }
+        }
     el->settingsHash = ra;
     hashAdd(ra, "hubName", hub->shortLabel);
     el->chromAuthority = hashFindVal(ra, "chromAuthority");
     }
 
 /* Clean up and go home. */
 lineFileClose(&lf);
 slReverse(&list);
 slSort(&list, genomeOrderKeyCmp);
 return list;
 }
 
 char *trackHubSetting(struct trackHub *hub, char *name)
 /* Return setting if it exists, otherwise NULL. */
 {
@@ -1092,34 +1150,39 @@
 struct trackDb *tdb;
 for (tdb = tdbList; tdb != NULL; tdb = tdb->next)
     {
     expandBigDataUrl(hub, genome, tdb);
     if  (absStormName)
         hashReplace(tdb->settingsHash, "metaDb", absStormName);
     if  (absTabName)
         hashReplace(tdb->settingsHash, "metaTab", absTabName);
     }
 
 validateTracks(hub, genome, tdbList);
 
 trackDbAddTableField(tdbList);
 if (!isEmpty(hub->name))
     trackHubAddNamePrefix(hub->name, tdbList);
-if (genome->twoBitPath == NULL || *foundFirstGenome)
-    trackHubAddGroupName(hub->name, tdbList);
-else
+
+if ((genome->twoBitPath != NULL) && (*foundFirstGenome == FALSE))
     *foundFirstGenome = TRUE;
+else if (genome->groups != NULL)
+    tdbListAddHubToGroup(hub->name, tdbList);
+else
+    trackHubAddGroupName(hub->name, tdbList);
+
+
 for (tdb = tdbList; tdb != NULL; tdb = tdb->next)
     {
     trackDbFieldsFromSettings(tdb);
     trackDbPolish(tdb);
     }
 return tdbList;
 }
 
 static void reprefixString(char **pString, char *prefix)
 /* Replace *pString with prefix + *pString, freeing
  * whatever was in *pString before. */
 {
 char *oldName = *pString;
 *pString = catTwoStrings(prefix, oldName);
 freeMem(oldName);
@@ -1606,15 +1669,21 @@
 char *trackHubBuild(char *db, struct cart *cart, struct dyString *visDy)
 /* Build a track hub using trackDb and the cart. */
 {
 struct trackDb *tdb = hTrackDb(db);
 slSort(&tdb,trackDbCmp);
 char *filename = getHubName(cart, db);
 
 FILE *f = mustOpen(filename, "a");
 chmod(filename, 0666);
 
 walkTree(f, cart, tdb, visDy);
 fclose(f);
 
 return cloneString(filename);
 }
+
+struct grp *trackHubGetGrps()
+/* Get the groups defined by attached track hubs. */
+{
+return trackHubGrps;
+}