8ccceea1c52492b6a869af8de3190a387d4b6550
braney
  Tue Mar 12 13:41:09 2024 -0700
added exportedDataHubs dialog and code to deal with quickLifted hubs

diff --git src/hg/lib/hubConnect.c src/hg/lib/hubConnect.c
index d6dff78..5b08b86 100644
--- src/hg/lib/hubConnect.c
+++ src/hg/lib/hubConnect.c
@@ -102,30 +102,51 @@
     struct dyString *trackHubs = dyStringNew(0);
     struct slPair *hubVar;
     boolean firstOne = TRUE;
     for (hubVar = hubVarList; hubVar != NULL; hubVar = hubVar->next)
         {
 	if (cartBoolean(cart, hubVar->name))
 	    {
 	    if (firstOne)
 		firstOne = FALSE;
 	    else
 		dyStringAppendC(trackHubs, ' ');
 	    dyStringAppend(trackHubs, hubVar->name + prefixLength);
 	    }
 	}
     slPairFreeList(&hubVarList);
+
+    // now see if we should quicklift any hubs
+    struct sqlConnection *conn = hConnectCentral();
+    char query[2048];
+    hubVarList = cartVarsWithPrefix(cart, "quickLift");
+    for (hubVar = hubVarList; hubVar != NULL; hubVar = hubVar->next)
+        {
+        unsigned hubNumber = atoi(hubVar->name + sizeof("quickLift"));
+        sqlSafef(query, sizeof(query), "select path from exportedDataHubs where id='%d'", hubNumber);
+        char *hubUrl = sqlQuickString(conn, query);
+        char *errorMessage;
+        unsigned hubId = hubFindOrAddUrlInStatusTable(cart, hubUrl, &errorMessage);
+
+        if (firstOne)
+            firstOne = FALSE;
+        else
+            dyStringAppendC(trackHubs, ' ');
+        dyStringPrintf(trackHubs, "%d:%s", hubId,(char *)hubVar->val);
+        }
+    hDisconnectCentral(&conn);
+
     cartSetString(cart, hubConnectTrackHubsVarName, trackHubs->string);
     dyStringFree(&trackHubs);
     cartRemove(cart, hgHubConnectRemakeTrackHub);
     }
 }
 
 struct slName *hubConnectHubsInCart(struct cart *cart)
 /* Return list of track hub ids that are turned on. */
 {
 hubConnectRemakeTrackHubVar(cart);
 char *trackHubString = cartOptionalString(cart, hubConnectTrackHubsVarName);
 return slNameListFromString(trackHubString, ' ');
 }
 
 boolean trackHubHasDatabase(struct trackHub *hub, char *database) 
@@ -170,31 +191,33 @@
 
 return tHub;
 }
 
 static boolean
 hubTimeToCheck(struct hubConnectStatus *hub, char *notOkStatus)
 /* check to see if enough time has passed to re-check a hub that
  * has an error status.  Use udcTimeout as the length of time to wait.*/
 {
 time_t checkTime = udcCacheTimeout();
 return dateIsOlderBy(notOkStatus, "%F %T", checkTime);
 }
 
 /* Given a hub ID return associated status. Returns NULL if no such hub.  If hub
  * exists but has problems will return with errorMessage field filled in. */
-struct hubConnectStatus *hubConnectStatusForId(struct sqlConnection *conn, int id)
+struct hubConnectStatus *hubConnectStatusForIdExt(struct sqlConnection *conn, int id, char *replaceDb, char *newDb, char *quickLiftChain)
+/* Given a hub ID return associated status. For quickLifted hubs, replace the db with our current db and
+ * keep track of the quickLiftChain for updating trackDb later.*/
 {
 struct hubConnectStatus *hub = NULL;
 char query[1024];
 sqlSafef(query, sizeof(query), 
     "select hubUrl,status, errorMessage,lastNotOkTime, shortLabel from %s where id=%d", getHubStatusTableName(), id);
 struct sqlResult *sr = sqlGetResult(conn, query);
 char **row = sqlNextRow(sr);
 if (row != NULL)
     {
     AllocVar(hub);
     hub->id = id;
     hub->hubUrl = cloneString(row[0]);
     hub->status = sqlUnsigned(row[1]);
     hub->errorMessage = cloneString(row[2]);
     hub->shortLabel = cloneString(row[4]);
@@ -206,71 +229,116 @@
             hub->trackHub->hubStatus = hub;
 	hub->errorMessage = cloneString(errorMessage);
 	hubUpdateStatus( hub->errorMessage, hub);
 	if (!isEmpty(hub->errorMessage))
 	    {
             boolean isCollection = (strstr(hub->hubUrl, "hgComposite") != NULL);
             if (isCollection)
                 warn("You created a <a href=\"/cgi-bin/hgCollection\"><b>Track "
          "Collection</b></a> that has expired and been removed. Track Collections "
          "expire 48 hours after their last use. <a href=\"/cgi-bin/hgSession\"><b>"
          "Save your session</b></a> to preserve collections long-term and to allow sharing.");
             // commenting this out, but leaving it in the source because we might use it later.
             //else
                 //warn("Could not connect to hub \"%s\": %s", hub->shortLabel, hub->errorMessage);
 	    }
+        if ((hub->trackHub != NULL) && (replaceDb != NULL))
+            {
+            struct trackHubGenome *genome = hub->trackHub->genomeList;
+
+            for(; genome; genome = genome->next)
+                {
+                if (sameString(genome->name, replaceDb))
+                    {
+                    genome->name = newDb;
+                    hashAdd(hub->trackHub->genomeHash, newDb, genome);
+                    genome->quickLiftChain = quickLiftChain;
+                    }
+                }
+            }
 	}
     }
 sqlFreeResult(&sr);
 return hub;
 }
 
