412456cf57b44d0747bfe632c7ea41f85554501d angie Fri Apr 14 09:56:57 2017 -0700 Add highlighting of HGVS mapped bases to distinguish them from padding bases. Also apply to other padded searches (e.g. SNPs). refs #19215 diff --git src/hg/lib/hgFind.c src/hg/lib/hgFind.c index 2567d04..a9234ab 100644 --- src/hg/lib/hgFind.c +++ src/hg/lib/hgFind.c @@ -2724,30 +2724,52 @@ { if (!isFuzzy || keyIsPrefixIgnoreCase(term, row[1])) { xrefPtr = slPairNew(cloneString(row[1]), cloneString(row[0])); slAddHead(&xrefList, xrefPtr); } } sqlFreeResult(&sr); hFreeConn(&conn); slReverse(&xrefList); if (xrefList == NULL && hgFindSpecSetting(hfs, "searchBoth") != NULL) xrefList = slPairNew(cloneString(""), cloneString(term)); return(xrefList); } +static void addHighlight(struct cart *cart, char *db, char *chrom, unsigned start, unsigned end) +/* Add the given region to the cart variable highlight. */ +{ +char *color = "fcfcac"; +struct dyString *dy = dyStringCreate("%s.%s:%u-%u#%s", db, chrom, start+1, end, color); +char *existing = cartOptionalString(cart, "highlight"); +if (isEmpty(existing)) + cartSetString(cart, "highlight", dyStringContents(dy)); +else + { + // Don't add region if it is already in the existing highlight setting. + char *alreadyIn = strstr(existing, dyStringContents(dy)); + if (!alreadyIn || + !(alreadyIn[dyStringLen(dy)] == '|' || alreadyIn[dyStringLen(dy)] == '\0')) + { + dyStringPrintf(dy, "|%s", existing); + cartSetString(cart, "highlight", dyStringContents(dy)); + } + } +dyStringFree(&dy); +} + static boolean doQuery(char *db, struct hgFindSpec *hfs, char *xrefTerm, char *term, struct hgPositions *hgp, boolean relativeFlag, int relStart, int relEnd, boolean multiTerm) /* Perform a query as specified in hfs, assuming table existence has been * checked and xref'ing has been taken care of. */ { struct slName *tableList = hSplitTableNames(db, hfs->searchTable); struct slName *tPtr = NULL; struct hgPosTable *table = NULL; struct hgPos *pos = NULL; struct sqlConnection *conn = hAllocConn(db); struct sqlResult *sr = NULL; char **row = NULL; char *termPrefix = hgFindSpecSetting(hfs, "termPrefix"); @@ -2801,30 +2823,32 @@ pos->name = cloneString(buf); pos->browserName = cloneString(row[3]); if (isNotEmpty(xrefTerm)) { safef(buf, sizeof(buf), "(%s%s)", termPrefix ? termPrefix : "", row[3]); pos->description = cloneString(buf); } if (relativeFlag && (pos->chromStart + relEnd) <= pos->chromEnd) { pos->chromEnd = pos->chromStart + relEnd; pos->chromStart = pos->chromStart + relStart; } else if (padding > 0 && !multiTerm) { + // highlight the item bases to distinguish from padding + addHighlight(cart, db, pos->chrom, pos->chromStart, pos->chromEnd); int chromSize = hChromSize(db, pos->chrom); pos->chromStart -= padding; pos->chromEnd += padding; if (pos->chromStart < 0) pos->chromStart = 0; if (pos->chromEnd > chromSize) pos->chromEnd = chromSize; } slAddHead(&table->posList, pos); } } if (table != NULL) slReverse(&table->posList); sqlFreeResult(&sr); @@ -3028,57 +3052,59 @@ hfs = hfsFind(longList, search); if (hfs != NULL) foundIt = hgFindUsingSpec(db, hfs, term, limitResults, hgp, FALSE, 0,0, FALSE); else warn("Unrecognized singleSearch=%s in URL", search); } if (foundIt) { fixSinglePos(hgp); if (cart != NULL) cartSetString(cart, "hgFind.matches", hgp->tableList->posList->browserName); } return foundIt; } -static boolean matchesHgvs(char *db, char *term, struct hgPositions *hgp) +static boolean matchesHgvs(struct cart *cart, char *db, char *term, struct hgPositions *hgp) /* Return TRUE if the search term looks like a variant encoded using the HGVS nomenclature */ /* See http://varnomen.hgvs.org/ */ { boolean foundIt = FALSE; struct hgvsVariant *hgvs = hgvsParseTerm(term); if (hgvs == NULL) hgvs = hgvsParsePseudoHgvs(db, term); if (hgvs) { struct dyString *dyWarn = dyStringNew(0); char *pslTable = NULL; struct bed *mapping = hgvsValidateAndMap(hgvs, db, term, dyWarn, &pslTable); if (dyStringLen(dyWarn) > 0) warn("%s", dyStringContents(dyWarn)); if (mapping) { int padding = 5; char *trackTable; if (isEmpty(pslTable)) trackTable = "chromInfo"; else if (startsWith("lrg", pslTable)) trackTable = "lrgTranscriptAli"; else trackTable = "refGene"; singlePos(hgp, "HGVS", NULL, trackTable, term, "", mapping->chrom, mapping->chromStart-padding, mapping->chromEnd+padding); + // highlight the mapped bases to distinguish from padding + addHighlight(cart, db, mapping->chrom, mapping->chromStart, mapping->chromEnd); foundIt = TRUE; } dyStringFree(&dyWarn); } return foundIt; } struct hgPositions *hgPositionsFind(char *db, char *term, char *extraCgi, char *hgAppNameIn, struct cart *cart, boolean multiTerm) /* Return table of positions that match term or NULL if none such. */ { struct hgPositions *hgp = NULL, *hgpItem = NULL; regmatch_t substrs[4]; boolean canonicalSpec = FALSE; boolean gbrowserSpec = FALSE; @@ -3163,31 +3189,31 @@ hgParseChromRange(db, term, &chrom, &start, &end); if (relativeFlag) { int chromSize = end; end = start + relEnd; start = start + relStart; if (end > chromSize) end = chromSize; if (start < 0) start = 0; } singlePos(hgp, "Chromosome Range", NULL, "chromInfo", originalTerm, "", chrom, start, end); } -else if (!matchesHgvs(db, term, hgp)) +else if (!matchesHgvs(cart, db, term, hgp)) { struct hgFindSpec *shortList = NULL, *longList = NULL; struct hgFindSpec *hfs; boolean done = FALSE; // Disable singleBaseSpec for any term that is not hgOfficialChromName // because that mangles legitimate IDs that are [A-Z]:[0-9]+. if (singleBaseSpec) { singleBaseSpec = relativeFlag = FALSE; term = cloneString(originalTerm); // restore original term relStart = relEnd = 0; } if (!trackHubDatabase(db))