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 */ {