da3772f3615ae3aa407af6aa2215a1b2ff480183
angie
  Mon Apr 29 13:40:41 2024 -0700
If configured, use nextclade to align sequences (better for more diverged viruses like influenza).  Also fix some buttons that the last change broke.

diff --git src/hg/hgPhyloPlace/phyloPlace.c src/hg/hgPhyloPlace/phyloPlace.c
index 28189b4..b3c2da1 100644
--- src/hg/hgPhyloPlace/phyloPlace.c
+++ src/hg/hgPhyloPlace/phyloPlace.c
@@ -295,31 +295,31 @@
     errAbort("Missing usher executable (expected to be at %s)", usherPath);
 return NULL;
 }
 
 char *getMatUtilsPath(boolean abortIfNotFound)
 /* Return hgPhyloPlaceData/matUtils if it exists, else NULL.  Do not free the returned value. */
 {
 char *matUtilsPath = PHYLOPLACE_DATA_DIR "/matUtils";
 if (fileExists(matUtilsPath))
     return matUtilsPath;
 else if (abortIfNotFound)
     errAbort("Missing matUtils executable (expected to be at %s)", matUtilsPath);
 return NULL;
 }
 
-static char *getNextcladePath()
+char *getNextcladePath()
 /* Return hgPhyloPlaceData/nextclade if it exists, else errAbort. Do not free the returned value. */
 {
 char *nextcladePath = PHYLOPLACE_DATA_DIR "/nextclade";
 if (fileExists(nextcladePath))
     return nextcladePath;
 else
     errAbort("Missing nextclade executable (expected to be at %s)", nextcladePath);
 return NULL;
 }
 
 //#*** This needs to go in a lib so CGIs know whether to include it in the menu. needs better name.
 boolean hgPhyloPlaceEnabled()
 /* Return TRUE if hgPhyloPlace is enabled in hg.conf and db wuhCor1 exists. */
 {
 char *cfgSetting = cfgOption("hgPhyloPlaceEnabled");
@@ -1477,65 +1477,69 @@
 struct dyString *dy = dyStringCreate("%s/MicrobeTrace/?url=", microbeTraceHost());
 return dyStringCannibalize(&dy);
 }
 
 static char *microbeTraceUrlFromTn(struct tempName *jsonTn)
 /* Return a link to MicrobeTrace to view an annotated subtree. */
 {
 char *jsonUrl = urlFromTn(jsonTn);
 char *urlBase = microbeTraceUrlBase();
 struct dyString *dy = dyStringCreate("%s%s", urlBase, jsonUrl);
 freeMem(jsonUrl);
 freeMem(urlBase);
 return dyStringCannibalize(&dy);
 }
 
-static void makeSubtreeDropdown(char *subtreeDropdownName, struct subtreeInfo *subtreeInfoList,
-                                struct tempName **jsonTns)
+static char *makeSubtreeDropdown(struct subtreeInfo *subtreeInfoList, struct tempName **jsonTns)
 /* Let user choose subtree to view */
 {
+static int serial = 0;
+char subtreeDropdownName[128];
+safef(subtreeDropdownName, sizeof subtreeDropdownName, "subtreeSelect%d", serial++);
 int count = slCount(subtreeInfoList);
 char *labels[count];
 char *values[count];
 struct subtreeInfo *ti;
 int ix;
 for (ix = 0, ti = subtreeInfoList;  ti != NULL;  ti = ti->next, ix++)
     {
     struct dyString *dy = dyStringCreate("subtree %d", ix+1);
     labels[ix] = dyStringCannibalize(&dy);
     values[ix] = urlFromTn(jsonTns[ix]);
     }
 cgiMakeDropListWithVals(subtreeDropdownName, labels, values, count, NULL);
 for (ix = 0;  ix < count;  ix++)
     {
     freeMem(labels[ix]);
     }
+return cloneString(subtreeDropdownName);
 }
 
 static void makeSubtreeJumpButton(char *subtreeDropdownName, char *dest, char *destUrlBase,
                                   char *destUrlParams, boolean skipProtocol)
 /* Make a button with javascript to get a JSON filename from a dropdown element, format a link
  * to dest, and jump to that dest when clicked. */
 {
+static int serial = 0;
 char *mouseover = "view selected subtree with your sequences and other sequences from the "
     "full phylogenetic tree for context";
 struct dyString *js = dyStringCreate("jsonUrl = document.querySelector('select[name=\"%s\"]').value;"
                                      "if (%d) { ix = jsonUrl.indexOf('://');"
                                      "          if (ix >= 0) { jsonUrl = jsonUrl.substr(ix+3); } }"
                                      "window.open('%s' + jsonUrl + '%s');",
                                      subtreeDropdownName, skipProtocol, destUrlBase, destUrlParams);
-struct dyString *id = dyStringCreate("jumpTo%s", dest);
+struct dyString *id = dyStringCreate("jumpTo%s_%d", dest, serial++);
 printf("<input type='button' id='%s' value='%s' title='%s' class='fullwidth'>",
        id->string, dest, mouseover);
 jsOnEventById("click", id->string, js->string);
 dyStringFree(&js);
 dyStringFree(&id);
 }
 
 static void makeButtonRow(struct tempName *singleSubtreeJsonTn, struct tempName *jsonTns[],
                           struct subtreeInfo *subtreeInfoList, int subtreeSize, boolean isFasta,
                           boolean offerCustomTrack)
 /* Russ's suggestion: row of buttons at the top to view results in GB, Nextstrain, Nextclade. */
 {
 puts("<p>");
 puts("<table class='invisalign'><tbody><tr>");
 if (offerCustomTrack)
@@ -1547,32 +1551,31 @@
     puts("</td>");
     }
 // SingleSubtree -- only for Nextstrain, not really applicable to MicrobeTrace
 if (nextstrainHost())
     {
     puts("<td>");
     makeNsSingleTreeButton(singleSubtreeJsonTn);
     puts("</td>");
     }
 
 // If both Nextstrain and MicrobeTrace are configured then make a subtree dropdown and buttons
 // to view in Nextstrain or MicrobeTrace
 if (nextstrainHost() && microbeTraceHost())
     {
     puts("<td>View subtree</td><td>");
-    char *subtreeDropdownName = "subtreeSelect";
-    makeSubtreeDropdown(subtreeDropdownName, subtreeInfoList, jsonTns);
+    char *subtreeDropdownName = makeSubtreeDropdown(subtreeInfoList, jsonTns);
     puts("</td><td>in</td><td>");
     makeSubtreeJumpButton(subtreeDropdownName, "Nextstrain", nextstrainUrlBase(),
                           NEXTSTRAIN_URL_PARAMS, TRUE);
     puts("<br>");
     if (subtreeSize <= MAX_MICROBETRACE_SUBTREE_SIZE)
         {
         makeSubtreeJumpButton(subtreeDropdownName, "MicrobeTrace", microbeTraceUrlBase(),
                               MICROBETRACE_URL_PARAMS, FALSE);
         }
     else
         {
         cgiMakeOptionalButton("disabledMTButton", "MicrobeTrace", TRUE);
         printf(" (%d samples is too many to view in MicrobeTrace; maximum is %d)",
                subtreeSize, MAX_MICROBETRACE_SUBTREE_SIZE);
         }
@@ -3378,30 +3381,35 @@
         }
     struct tempName *singleSubtreeJsonTn;
     AllocVar(singleSubtreeJsonTn);
     trashDirFile(singleSubtreeJsonTn, "ct", "singleSubtreeAuspice", ".json");
     treeToAuspiceJson(results->singleSubtreeInfo, org, refName, geneInfoList, gSeqWin, sampleMetadata,
                       sampleUrls, results->samplePlacements, singleSubtreeJsonTn->forCgi, source);
     reportTiming(&startTime, "make Auspice JSON");
     struct subtreeInfo *subtreeInfoForButtons = results->subtreeInfoList;
     if (subtreeCount > MAX_SUBTREE_BUTTONS)
         subtreeInfoForButtons = NULL;
     char *dbSetting = phyloPlaceRefSetting(org, refName, "db");
     if (dbSetting)
         db = connectIfHub(cart, dbSetting);
     boolean canDoCustomTracks = (!subtreesOnly &&
                                  (sameString(db, refName) || isNotEmpty(dbSetting)));
