0bd11409624970f87f46089c184f38bc9360f136
braney
  Sat Mar 10 11:18:22 2018 -0800
make sure auto-generated short labels are unique within the collection

diff --git src/hg/hgCollection/hgCollection.c src/hg/hgCollection/hgCollection.c
index afd925e..7cccd6f 100644
--- src/hg/hgCollection/hgCollection.c
+++ src/hg/hgCollection/hgCollection.c
@@ -41,31 +41,56 @@
 char *visibility;
 unsigned long color;
 char *viewFunc;        // The method by which calculated tracks should be calculated
 char *missingMethod;   // How should missing data be treated in calculated tracks
 };
 
 struct trackDbRef 
 {
 struct trackDbRef *next;
 struct trackDb *tdb;
 struct grp *grp;
 double priority;
 int order;
 };
 
-static char *makeUnique(struct hash *nameHash, char *name)
+static char *makeUniqueLabel(struct hash *labelHash, char *label)
+// Make the short label  of this track unique.
+{
+if (hashLookup(labelHash, label) == NULL)
+    {
+    hashStore(labelHash, label);
+    return label;
+    }
+
+unsigned count = 1;
+char buffer[4096];
+
+for(;; count++)
+    {
+    safef(buffer, sizeof buffer, "%s (%d)", label, count);
+    if (hashLookup(labelHash, buffer) == NULL)
+        {
+        hashStore(labelHash, buffer);
+        return cloneString(buffer);
+        }
+    }
+
+return NULL;
+}
+
+static char *makeUniqueName(struct hash *nameHash, char *name)
 // Make the name of this track unique.
 {
 char *skipHub = trackHubSkipHubName(name);
 if (hashLookup(nameHash, skipHub) == NULL)
     {
     hashStore(nameHash, name);
     return skipHub;
     }
 
 char base[4096];
 safef(base, sizeof base, "%s_%lx",skipHub, time(NULL) - 1520629086);
 
 unsigned count = 0;
 char buffer[4096];
 
@@ -361,65 +386,69 @@
 struct hash *groupHash = newHash(10);
 int count = 0;
 
 for(curGroup = groupList; curGroup;  curGroup = curGroup->next)
     {
     if (curGroup->priority == 0)
         curGroup->priority = count--;
     hashAdd(groupHash, curGroup->name, curGroup);
     }
 
 curGroup = NULL;
 if (hubName != NULL)
     curGroup = hashFindVal(groupHash, hubName);
 
 jsInlineF("var collectionData = []; ");
-struct dyString *dy = newDyString(1024);
+struct dyString *dyNames = newDyString(1024);
+struct dyString *dyLabels = newDyString(1024);
 jsInlineF("var collectionNames = [];");
+jsInlineF("var collectionLabels = [];");
 if (curGroup != NULL)
     {
     // print out all the tracks in all the collections
     struct trackDb *tdb;
     jsInlineF("collectionData['#'] = [");
     boolean first = TRUE;
     for(tdb = trackList; tdb;  tdb = tdb->next)
         {
         if (sameString(tdb->grp, hubName))
             {
             if (!first)
                 {
                 jsInlineF(",");
                 }
             printTrack("#", tdb,  TRUE);
-            dyStringPrintf(dy, "collectionNames['%s']=1;", trackHubSkipHubName(tdb->track));
+            dyStringPrintf(dyNames, "collectionNames['%s']=1;", trackHubSkipHubName(tdb->track));
+            dyStringPrintf(dyLabels, "collectionLabels['%s']=1;", tdb->shortLabel);
             first = FALSE;
             }
         }
     jsInlineF("];");
     for(tdb = trackList; tdb;  tdb = tdb->next)
         {
         if ( sameString(tdb->grp, curGroup->name))
             {
             printSubtracks("collectionData", tdb, TRUE);
-            addSubtrackNames(dy, tdb);
+            addSubtrackNames(dyNames, tdb);
             }
         }
     }
 else
     jsInlineF("collectionData['#'] = [];");
 
-jsInlineF("%s", dy->string);
+jsInlineF("%s", dyNames->string);
+jsInlineF("%s", dyLabels->string);
 
 jsInlineF("var trackData = []; ");
 struct dyString *rootChildren = newDyString(512);
 addVisibleTracks(groupHash, rootChildren, cart, trackList);
 for(curGroup = groupList; curGroup;  curGroup = curGroup->next)
     {
     if ((hubName != NULL) && sameString(curGroup->name, hubName))
         continue;
     if (!isEmpty(rootChildren->string))
         dyStringPrintf(rootChildren, ",");
     dyStringPrintf(rootChildren, "{icon:'../images/folderC.png',id:'%s', text:'%s', parent:'#', children:true,li_attr:{title:'%s'}}", curGroup->name, curGroup->label, FOLDERTITLE);
     struct trackDb *tdb;
     jsInlineF("trackData['%s'] = [", curGroup->name);
     boolean first = TRUE;
     for(tdb = trackList; tdb;  tdb = tdb->next)
@@ -566,31 +595,31 @@
         if (conn == NULL)
             errAbort("track hub has bigWig without bigDataUrl");
         dataUrl = getSqlBigWig(conn, db, tdb);
         hashReplace(tdb->settingsHash, "bigDataUrl", dataUrl);
         }
     }
 
 char *tdbType = trackDbSetting(tdb, "tdbType");
 if (tdbType != NULL)
     hashReplace(tdb->settingsHash, "type", tdbType);
 
 hashRemove(tdb->settingsHash, "superTrack");
 hashReplace(tdb->settingsHash, "parent", parent);
 hashReplace(tdb->settingsHash, "shortLabel", track->shortLabel);
 hashReplace(tdb->settingsHash, "longLabel", track->longLabel);