+struct hubConnectStatus *hubConnectStatusForId(struct sqlConnection *conn, int id)
+{
+return hubConnectStatusForIdExt(conn, id, NULL, NULL, NULL);
+}
+
 struct hubConnectStatus *hubConnectStatusListFromCartAll(struct cart *cart)
 /* Return list of all track hubs that are referenced by cart. */
 {
 struct hubConnectStatus *hubList = NULL, *hub;
 struct slPair *pair, *pairList = cartVarsWithPrefix(cart, hgHubConnectHubVarPrefix);
 struct sqlConnection *conn = hConnectCentral();
 for (pair = pairList; pair != NULL; pair = pair->next)
     {
     // is this hub turned connected??
     if (differentString(pair->val, "1"))
         continue;
 
     int id = hubIdFromCartName(pair->name);
     hub = hubConnectStatusForId(conn, id);
     if (hub != NULL)
 	{
         slAddHead(&hubList, hub);
 	}
     }
 slFreeList(&pairList);
 hDisconnectCentral(&conn);
 slReverse(&hubList);
 return hubList;
 }
 
 struct hubConnectStatus *hubConnectStatusListFromCart(struct cart *cart)
 /* Return list of track hubs that are turned on by user in cart. */
 {
-struct hubConnectStatus *hubList = NULL, *hub;
+struct hubConnectStatus *hubList = NULL, *hub = NULL;
 struct slName *name, *nameList = hubConnectHubsInCart(cart);
 struct sqlConnection *conn = hConnectCentral();
 for (name = nameList; name != NULL; name = name->next)
     {
-    int id = sqlSigned(name->name);
+    // items in trackHub statement may need to be quickLifted.  This is implied
+    // by the hubStatus id followed by a colon and then a index into the quickLiftChain table
+    char *copy = cloneString(name->name);
+    char *colon = strchr(copy, ':');
+    if (colon)
+        *colon++ = 0;
+    int id = sqlSigned(copy);
+    if (colon == NULL)  // not quickLifted
         hub = hubConnectStatusForId(conn, id);
+    else
+        {
+        char query[4096];
+        sqlSafef(query, sizeof(query), "select fromDb, toDb, path from %s where id = \"%s\"", "quickLiftChain", colon);
+        struct sqlResult *sr = sqlGetResult(conn, query);
+        char **row;
+        char *replaceDb = NULL;
+        char *quickLiftChain = NULL;
+        char *toDb = NULL;
+        while ((row = sqlNextRow(sr)) != NULL)
+            {
+            replaceDb = cloneString(row[0]);
+            toDb = cloneString(row[1]);
+            quickLiftChain = cloneString(row[2]);
+            break; // there's only one
+            }
+        sqlFreeResult(&sr);
+        hub = hubConnectStatusForIdExt(conn, id, replaceDb, toDb, quickLiftChain);
+        }
     if (hub != NULL)
 	{
 	if (!isEmpty(hub->errorMessage) && (strstr(hub->hubUrl, "hgComposite") != NULL))
             {
             // custom collection hub has disappeared.   Remove it from cart
             cartSetString(cart, hgHubConnectRemakeTrackHub, "on");
             char buffer[1024];
             safef(buffer, sizeof buffer, "hgHubConnect.hub.%d", id);
             cartRemove(cart, buffer);
             }
         else
             slAddHead(&hubList, hub);
 	}
     }
 slFreeList(&nameList);
@@ -357,48 +425,65 @@
 return p;
 }
 
 
 void hubConnectAddDescription(char *database, struct trackDb *tdb)
 /* Fetch tdb->track's html description (or nearest ancestor's non-empty description)
  * and store in tdb->html. */
 {
 unsigned hubId = hubIdFromTrackName(tdb->track);
 struct hubConnectStatus *hub = hubFromId(hubId);
 struct trackHubGenome *hubGenome = trackHubFindGenome(hub->trackHub, database);
 trackHubPolishTrackNames(hub->trackHub, tdb);
 trackHubAddDescription(hubGenome->trackDbFile, tdb);
 }
 
