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>&nbsp</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");