81a04805893ab59c954464ac449abe65da05ac7f
hiram
  Mon Sep 21 16:28:30 2026 -0700
highlight NCBI "reference" assemblies in the search result and hg.conf gated with hgGateway.showRefBadge=on default is off refs #38401

diff --git src/hg/hgGateway/hgGateway.c src/hg/hgGateway/hgGateway.c
index 866bf218411..a0d73f49c51 100644
--- src/hg/hgGateway/hgGateway.c
+++ src/hg/hgGateway/hgGateway.c
@@ -769,43 +769,45 @@
 match->label = cloneString(label);
 return match;
 }
 
 // Genark  hub match:
 struct gHubMatch
     // description of an genark hub db
     {
     struct gHubMatch *next;
     char *gcAccession;
     char *hubUrl;
     char *asmName;
     char *scientificName;
     char *commonName;
     unsigned priority; // for ranking, currently unused
+    boolean isReference; // TRUE if assemblyList.refSeqCategory == 'reference'
     };
 
-static struct gHubMatch *gHubMatchNew(char *acc, char *hubUrl, char *asmName, char *scientificName, char *commonName, unsigned priority)
+static struct gHubMatch *gHubMatchNew(char *acc, char *hubUrl, char *asmName, char *scientificName, char *commonName, unsigned priority, boolean isReference)
 /* Allocate and return a description of an assembly hub db. */
 {
 struct gHubMatch *match;
 AllocVar(match);
 match->gcAccession = cloneString(acc);
 match->hubUrl = cloneString(hubUrl);
 match->asmName = cloneString(asmName);
 match->scientificName = cloneString(scientificName);
 match->commonName = cloneString(commonName);
 match->priority = priority;
+match->isReference = isReference;
 return match;
 }
 
 static struct hash *unpackHubDbUrlList(struct slName *hubDbUrlList, struct hash **labelHash)
 /* hubDbUrlList contains strings like "db\tlabel\thubUrl" -- split on tab and return a hash of
  * hubUrl to one or more dbs. */
 {
 struct hash *hubToDb = hashNew(0);
 struct hash *dbToLabel = hashNew(0);
 struct slName *hubDbUrl;
 for (hubDbUrl = hubDbUrlList;  hubDbUrl != NULL;  hubDbUrl = hubDbUrl->next)
     {
     char *tab = strchr(hubDbUrl->name, '\t');
     if (tab)
         {
@@ -1012,30 +1014,31 @@
         comBold = genarkBold(savePtr, searchWords[i]);
         freeMem(savePtr);
         savePtr = sciBold;
         sciBold = genarkBold(savePtr, searchWords[i]);
         freeMem(savePtr);
         savePtr = gcxBold;
         gcxBold = genarkBold(savePtr, searchWords[i]);
         freeMem(savePtr);
 	}
 
     jsonWriteObjectStart(jw, NULL);
     jsonWriteString(jw, "genome", gHubMatch->gcAccession);
     jsonWriteString(jw, "db", gHubMatch->asmName);
     jsonWriteString(jw, "hubUrl", gHubMatch->hubUrl);
     jsonWriteString(jw, "scientificName", gHubMatch->scientificName);
+    jsonWriteBoolean(jw, "isReference", gHubMatch->isReference);
     // Add a category label for customized autocomplete-with-categories.
     jsonWriteString(jw, "category", "UCSC GenArk - bulk-annotated assemblies from NCBI Genbank/RefSeq");
     jsonWriteString(jw, "value", gHubMatch->asmName);
     // Use just the db as label, since shortLabel is included in the category label.
     jsonWriteStringf(jw, "label", "%s - %s (%s)", comBold, sciBold, gcxBold);
     jsonWriteObjectEnd(jw);
     }
 }
 
 /* maximum limit of how many matches to display from genark */
 #define GENARK_LIMIT 20
 
 static char *devAlphaHubTxt(char *hubUrl)
 /* On hgwdev, genome-test and hgwdev sandboxes, GenArk hubs are staged under
  * alpha.hub.txt instead of hub.txt.  On those hosts, swap the trailing
@@ -1049,31 +1052,31 @@
 
 static struct gHubMatch *filterGenarkMatches(char *genarkHubUrl, struct genark *matchList)
 /* Turn the sql results into a struct gHubMatch list */
 {
 struct genark *match;
 struct gHubMatch *ret = NULL;
 
 int c = 0;
 for (match = matchList; match != NULL; match = match->next)
     {
     ++c;
     // the match contains tab-sep accession, hubUrl, asmName, scientificName, commonName
     char hubUrl[PATH_LEN+1];
     safef(hubUrl, sizeof(hubUrl), "%s/%s", genarkHubUrl, match->hubUrl);
     char *finalHubUrl = devAlphaHubTxt(hubUrl);
-    slAddHead(&ret, gHubMatchNew(match->gcAccession, finalHubUrl, match->asmName, match->scientificName, match->commonName, -1));
+    slAddHead(&ret, gHubMatchNew(match->gcAccession, finalHubUrl, match->asmName, match->scientificName, match->commonName, -1, FALSE));
     freeMem(finalHubUrl);
     if (c > GENARK_LIMIT)
 	break;
     }
 if (ret)
     slReverse(&ret);
 return ret;
 }
 
 static struct gHubMatch *filterAssemblyListMatches(struct sqlConnection *conn,
    struct dbDb *dbDbList, struct dbDbMatch **dbDbMatchList, char *asmListTable, char *term, char *genarkPrefix, boolean wildCard)
 {
 struct gHubMatch *ret = NULL;
 struct dyString *query = dyStringNew(64);
 /* LIMIT of 100 will allow enough results to include some genArk assemblies */
@@ -1083,31 +1086,38 @@
     sqlDyStringPrintf(query, "SELECT * FROM %s WHERE MATCH(name, commonName, scientificName, clade, description, refSeqCategory, versionStatus, assemblyLevel) AGAINST ('%s' IN BOOLEAN MODE) AND browserExists=1 ORDER BY priority LIMIT 100", asmListTable, term);
 
 struct sqlResult *sr = sqlGetResult(conn, query->string);
 dyStringFree(&query);
 char **row;
 int c = 0;
 while ((row = sqlNextRow(sr)) != NULL)
     {
     struct assemblyList *el = assemblyListLoadWithNull(row);
     if (isGenArk(el->name))
 	{
 	++c;
 	char genarkUrl[PATH_MAX];
 	safef(genarkUrl, sizeof(genarkUrl), "%s/%s", genarkPrefix, el->hubUrl);
 	char *finalHubUrl = devAlphaHubTxt(genarkUrl);
-	slAddHead(&ret, gHubMatchNew(el->name, finalHubUrl, NULL, el->scientificName, el->commonName, *el->priority));
+	boolean isReference = FALSE;
+	if (cfgOptionBooleanDefault("hgGateway.showRefBadge", FALSE) && isNotEmpty(el->refSeqCategory))
+	    {
+	    char rscBuf[64];
+	    safecpy(rscBuf, sizeof(rscBuf), el->refSeqCategory);
+	    isReference = sameWord(trimSpaces(rscBuf), "reference");
+	    }
+	slAddHead(&ret, gHubMatchNew(el->name, finalHubUrl, NULL, el->scientificName, el->commonName, *el->priority, isReference));
 	freeMem(finalHubUrl);
 	}
 	else
 	{
         struct dbDb *dbDb;
         for (dbDb = dbDbList;  dbDb != NULL; dbDb = dbDb->next)
 	    {
             if (sameWord(dbDb->name, el->name))
 		{
 		// Make uppercase version of target for case-insensitive matching.
 		int targetLen = strlen(el->name);
 		char targetUpcase[targetLen + 1];
 		safencpy(targetUpcase, sizeof(targetUpcase), el->name, targetLen);
 		touppers(targetUpcase);
                 struct dbDbMatch *extraList = searchDbDb(dbDbList, targetUpcase);