fbdc691703e4bc2d98fcdb2014fe6fa19771c104 braney Fri Feb 10 17:05:10 2017 -0800 make building a custom track with blat results an option on the hgBlat page. Add some features to bigPsl click support to get the same kind of UI that hgBlat provides. diff --git src/hg/hgBlat/hgBlat.c src/hg/hgBlat/hgBlat.c index fbb2ec7..829dcba 100644 --- src/hg/hgBlat/hgBlat.c +++ src/hg/hgBlat/hgBlat.c @@ -32,31 +32,30 @@ boolean orgChange = FALSE; boolean dbChange = FALSE; struct serverTable /* Information on a server. */ { char *db; /* Database name. */ char *genome; /* Genome name. */ boolean isTrans; /* Is tranlated to protein? */ char *host; /* Name of machine hosting server. */ char *port; /* Port that hosts server. */ char *nibDir; /* Directory of sequence files. */ }; char *typeList[] = {"BLAT's guess", "DNA", "protein", "translated RNA", "translated DNA"}; -char *sortList[] = {"query,score", "query,start", "chrom,score", "chrom,start", "score"}; char *outputList[] = {"hyperlink", "psl", "psl no header"}; #ifdef LOWELAB int minMatchShown = 14; #else int minMatchShown = 20; #endif static struct serverTable *trackHubServerTable(char *db, boolean isTrans) /* Find out if database is a track hub with a blat server */ { char *host, *port; if (!trackHubGetBlatParams(db, isTrans, &host, &port)) return NULL; @@ -203,214 +202,148 @@ ++same; } return same; } boolean allDigits(char *s) /* Return TRUE if s is all digits */ { char c; while ((c = *s++) != 0) if (!isdigit(c)) return FALSE; return TRUE; } -int cmpChrom(char *a, char *b) -/* Compare two chromosomes. */ -{ -return cmpStringsWithEmbeddedNumbers(a, b); -} - -int pslCmpTargetScore(const void *va, const void *vb) -/* Compare to sort based on target then score. */ -{ -const struct psl *a = *((struct psl **)va); -const struct psl *b = *((struct psl **)vb); -int diff = cmpChrom(a->tName, b->tName); -if (diff == 0) - diff = pslScore(b) - pslScore(a); -return diff; -} - -int pslCmpTargetStart(const void *va, const void *vb) -/* Compare to sort based on target start. */ -{ -const struct psl *a = *((struct psl **)va); -const struct psl *b = *((struct psl **)vb); -int diff = cmpChrom(a->tName, b->tName); -if (diff == 0) - diff = a->tStart - b->tStart; -return diff; -} - void printLuckyRedirect(char *browserUrl, struct psl *psl, char *database, char *pslName, char *faName, char *uiState, char *unhideTrack) /* Print out a very short page that redirects us. */ { char url[1024]; safef(url, sizeof(url), "%s?position=%s:%d-%d&db=%s&ss=%s+%s&%s%s", browserUrl, psl->tName, psl->tStart + 1, psl->tEnd, database, pslName, faName, uiState, unhideTrack); /* htmlStart("Redirecting"); */ /* Odd it appears that we've already printed the Content-Typ:text/html line but I can't figure out where... */ htmStart(stdout, "Redirecting"); char javascript[1024]; safef(javascript, sizeof javascript, "location.replace('%s');", url); jsInline(javascript); printf("<noscript>No javascript support:<br>Click <a href='%s'>here</a> for browser.</noscript>", url); htmlEnd(); } -static char *replaceSuffix(char *input, char *newSuffix) -/* Given a filename with a suffix, replace existing suffix with a new suffix. */ -{ -char buffer[4096]; -safecpy(buffer, sizeof buffer, input); -char *dot = strrchr(buffer, '.'); -safecpy(dot+1, sizeof buffer - 1 - (dot - buffer), newSuffix); -return cloneString(buffer); -} -static void makeBigPsl(char *pslName, char *faName, char *db, char *outputBigBed) -/* Make a bigPsl with the blat results. */ -{ -char *bigPslFile = replaceSuffix(outputBigBed, "bigPsl"); - -char cmdBuffer[4096]; -safef(cmdBuffer, sizeof(cmdBuffer), "loader/pslToBigPsl %s -fa=%s stdout | sort -k1,1 -k2,2n > %s", pslName, faName, bigPslFile); -system(cmdBuffer); -char buf[4096]; -char *twoBitDir; -if (trackHubDatabase(db)) - { - struct trackHubGenome *genome = trackHubGetGenome(db); - twoBitDir = genome->twoBitPath; - } -else - { - safef(buf, sizeof(buf), "/gbdb/%s", db); - twoBitDir = hReplaceGbdbSeqDir(buf, db); - safef(buf, sizeof(buf), "%s%s.2bit", twoBitDir, db); - twoBitDir = buf; - } - -safef(cmdBuffer, sizeof(cmdBuffer), "loader/bedToBigBed -verbose=0 -udcDir=%s -extraIndex=name -sizesIs2Bit -tab -as=loader/bigPsl.as -type=bed9+16 %s %s %s", - udcDefaultDir(), bigPslFile, twoBitDir, outputBigBed); -system(cmdBuffer); -unlink(bigPslFile); -} +/* forward declaration to reduce churn */ +static void getCustomName(char *database, struct cart *cart, struct psl *psl, char **pName, char **pDescription); void showAliPlaces(char *pslName, char *faName, char *customText, char *database, enum gfType qType, enum gfType tType, char *organism, boolean feelingLucky) /* Show all the places that align. */ { +boolean useBigPsl = cfgOptionBooleanDefault("useBlatBigPsl", FALSE); struct lineFile *lf = pslFileOpen(pslName); struct psl *pslList = NULL, *psl; char *browserUrl = hgTracksName(); char *hgcUrl = hgcName(); char uiState[64]; char *vis; char unhideTrack[64]; -char *sort = cartUsualString(cart, "sort", sortList[0]); +char *sort = cartUsualString(cart, "sort", pslSortList[0]); char *output = cartUsualString(cart, "output", outputList[0]); boolean pslOut = startsWith("psl", output); boolean isStraightNuc = (qType == gftRna || qType == gftDna); int minThreshold = (isStraightNuc ? minMatchShown : 0); sprintf(uiState, "%s=%s", cartSessionVarName(), cartSessionId(cart)); /* If user has hidden BLAT track, add a setting that will unhide the track if user clicks on a browser link. */ vis = cartOptionalString(cart, "hgUserPsl"); if (vis != NULL && sameString(vis, "hide")) snprintf(unhideTrack, sizeof(unhideTrack), "&hgUserPsl=dense"); else unhideTrack[0] = 0; while ((psl = pslNext(lf)) != NULL) { if (psl->match >= minThreshold) slAddHead(&pslList, psl); } lineFileClose(&lf); if (pslList == NULL) { puts("<table><tr><td><hr>Sorry, no matches found<hr><td></tr></table>"); return; } -if (sameString(sort, "query,start")) - { - slSort(&pslList, pslCmpQuery); - } -else if (sameString(sort, "query,score")) - { - slSort(&pslList, pslCmpQueryScore); - } -else if (sameString(sort, "score")) - { - slSort(&pslList, pslCmpScore); - } -else if (sameString(sort, "chrom,start")) - { - slSort(&pslList, pslCmpTargetStart); - } -else if (sameString(sort, "chrom,score")) - { - slSort(&pslList, pslCmpTargetScore); - } -else - { - slSort(&pslList, pslCmpQueryScore); - } +pslSortListByVar(&pslList, sort); + if(feelingLucky) { /* If we found something jump browser to there. */ if(slCount(pslList) > 0) printLuckyRedirect(browserUrl, pslList, database, pslName, faName, uiState, unhideTrack); /* Otherwise call ourselves again not feeling lucky to print empty results. */ else { cartWebStart(cart, database, "%s BLAT Results", trackHubSkipHubName(organism)); showAliPlaces(pslName, faName, customText, database, qType, tType, organism, FALSE); cartWebEnd(); } } else if (pslOut) { printf("<TT><PRE>"); if (!sameString(output, "psl no header")) pslxWriteHead(stdout, qType, tType); for (psl = pslList; psl != NULL; psl = psl->next) pslTabOut(psl, stdout); printf("</PRE></TT>"); } else { printf("<H2>BLAT Search Results</H2>"); char* posStr = cartOptionalString(cart, "position"); if (posStr != NULL) printf("<P>Go back to <A HREF=\"%s\">%s</A> on the Genome Browser.</P>\n", browserUrl, posStr); + if (useBigPsl) + { + char *trackName = NULL; + char *trackDescription = NULL; + getCustomName(database, cart, pslList, &trackName, &trackDescription); + psl = pslList; + printf("<A HREF=\"%s?o=%d&t=%d&trackName=%s&trackDescription=%s&g=buildBigPsl&i=%s+%s+%s&c=%s&l=%d&r=%d&db=%s&%s\">", + hgcUrl, psl->tStart, psl->tEnd,cgiEncode(trackName), cgiEncode(trackDescription), pslName, cgiEncode(faName), psl->qName, psl->tName, + psl->tStart, psl->tEnd, database, uiState); + + //printf( + //"<FORM ACTION=\"../cgi-bin/hgc\" METHOD=\"GET\" NAME=\"mainForm\">\n"); + printf("<P>Build a custom track with these results. Track will be called %s </A>", trackDescription); + //printf("Description: %s\n", trackDescription); + //printf("<INPUT TYPE=SUBMIT NAME=Submit VALUE=\"Do It\">\n"); + //printf("</FORM>"); + } + printf("<DIV STYLE=\"display:block; float:left\"><TT><PRE>"); printf(" ACTIONS QUERY SCORE START END QSIZE IDENTITY CHRO STRAND START END SPAN\n"); printf("---------------------------------------------------------------------------------------------------\n"); for (psl = pslList; psl != NULL; psl = psl->next) { if (customText) printf("<A HREF=\"%s?position=%s:%d-%d&db=%s&hgt.customText=%s&%s%s\">", browserUrl, psl->tName, psl->tStart + 1, psl->tEnd, database, customText, uiState, unhideTrack); else printf("<A HREF=\"%s?position=%s:%d-%d&db=%s&ss=%s+%s&%s%s\">", browserUrl, psl->tName, psl->tStart + 1, psl->tEnd, database, pslName, faName, uiState, unhideTrack); printf("browser</A> "); printf("<A HREF=\"%s?o=%d&g=htcUserAli&i=%s+%s+%s&c=%s&l=%d&r=%d&db=%s&%s\">", @@ -605,68 +538,33 @@ else if (count == 2) { safef(shortName, sizeof shortName, "blat %s+%d", names->name, count - 1); safef(description, sizeof description, "blat on %d queries (%s, %s)", count, names->name, names->next->name); } else { safef(shortName, sizeof shortName, "blat %s+%d", names->name, count - 1); safef(description, sizeof description, "blat on %d queries (%s, %s, ...)", count, names->name, names->next->name); } *pName = makeNameUnique(shortName, database, cart); *pDescription = cloneString(description); } -static char *outBigPsl(char *db, struct psl *pslList, char *pslFilename, char *faFilename, boolean isProt) -// Make a bigPsl from a list of psls and return its name. -{ -struct tempName bigBedTn; -trashDirDateFile(&bigBedTn, "hgBlat", "bp", ".bb"); -char *bigBedFile = bigBedTn.forCgi; -makeBigPsl(pslFilename, faFilename, db, bigBedFile); -char *customTextFile = replaceSuffix(bigBedFile, "txt"); -FILE *fp = fopen(customTextFile, "w"); -char* host = getenv("HTTP_HOST"); -char* reqUrl = getenv("REQUEST_URI"); -// remove everything after / in URL -char *e = strrchr(reqUrl, '/'); -if (e) *e = 0; - -char *trackName = NULL; -char *trackDescription = NULL; - -getCustomName(db, cart, pslList, &trackName, &trackDescription); -char *customTextTemplate = "track type=bigPsl indelDoubleInsert=on indelQueryInsert=on pslFile=%s visibility=pack showAll=on htmlUrl=http://%s/goldenPath/help/hgUserPsl.html %s bigDataUrl=http://%s%s/%s name=\"%s\" description=\"%s\"\n"; -char *extraForMismatch = "showDiffBasesAllScales=. baseColorUseSequence=lfExtra baseColorDefault=diffBases"; - -if (isProt) - extraForMismatch = ""; -fprintf(fp, customTextTemplate, bigBedTn.forCgi, host, extraForMismatch, host, reqUrl, bigBedTn.forCgi, trackName, trackDescription); -fclose(fp); - -char buffer[4096]; -safef(buffer, sizeof buffer, "http://%s%s/%s", host, reqUrl, customTextFile); - -return cloneString(buffer); -} - void blatSeq(char *userSeq, char *organism, char *database) /* Blat sequence user pasted in. */ { -boolean doHyper = sameString(cartUsualString(cart, "output", outputList[0]), "hyperlink");; -boolean useBigPsl = cfgOptionBooleanDefault("useBlatBigPsl", FALSE) && doHyper; FILE *f; struct dnaSeq *seqList = NULL, *seq; struct tempName pslTn, faTn; int maxSingleSize, maxTotalSize, maxSeqCount; int minSingleSize = minMatchShown; char *genome, *db; char *type = cgiString("type"); char *seqLetters = cloneString(userSeq); struct serverTable *serve; int conn; int oneSize, totalSize = 0, seqCount = 0; boolean isTx = FALSE; boolean isTxTx = FALSE; boolean txTxBoth = FALSE; struct gfOutput *gvo; @@ -842,48 +740,32 @@ { gfAlignTrans(&conn, serve->nibDir, seq, 5, tFileCache, gvo); } } else { gfAlignStrand(&conn, serve->nibDir, seq, FALSE, minMatchShown, tFileCache, gvo); reverseComplement(seq->dna, seq->size); conn = gfConnect(serve->host, serve->port); gfAlignStrand(&conn, serve->nibDir, seq, TRUE, minMatchShown, tFileCache, gvo); } gfOutputQuery(gvo, f); } carefulClose(&f); -if (useBigPsl) - { - struct psl *pslList = pslLoadAll(pslTn.forCgi); - - if (pslList != NULL) - { - char *customTrack = outBigPsl(database, pslList, pslTn.forCgi, faTn.forCgi, qIsProt); - showAliPlaces(pslTn.forCgi, faTn.forCgi, customTrack, serve->db, qType, tType, - organism, feelingLucky); - } - else - puts("<table><tr><td><hr>Sorry, no matches found<hr><td></tr></table>"); - } -else - { showAliPlaces(pslTn.forCgi, faTn.forCgi, NULL, serve->db, qType, tType, organism, feelingLucky); - } if(!feelingLucky) cartWebEnd(); gfFileCacheFree(&tFileCache); } void askForSeq(char *organism, char *db) /* Put up a little form that asks for sequence. * Call self.... */ { /* ignore struct serverTable* return, but can error out if not found */ findServer(db, FALSE); /* JavaScript to update form when org changes */ char *onChangeText = "" "document.mainForm.changeInfo.value='orgChange';" @@ -903,31 +785,31 @@ printf("<TD ALIGN=CENTER>Query type:</TD>"); printf("<TD ALIGN=CENTER>Sort output:</TD>"); printf("<TD ALIGN=CENTER>Output type:</TD>"); printf("<TD ALIGN=CENTER> </TD>"); printf("</TR>\n<TR>\n"); printf("<TD ALIGN=CENTER>\n"); printBlatGenomeListHtml(db, "change", onChangeText); printf("</TD>\n"); printf("<TD ALIGN=CENTER>\n"); printBlatAssemblyListHtml(db); printf("</TD>\n"); printf("<TD ALIGN=CENTER>\n"); cgiMakeDropList("type", typeList, ArraySize(typeList), NULL); printf("</TD>\n"); printf("<TD ALIGN=CENTER>\n"); -cgiMakeDropList("sort", sortList, ArraySize(sortList), cartOptionalString(cart, "sort")); +cgiMakeDropList("sort", pslSortList, ArraySize(pslSortList), cartOptionalString(cart, "sort")); printf("</TD>\n"); printf("<TD ALIGN=CENTER>\n"); cgiMakeDropList("output", outputList, ArraySize(outputList), cartOptionalString(cart, "output")); printf("</TD>\n"); printf("</TR>\n<TR>\n"); userSeq = cartUsualString(cart, "userSeq", ""); printf("<TD COLSPAN=5 ALIGN=CENTER>\n"); htmlPrintf("<TEXTAREA NAME=userSeq ROWS=14 COLS=80>%s</TEXTAREA>\n", userSeq); printf("</TD>\n"); printf("</TR>\n<TR>\n"); printf("<TD COLSPAN=5 ALIGN=CENTER>\n"); printf("<INPUT TYPE=SUBMIT NAME=Submit VALUE=submit>\n"); printf("<INPUT TYPE=SUBMIT NAME=Lucky VALUE=\"I'm feeling lucky\">\n"); printf("<INPUT TYPE=SUBMIT NAME=Clear VALUE=clear>\n"); printf("</TD>\n");