324020eced2a302856ea0cbec8942b374337a3f8
chmalee
  Mon Oct 31 10:02:26 2022 -0700
When a user has entered a search term that resolves to a single position, such as an empty chromosome name or HGVS term, have the client redirect straight to hgTracks, refs #29693

diff --git src/hg/hgSearch/hgSearch.c src/hg/hgSearch/hgSearch.c
index 4d669de..df50800 100644
--- src/hg/hgSearch/hgSearch.c
+++ src/hg/hgSearch/hgSearch.c
@@ -145,67 +145,75 @@
 cartTrackDbInit(cart, &hgFindTdbList, &hgFindGrpList, FALSE);
 if (!hgFindTdbList)
     errAbort("Error getting tracks for %s", db);
 if (!hgFindGrpList)
     errAbort("Error getting groups for %s", db);
 struct trackDb *superList = addSupers(hgFindTdbList);
 hgFindTdbList = superList;
 hashTdbNames(superList, hgFindTrackHash);
 struct grp *g;
 for (g = hgFindGrpList; g != NULL; g = g->next)
     if (!sameString(g->name, "allTracks") && !sameString(g->name, "allTables"))
         hashStore(hgFindGroupHash, g->name);
 }
 
 
-void doQuery(struct jsonWrite *jw, char *db, struct searchCategory *categories, char *searchTerms, boolean measureTiming)
+struct hgPositions *doQuery(struct jsonWrite *jw, char *db, struct searchCategory *categories, boolean doRedirect, char *searchTerms, boolean measureTiming)
 /* Fire off a query. If the query results in a single position and we came from another CGI,
- * redirect right back to that CGI (except if we came from hgGateway redirect to hgTracks),
- * otherwise return the position list as JSON */
+ * inform the client that we can redirect right to hgTracks, otherwise write the
+ * list as JSON. Return the results if we want to handle the redirect server
+ * side later. */
 {
 struct hgPositions *hgp = NULL;
 char *chrom;
 int retWinStart = 0, retWinEnd = 0;
 boolean categorySearch = TRUE;
 hgp = genomePosCJ(jw, db, searchTerms, &chrom, &retWinStart, &retWinEnd, cart, categories, categorySearch);
 // at this point, if the search term wasn't a singlePos, we have written
-// out the JSON already. So now take care of the singlePos case
+// out the JSON already. So now take care of the singlePos case.
 if (hgp && hgp->singlePos)
     {
     // if we got an hgvs match to chromInfo (example: chrX:g.31500000_31600000del),
     // or just a plain position range was searched, we have to create the json
     // manually, cause the tdb lookups in hgPositionsJson() won't work
     struct hgPosTable *table = hgp->tableList;
     jsonWriteListStart(jw, "positionMatches");
     jsonWriteObjectStart(jw, NULL);
     jsonWriteString(jw, "name", table->name);
     jsonWriteString(jw, "description", table->description);
     if (table->searchTime >= 0)
         jsonWriteNumber(jw, "searchTime", table->searchTime);
     jsonWriteListStart(jw, "matches");
 
     jsonWriteObjectStart(jw, NULL);
     char position[512];
     safef(position, sizeof(position), "%s:%d-%d", hgp->singlePos->chrom, retWinStart, retWinEnd);
     jsonWriteString(jw, "position", position);
     jsonWriteString(jw, "posName", hgp->query);
+    if (doRedirect)
+        {
+        // If we are coming from hgTracks or hgGateway, then we can just return right
+        // back to hgTracks, the client will handle this:
+        jsonWriteBoolean(jw, "doRedirect", doRedirect);
+        }
     jsonWriteObjectEnd(jw);
 
     jsonWriteListEnd(jw); // end matches
     jsonWriteObjectEnd(jw); // end one table
     jsonWriteListEnd(jw); // end positionMatches
     }
+return hgp;
 }
 
 static void addCategoryFieldsToHash(struct hash *elementHash, struct searchCategory *category)
 {
 hashAdd(elementHash, "name", newJsonString(category->name));
 hashAdd(elementHash, "id", newJsonString(category->id));
 hashAdd(elementHash, "visibility", newJsonNumber((long)category->visibility));
 hashAdd(elementHash, "group", newJsonString(category->groupName));
 hashAdd(elementHash, "label", newJsonString(category->label));
 hashAdd(elementHash, "description", newJsonString(category->description));
 hashAdd(elementHash, "parents", newJsonString(slNameListToString(category->parents, ',')));
 hashAdd(elementHash, "priority", newJsonDouble(category->priority));
 }
 
 static boolean nonTrackCateg(struct searchCategory *categ)
