7e58340888377874edaad1dbc5174e20295f890c angie Mon Feb 22 14:17:33 2021 -0800 Support upload of more sequences, add TSV file summarizing sample variants and placements. Requested by Joe de Risi (UCSF). Increase timeout to 10 minutes; make TSV with each sample's ID, nuc muts, AA muts, imputed bases and path from root to sample. Also use Yatish's new -K subtree algorithm in usher: one subtree encompassing all uploaded samples, plus the specified number of samples randomly selected from the rest of the tree. Don't show every single sample name in the title because there can be 1000 samples in the same subtree now. :) diff --git src/hg/hgPhyloPlace/hgPhyloPlace.c src/hg/hgPhyloPlace/hgPhyloPlace.c index d2485e7..1c5c27c 100644 --- src/hg/hgPhyloPlace/hgPhyloPlace.c +++ src/hg/hgPhyloPlace/hgPhyloPlace.c @@ -1,22 +1,23 @@ /* hgPhyloPlace - Upload SARS-CoV-2 sequence for placement in phylo tree. */ /* Copyright (C) 2020 The Regents of the University of California */ #include "common.h" #include "botDelay.h" #include "cart.h" +#include "cgiApoptosis.h" #include "cheapcgi.h" #include "hCommon.h" #include "hash.h" #include "hui.h" #include "jsHelper.h" #include "knetUdc.h" #include "linefile.h" #include "net.h" #include "options.h" #include "phyloPlace.h" #include "portable.h" #include "trackLayout.h" #include "udc.h" #include "web.h" @@ -141,31 +142,31 @@ } static void newPageEndStuff() { puts( "</div>"); jsIncludeFile("utils.js", NULL); webIncludeFile("inc/gbFooter.html"); webEndJWest(); } #define CHECK_FILE_INPUT_JS "{ var $fileInput = $('input[name="seqFileVar"]'); " \ "if ($fileInput && $fileInput[0] && $fileInput[0].files && !$fileInput[0].files.length) {" \ " alert('Please choose a file first, then click the upload button.');" \ " return false; " \ - "} else { return true; } }" + "} else { loadingImage.run(); return true; } }" static void inputForm() /* Ask the user for FASTA or VCF. */ { printf("<form action='%s' name='mainForm' method=POST enctype='multipart/form-data'>\n\n", "hgPhyloPlace"); cartSaveSession(cart); char *db = "wuhCor1"; cgiMakeHiddenVar("db", db); puts(" <div class='gbControl col-md-12'>"); puts("<div class='readableWidth'>"); puts("<p>Upload your SARS-CoV-2 sequence (FASTA or VCF file) to find the most similar\n" "complete, high-coverage samples from \n" "<a href='https://www.gisaid.org/' target='_blank'>GISAID</a>\n" "or from public sequence databases (" @@ -214,34 +215,43 @@ seqFileVar, seqFileVar); struct treeChoices *treeChoices = loadTreeChoices(db); if (treeChoices) { puts("</p><p>"); printf("Phylogenetic tree version: "); char *phyloPlaceTree = cartOptionalString(cart, "phyloPlaceTree"); cgiMakeDropListWithVals("phyloPlaceTree", treeChoices->descriptions, treeChoices->protobufFiles, treeChoices->count, phyloPlaceTree); } puts("</p><p>"); printf("Number of samples per subtree showing sample placement: "); int subtreeSize = cartUsualInt(cart, "subtreeSize", 50); cgiMakeIntVarWithLimits("subtreeSize", subtreeSize, "Number of samples in subtree showing neighborhood of placement", - 5, 10, 1000); + 5, 10, 2000); puts("</p><p>"); cgiMakeOnClickSubmitButton(CHECK_FILE_INPUT_JS, "submit", "upload"); puts("</p>"); +// Add a loading image to reassure people that we're working on it when they upload a big file +printf("<div><img id='loadingImg' src='../images/loading.gif' />\n"); +printf("<span id='loadingMsg'></span></div>\n"); +jsInline("$(document).ready(function() {\n" + " loadingImage.init($('#loadingImg'), $('#loadingMsg'), " + "'<p style=\"color: red; font-style: italic;\">Uploading and processing your sequences " + "may take some time. Please leave this window open while we work on your sequences.</p>');" + "});\n"); + puts(" </div>"); puts("</form>"); } static void exampleForm() /* Let the user try Russ's example. */ { printf("<form action='%s' name='exampleForm' method=POST>\n\n", "hgPhyloPlace"); cartSaveSession(cart); cgiMakeHiddenVar("db", "wuhCor1"); puts(" <div class='gbControl col-md-12'>"); puts("If you don't have a local file, you can try an " "<a href='https://github.com/russcd/USHER_DEMO/' target=_blank>example</a>: "); cgiMakeButton("submit", "try example"); @@ -270,30 +280,32 @@ puts("<p>\n" "GISAID data displayed in the Genome Browser are subject to GISAID's\n" "<a href='https://www.gisaid.org/registration/terms-of-use/' target=_blank>" "Terms and Conditions</a>.\n" "SARS-CoV-2 genome sequences and metadata are available for download from\n" "<a href='https://gisaid.org' target=_blank>GISAID</a> EpiCoV™.\n" "</p>"); puts("</div>"); puts("</div>"); } static void mainPage(char *db) { // Start web page with new-style header webStartGbNoBanner(cart, db, "UShER: Upload"); +jsIncludeFile("jquery.js", NULL); +jsIncludeFile("ajax.js", NULL); newPageStartStuff(); puts("<div class='row'>" " <div class='row gbSectionBanner'>\n" " <div class='col-md-11'>UShER: Ultrafast Sample placement on Existing tRee</div>\n" " <div class='col-md-1'></div>\n" " </div>\n" "</div>\n" "<div class='row'>\n"); if (hgPhyloPlaceEnabled()) { inputForm(); exampleForm(); linkToLandingPage(); gisaidFooter(); @@ -306,42 +318,46 @@ } puts("</div>\n"); newPageEndStuff(); } static void resultsPage(char *db, struct lineFile *lf) /* QC the user's uploaded sequence(s) or VCF; if input looks valid then run usher * and display results. */ { webStartGbNoBanner(cart, db, "UShER: Results"); newPageStartStuff(); hgBotDelay(); +// Allow 10 minutes for big sets of sequences +lazarusLives(10 * 60); + puts("<div class='row'>" " <div class='row gbSectionBanner'>\n" " <div class='col-md-11'>UShER: Ultrafast Sample placement on Existing tRee</div>\n" " <div class='col-md-1'></div>\n" " </div>\n" "</div>\n" "<div class='row'>\n"); // Form submits subtree custom tracks to hgTracks printf("<form action='%s' name='resultsForm' method=%s>\n\n", hgTracksName(), cartUsualString(cart, "formMethod", "POST")); cartSaveSession(cart); puts(" <div class='gbControl col-md-12'>"); +fflush(stdout); if (lf != NULL) { // Use trackLayout to get hgTracks parameters relevant to displaying trees: struct trackLayout tl; trackLayoutInit(&tl, cart); // Do our best to place the user's samples, make custom tracks if successful: char *phyloPlaceTree = cartOptionalString(cart, "phyloPlaceTree"); int subtreeSize = cartUsualInt(cart, "subtreeSize", 50); char *ctFile = phyloPlaceSamples(lf, db, phyloPlaceTree, measureTiming, subtreeSize, tl.fontHeight); if (ctFile) { cgiMakeHiddenVar(CT_CUSTOM_TEXT_VAR, ctFile); if (tl.leftLabelWidthChars < 0 || tl.leftLabelWidthChars == leftLabelWidthDefaultChars)