-hashReplace(tdb->settingsHash, "track", makeUnique(collectionNameHash, name));
+hashReplace(tdb->settingsHash, "track", makeUniqueName(collectionNameHash, name));
 char priBuf[128];
 safef(priBuf, sizeof priBuf, "%d", priority);
 hashReplace(tdb->settingsHash, "priority", cloneString(priBuf));
 char colorString[64];
 safef(colorString, sizeof colorString, "%d,%d,%d", (color >> 16) & 0xff,(color >> 8) & 0xff,color & 0xff);
 hashReplace(tdb->settingsHash, "color", colorString);
 
 struct dyString *dy = trackDbString(tdb);
 
 fprintf(f, "%s",  dy->string);
 fprintf(f, "\n");
 }
 
 static void outComposite(FILE *f, struct track *collection, int priority)
 // output a composite header for user composite
@@ -785,40 +814,42 @@
 
 slReverse(&collectionList);
 return collectionList;
 }
 
 static void doAjax(struct cart *cart, char *db, char *jsonText, struct hash *nameHash)
 // Save our state
 {
 cgiDecodeFull(jsonText, jsonText, strlen(jsonText));
 struct jsonElement *collectionElements = jsonParse(jsonText);
 struct track *collectionList = parseJsonElements(collectionElements);
 
 updateHub(cart, db, collectionList, nameHash);
 }
 