@@ -406,31 +414,32 @@
 
 /* End handler helper functions */
 
 /* Handlers for returning JSON to client */
 static void getSearchResults(struct cartJson *cj, struct hash *paramHash)
 /* User has entered a term to search, search this term against the selected categories */
 {
 char *db = cartJsonRequiredParam(paramHash, "db", cj->jw, "getSearchResults");
 cartSetString(cj->cart, "db", db);
 initGenbankTableNames(db);
 hashTracksAndGroups(cj->cart, db);
 char *searchTerms = cartJsonRequiredParam(paramHash, SEARCH_TERM_VAR, cj->jw, "getSearchResults");
 measureTiming = cartUsualBoolean(cj->cart, "measureTiming", FALSE);
 struct jsonElement *searchCategs = hashFindVal(paramHash, "categs");
 struct searchCategory *searchCategoryList = makeCategsFromJson(searchCategs, db);
-doQuery(cj->jw, db, searchCategoryList, searchTerms, measureTiming);
+boolean doRedirect = FALSE;
+(void)doQuery(cj->jw, db, searchCategoryList, doRedirect, searchTerms, measureTiming);
 fprintf(stderr, "performed query on %s\n", searchTerms);
 }
 
 static void getUiState(struct cartJson *cj, struct hash *paramHash)
 /* We haven't seen this database before, return list of all searchable stuff */
 {
 char *db = cartJsonRequiredParam(paramHash, "db", cj->jw, "getUiState");
 cartSetString(cj->cart, "db", db);
 initGenbankTableNames(db);
 hashTracksAndGroups(cj->cart, db);
 writeDefaultForDb(cj->jw, db);
 }
 
 static struct jsonElement *getGenomes()
 /* Return a string that the javascript can use to put up a species and db select. */
@@ -554,41 +563,65 @@
 char *db = NULL;
 char *genome = NULL;
 getDbAndGenome(cart, &db, &genome, oldVars);
 char *userSearch = cartCgiUsualString(cart, "search", NULL);
 if (userSearch == NULL || isEmpty(userSearch))
     {
     doMainPage();
     return;
     }
 initGenbankTableNames(db);
 hashTracksAndGroups(cart, db);
 struct searchCategory *allCategories = getAllCategories(cart, db, hgFindGroupHash);
 struct jsonElement *categsJsonElement = jsonElementFromSearchCategory(allCategories, db);
 
 struct cartJson *cj = cartJsonNew(cart);
-dyStringPrintf(cj->jw->dy, "\"db\": '%s'", db);
+// since we are coming directly from hgTracks/hgGateway (or a URL manipulation),
+// if the search would normally be a singlePos, like a chromosome name or HGVS search,
+// we can just go directly there. But if we aren't a singlePos, we need to show
+// the results page
+boolean doRedirect = TRUE;
+measureTiming = cartUsualBoolean(cart, "measureTiming", FALSE);
+(void)doQuery(cj->jw, db, allCategories, doRedirect, userSearch, measureTiming);
+/* This is an option to do an automatic redirect but I'm commenting it
+ * out for now, as this page suggests the refresh option is not helpful
+ * for visually impaired users:
+ * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta
+ * For now the client javascript can handle the redirect
+if (hgp && hgp->singlePos)
+    {
+    char newPosBuf[128];
+    safef(newPosBuf, sizeof(newPosBuf), "%s:%d-%d", hgp->singlePos->chrom, hgp->singlePos->chromStart+1, hgp->singlePos->chromEnd);
+    cartSetString(cj->cart, "position", newPosBuf);
+    if (hgp->singlePos->highlight)
+        cartSetString(cj->cart, "addHighlight", hgp->singlePos->highlight);
+    puts("Content-type:text/html\n");
+    puts("<HTML>\n<HEAD>\n");
+    hPrintf("<META HTTP-EQUIV=refresh CONTENT=\"1;URL="
+            "../cgi-bin/hgTracks?db=%s&position=%s\">\n", db, newPosBuf);
+    puts("</HEAD>\n</HTML>");
+    }
+else
+*/
+dyStringPrintf(cj->jw->dy, ", \"db\": '%s'", db);
 dyStringPrintf(cj->jw->dy, ", \"categs\": ");
 jsonDyStringPrint(cj->jw->dy, categsJsonElement, NULL,-1);
 dyStringPrintf(cj->jw->dy, ", \"trackGroups\": ");
 jsonDyStringPrint(cj->jw->dy, makeTrackGroupsJson(db), NULL, -1);
 dyStringPrintf(cj->jw->dy, ", \"genomes\": ");
 jsonDyStringPrint(cj->jw->dy, getGenomes(), NULL, -1);
-dyStringPrintf(cj->jw->dy, ", ");
 
-measureTiming = cartUsualBoolean(cart, "measureTiming", FALSE);
-doQuery(cj->jw, db, allCategories, userSearch, measureTiming);
 // Now we need to actually spit out the page + json
 webStartGbNoBanner(cart, db, "Search Disambiguation");
 printMainPageIncludes();
 jsInlineF("var hgsid='%s';\n", cartSessionId(cart));
 jsInline("var cartJson = {");
 jsInline(cj->jw->dy->string);
 jsInline("};\n");
 jsInline("hgSearch.init();\n");
 webEndGb();
 }
 /* End do commands */
 
 void doMiddle(struct cart *theCart)
 /* Set up globals and make web page */
 {