+    if (canDoCustomTracks)
+        // Form submits subtree custom tracks to hgTracks
+        printf("<form action='%s' name='resultsForm_%s' method=%s>\n\n",
+               hgTracksName(), db, cartUsualString(cart, "formMethod", "POST"));
+
     makeButtonRow(singleSubtreeJsonTn, jsonTns, subtreeInfoForButtons, subtreeSize, isFasta,
                   canDoCustomTracks);
     printf("<p>If you have metadata you wish to display, click a 'view subtree in "
            "Nextstrain' button, and then you can drag on a CSV file to "
            "<a href='"NEXTSTRAIN_DRAG_DROP_DOC"' target=_blank>add it to the tree view</a>."
            "</p>\n");
     puts("<p><em>Note: "
          "The Nextstrain subtree views, and Download files below, are temporary files and will "
          "expire within two days.  "
          "Please download the Nextstrain subtree JSON files if you will want to view them "
          "again in the future.  The JSON files can be drag-dropped onto "
          "<a href='https://auspice.us/' target=_blank>https://auspice.us/</a>."
          "</em></p>");
 
     struct tempName *tsvTn = NULL, *sTsvTn = NULL;
@@ -3517,33 +3525,30 @@
                 printf(", %s", sln->name);
             }
         puts(" (JSON file)</a>");
         }
     puts("</ul>");
 
     if (ctTn != NULL)
         {
         // Notify in opposite order of custom track creation.
         puts("<h3>Custom tracks for viewing in the Genome Browser</h3>");
         printf("<p>Added custom track of uploaded samples.</p>\n");
         if (subtreeCount > 0 && subtreeCount <= MAX_SUBTREE_CTS)
             printf("<p>Added %d subtree custom track%s.</p>\n",
                    subtreeCount, (subtreeCount > 1 ? "s" : ""));
         ctFile = urlFromTn(ctTn);
-        // Form submits subtree custom tracks to hgTracks
-        printf("<form action='%s' name='resultsForm_%s' method=%s>\n\n",
-               hgTracksName(), db, cartUsualString(cart, "formMethod", "POST"));
         cartSaveSession(cart);
         cgiMakeHiddenVar("db", db);
         cgiMakeHiddenVar(CT_CUSTOM_TEXT_VAR, ctFile);
         if (tl->leftLabelWidthChars < 0 || tl->leftLabelWidthChars == leftLabelWidthDefaultChars)
             cgiMakeHiddenVar(leftLabelWidthVar, leftLabelWidthForLongNames);
         cgiMakeButton("submit", "view in Genome Browser");
         puts("</form>");
         }
     }
 }
 
 static char *dumpLfToTrashFile(struct lineFile *lf)
 /* Dump the contents of lf to a trash file (for passing to an executable). Return trash file name. */
 {
 struct tempName tmp;