-static void buildNameHash(struct hash *nameHash, struct trackDb *list)
+static void buildNameHash(struct hash *nameHash, struct hash *labelHash, struct trackDb *list)
 {
 if (list == NULL)
     return;
 
 struct trackDb *tdb = list;
 for(tdb = list; tdb;  tdb = tdb->next)
     {
     hashAdd(nameHash, trackHubSkipHubName(tdb->track), tdb);
-    buildNameHash(nameHash, tdb->subtracks);
+    if (labelHash)
+        hashAdd(labelHash, tdb->shortLabel, tdb);
+    buildNameHash(nameHash, NULL,  tdb->subtracks);
     }
 }
 
 static struct trackDb *traverseTree(struct trackDb *oldList, struct hash *groupHash)
 // add acceptable tracks to our tree
 {
 struct trackDb *newList = NULL, *tdb, *tdbNext;
 
 for(tdb = oldList;  tdb ; tdb = tdbNext)
     {
     tdbNext = tdb->next;
     if (tdb->subtracks)
         {
         tdb->subtracks = traverseTree(tdb->subtracks, groupHash);
 
@@ -972,37 +1003,37 @@
     }
 }
 
 static void doAddTrack(struct cart *cart, char *db, struct trackDb *trackList,  char *trackName, char *collectionName, struct hash *nameHash)
 /* Add a track to a collection in a hub. */
 {
 char *fileName = getHubName(cart, db);
 char *hubName = hubNameFromUrl(fileName);
 FILE *f = fopen(fileName, "w");
 struct trackDb *newTdb = hashMustFindVal(nameHash, trackHubSkipHubName(trackName));
 if (newTdb->subtracks)
     {
     struct trackDb *subTdb;
     for(subTdb = newTdb->subtracks; subTdb; subTdb = subTdb->next)
         {
-        hashReplace(subTdb->settingsHash, "track", makeUnique(nameHash, subTdb->track));
+        hashReplace(subTdb->settingsHash, "track", makeUniqueName(nameHash, subTdb->track));
         hashReplace(subTdb->settingsHash, "parent", trackHubSkipHubName(collectionName));
         }
     }
 else
     {
-    hashReplace(newTdb->settingsHash, "track", makeUnique(nameHash, trackName));
+    hashReplace(newTdb->settingsHash, "track", makeUniqueName(nameHash, trackName));
     hashReplace(newTdb->settingsHash, "parent", trackHubSkipHubName(collectionName));
     }
 char *tdbType = trackDbSetting(newTdb, "tdbType");
 if (tdbType != NULL)
     {
     hashReplace(newTdb->settingsHash, "type", tdbType);
     hashReplace(newTdb->settingsHash, "shortLabel", trackDbSetting(newTdb, "name"));
     hashReplace(newTdb->settingsHash, "longLabel", trackDbSetting(newTdb, "description"));
     }
 
 
 outHubHeader(f, db);
 struct sqlConnection *conn = NULL;
 if (!trackHubDatabase(db))
     conn = hAllocConn(db);
@@ -1020,50 +1051,53 @@
 char *genome;
 getDbAndGenome(cart, &db, &genome, oldVars);
 initGenbankTableNames(db);
 int timeout = cartUsualInt(cart, "udcTimeout", 300);
 if (udcCacheTimeout() < timeout)
     udcSetCacheTimeout(timeout);
 knetUdcInstall();
 
 struct trackDb *trackList;
 struct grp *groupList;
 cartTrackDbInit(cart, &trackList, &groupList, TRUE);
 pruneTrackList(&trackList, &groupList);
 
 struct trackDb *superList = addSupers(trackList);
 struct hash *nameHash = newHash(5);
-buildNameHash(nameHash, superList);
+struct hash *labelHash = newHash(5);
+buildNameHash(nameHash, labelHash, superList);
 
 char *cmd = cartOptionalString(cart, "cmd");
 if (cmd == NULL)
     {
     doMainPage(cart, db, groupList, superList);
     }
 else if (sameString("addTrack", cmd))
     {
     char *trackName = cgiString("track");
     char *collectionName = cgiString("collection");
     doAddTrack(cart, db, superList, trackName, collectionName, nameHash);
     apiOut("{\"serverSays\": \"added %s to collection\"}", NULL);
     }
 else if (sameString("newCollection", cmd))
     {
     char *trackName = cgiString("track");
-    char *collectionName = makeUnique(nameHash, "coll");
-    char *shortLabel = "New Collection";
-    char *longLabel = "Description of New Collection";
+    char *collectionName = makeUniqueName(nameHash, "coll");
+    char *shortLabel = makeUniqueLabel(labelHash, "New Collection");
+    char buffer[4096];
+    safef(buffer, sizeof buffer, "%s description", shortLabel);
+    char *longLabel = cloneString(buffer);
     struct trackDb *tdb;
 
     AllocVar(tdb);
     slAddHead(&superList, tdb);
     tdb->settingsHash = newHash(5);
     tdb->type = cloneString("wig");
 
     hashAdd(tdb->settingsHash, "track", collectionName);
     hashAdd(tdb->settingsHash, "shortLabel", shortLabel);
     hashAdd(tdb->settingsHash, "longLabel", longLabel);
     hashAdd(tdb->settingsHash, "autoScale", "on");
     hashAdd(tdb->settingsHash, "compositeTrack", "on");
     hashAdd(tdb->settingsHash, "aggregate", "none");
     hashAdd(tdb->settingsHash, "type", "mathWig");
     hashAdd(tdb->settingsHash, "visibility", "full");