3024edbfb4af164c8ef34eded35ec41015437c5c
hiram
  Wed Mar 11 14:56:35 2026 -0700
allow GenArk hubs to exist as "curated" hubs with a dbDb entry such as rn8 and a nibPath to the GenArk /gbdb/ directory refs #32985

diff --git src/hg/lib/hubConnect.c src/hg/lib/hubConnect.c
index 1ad63ece3b3..42f8259c3e1 100644
--- src/hg/lib/hubConnect.c
+++ src/hg/lib/hubConnect.c
@@ -1202,102 +1202,173 @@
 
 hDisconnectCentral(&conn);
 return added;
 }
 
 static char *getCuratedHubPrefix()
 /* figure out what sandbox we're in. */
 {
 char *curatedHubPrefix = cfgOption("curatedHubPrefix");
 if (isEmpty(curatedHubPrefix))
     curatedHubPrefix = "public";
 
 return curatedHubPrefix;
 }
 
+static boolean genarkCurated(char *dirPath, char **hubUrl)
+/* Given a dirPath to a GenArk hub, find the appropriate hub.txt */
+{
+boolean ret = FALSE;
+if (dirPath != NULL && isDirectory(dirPath))
+    {
+    *hubUrl = NULL;
+    char *prefix = getCuratedHubPrefix();
+
+    /* Try <prefix>.hub.txt first */
+    char candidate[4096];
+    safef(candidate, sizeof candidate, "%s/%s.hub.txt", dirPath, prefix);
+    if (fileExists(candidate))
+        {
+        *hubUrl = cloneString(candidate);
+	ret = TRUE;
+        }
+    else	/* no specific 'prefix.hub.txt' than simply use the 'hub.txt' */
+        {
+        safef(candidate, sizeof candidate, "%s/hub.txt", dirPath);
+        if (fileExists(candidate))
+	    {
+	    *hubUrl = cloneString(candidate);
+	    ret = TRUE;
+	    }
+        }
+    }
+
+return ret;
+}
+
+
+// nibPath: hub:/gbdb/hs1/hubs - 'hs1' style
+// nibPath: hub:/gbdb/genark/GCF/036/323/735/GCF_036323735.1 - GenArk style
 
 boolean hubConnectGetCuratedUrl(char *db, char **hubUrl)
 /* Check to see if this db is a curated hub and if so return its hubUrl */
 {
 struct sqlConnection *conn = hConnectCentral();
 char query[4096];
 sqlSafef(query, sizeof query, "SELECT nibPath from %s where name = '%s' AND nibPath like '%s%%'",
           dbDbTable(), db, hubCuratedPrefix);
 
 char *dir = sqlQuickString(conn, query);
 boolean ret = !isEmpty(dir);
 hDisconnectCentral(&conn);
 
 if (hubUrl != NULL) // if user passed in hubUrl, calculate what it should be
     {
     *hubUrl = NULL;
     if (!isEmpty(dir))   // this is a curated hub
         {
         char *path = dir + sizeof(hubCuratedPrefix) - 1;
         char url[4096];
+        safef(url, sizeof url, "%s/%s", path, getCuratedHubPrefix());
+	if (isDirectory(url))	// 'hs1' style curated hub
+	    {
 	    safef(url, sizeof url, "%s/%s/hub.txt", path,getCuratedHubPrefix());
             *hubUrl = cloneString(url);
 	    }
+	else
+	    {
+	    if (genarkCurated(path, hubUrl))
+		{
+                ret = TRUE;
+		}
+	    }
+        }
     }
 
 return ret;
 }
 
 boolean hubConnectIsCurated(char *db)
 /* Look in the dbDb table to see if this hub is curated. */
 {
 return hubConnectGetCuratedUrl(db, NULL);
 }
 
 static int lookForCuratedHubs(struct cart *cart, char *db,  char *curatedHubPrefix)
 /* Check to see if db is a curated hub which will require the hub to be attached. 
  * The variable curatedHubPrefix has the release to use (alpha, beta, public, or a user name ) */
 {
 struct sqlConnection *conn = hConnectCentral();
 char query[4096];
 sqlSafef(query, sizeof query, "SELECT nibPath from %s where name = '%s' AND nibPath like '%s%%'",
           dbDbTable(), db, hubCuratedPrefix);
 
 char *dir = cloneString(sqlQuickString(conn, query));
 hDisconnectCentral(&conn);
 
 if (!isEmpty(dir))
     {
     char *path = &dir[sizeof hubCuratedPrefix - 1];
     char urlBuf[4096];
+    safef(urlBuf, sizeof urlBuf, "%s/%s", path, curatedHubPrefix);
+    char *url = NULL;
+    if (isDirectory(urlBuf))	// 'hs1' style curated hub
+	{
 	safef(urlBuf, sizeof urlBuf, "%s/%s/hub.txt", path, curatedHubPrefix);
-    char *url = hReplaceGbdb(urlBuf);
+	url = hReplaceGbdb(urlBuf);
+	}
+    else	// GenArk style curated hub
+	{
+	safef(urlBuf, sizeof urlBuf, "%s/%s.hub.txt", path, curatedHubPrefix);
+	if (fileExists(urlBuf))
+	    url = hReplaceGbdb(urlBuf);
+	else
+	    {
+	    safef(urlBuf, sizeof urlBuf, "%s/hub.txt", path);
+	    url = hReplaceGbdb(urlBuf);
+	    }
+	}
 
     struct hubConnectStatus *status = getAndSetHubStatus( cart, url, TRUE);
 
     if (status && isEmpty(status->errorMessage))
 	{
 	char buffer[4096];
-        safef(buffer, sizeof buffer, "hub_%d_%s", status->id, db);
+	/* For GenArk-style hubs the hub.txt names the genome by accession
+	 * (e.g. GCF_036323735.1), not by the dbDb browser name (e.g. rn8).
+	 * Use the actual genome name from the fetched hub so that the cart
+	 * db matches what is registered in hubAssemblyHash.  The genome
+	 * name is already hub-decorated (hub_<id>_<genomeName>) at this
+	 * point, so use it directly.
+	  */
+	char *genomeName = db;
+	if (status->trackHub && status->trackHub->genomeList)
+           genomeName = trackHubSkipHubName(status->trackHub->genomeList->name);
+	safef(buffer, sizeof buffer, "hub_%d_%s", status->id, genomeName);
 
         cartSetString(cart, "db", buffer);
         if (cgiOptionalString("db"))
             {
             /* user specified db on URL, we need to decorate and put it back. */
             cgiVarSet("db",  cloneString(buffer));
             }
 
         return status->id;
         }
     else
         {
-        if (!isEmpty(status->errorMessage))
+        if (status && isNotEmpty(status->errorMessage))
             errAbort("Hub error: url %s: error  %s.", url, status->errorMessage);
         else
             errAbort("Cannot open hub %s.", url);
         }
 
     }
 return 0;
 }
 
 static void portHubStatus(struct cart *cart)
 /* When a session has been saved on a different host it may have cart variables that reference hubStatus id's
  * that are different than what the local machine has.  Look for these cases using the "assumesHub" cart variable
  * that maps id numbers to URL's. */
 {
 char *assumesHubStr = cartOptionalString(cart, "assumesHub");