+static void assignQuickLift(struct trackDb *tdbList, char *quickLiftChain)
+/* step through a trackDb list and assign a quickLift chain to each track */
+{
+if (tdbList == NULL)
+    return;
+
+struct trackDb *tdb;
+for(tdb = tdbList; tdb; tdb = tdb->next)
+    {
+    assignQuickLift(tdb->subtracks, quickLiftChain);
+
+    hashAdd(tdb->settingsHash, "quickLiftUrl", quickLiftChain);
+    }
+}
+
 struct trackDb *hubConnectAddHubForTrackAndFindTdb( char *database, 
     char *trackName, struct trackDb **pTdbList, struct hash *trackHash)
 /* Go find hub for trackName (which will begin with hub_), and load the tracks
  * for it, appending to end of list and adding to trackHash.  Return the
  * trackDb associated with trackName. This will also fill in the html fields,
  * but just for that track and it's parents. */ 
 {
 unsigned hubId = hubIdFromTrackName(trackName);
 struct hubConnectStatus *hub = hubFromId(hubId);
 struct trackHubGenome *hubGenome = trackHubFindGenome(hub->trackHub, database);
 if (hubGenome == NULL)
     errAbort("Cannot find genome %s in hub %s", database, hub->hubUrl);
 boolean foundFirstGenome = FALSE;
 struct trackDb *tdbList = trackHubTracksForGenome(hub->trackHub, hubGenome, NULL, &foundFirstGenome);
 tdbList = trackDbLinkUpGenerations(tdbList);
 tdbList = trackDbPolishAfterLinkup(tdbList, database);
 //this next line causes warns to print outside of warn box on hgTrackUi
 //trackDbPrioritizeContainerItems(tdbList);
+if (hubGenome->quickLiftChain)
+    assignQuickLift(tdbList, hubGenome->quickLiftChain);
 trackHubPolishTrackNames(hub->trackHub, tdbList);
 char *fixTrackName = cloneString(trackName);
 trackHubFixName(fixTrackName);
 rAddTrackListToHash(trackHash, tdbList, NULL, FALSE);
 if (pTdbList != NULL)
     *pTdbList = slCat(*pTdbList, tdbList);
 struct trackDb *tdb = hashFindVal(trackHash, fixTrackName);
 if (tdb == NULL) 
     // superTracks aren't in the hash... look in tdbList
     tdb = findSuperTrack(tdbList, fixTrackName);
 
 if (tdb == NULL) 
     errAbort("Can't find track %s in %s", fixTrackName, hub->trackHub->url);
 
 /* Add html for track and parents. */
@@ -789,30 +874,33 @@
                     return cacheTdb;
                 }
 
             memCheckPoint(); // we want to know how much memory is used to build the tdbList
             }
 
         struct dyString *incFiles = dyStringNew(4096);
         tdbList = trackHubTracksForGenome(trackHub, hubGenome, incFiles, foundFirstGenome);
         tdbList = trackDbLinkUpGenerations(tdbList);
         tdbList = trackDbPolishAfterLinkup(tdbList, database);
         trackDbPrioritizeContainerItems(tdbList);
         trackHubPolishTrackNames(trackHub, tdbList);
 
         if (doCache)
             trackDbHubCloneTdbListToSharedMem(hubGenome->trackDbFile, tdbList, memCheckPoint(), incFiles->string);
+
+        if (hubGenome->quickLiftChain)
+            assignQuickLift(tdbList, hubGenome->quickLiftChain);
 	}
     }
 return tdbList;
 }
 
 static struct grp *grpFromHub(struct hubConnectStatus *hub)
 /* Make up a grp structur from hub */
 {
 struct grp *grp;
 AllocVar(grp);
 char name[16];
 safef(name, sizeof(name), "hub_%d", hub->id);
 grp->name = cloneString(name);
 grp->label = cloneString(hub->shortLabel);
 return grp;