d060b70a11099fb693b769c781796b6b577715fb angie Fri Nov 1 12:47:25 2024 -0700 If sessionDataDir is in hg.conf, make a new UI option to request that the Auspice JSON output files don't expire after two days, using sessionDataDir/hgPhyloPlace/ for storage. diff --git src/hg/hgPhyloPlace/phyloPlace.c src/hg/hgPhyloPlace/phyloPlace.c index 589725b..1db8526 100644 --- src/hg/hgPhyloPlace/phyloPlace.c +++ src/hg/hgPhyloPlace/phyloPlace.c @@ -17,30 +17,31 @@ #include "hubConnect.h" #include "hui.h" #include "iupac.h" #include "jsHelper.h" #include "linefile.h" #include "mmHash.h" #include "obscure.h" #include "parsimonyProto.h" #include "phyloPlace.h" #include "phyloTree.h" #include "pipeline.h" #include "psl.h" #include "pthreadWrap.h" #include "ra.h" #include "regexHelper.h" +#include "sessionData.h" #include "trackHub.h" #include "trashDir.h" #include "vcf.h" // Globals: static boolean measureTiming = FALSE; // Parameter constants: int maxGenotypes = 1000; // Upper limit on number of samples user can upload at once. boolean showParsimonyScore = FALSE; int minSamplesForOwnTree = 3; // If user uploads at least this many samples, show tree for them. char *leftLabelWidthForLongNames = "55";// Leave plenty of room for tree and long virus strain names char *phyloPlaceOrgSetting(char *org, char *settingName) @@ -3290,30 +3291,43 @@ static struct phyloTree *uploadedSamplesTree(char *singleSubtreeFile, struct slName *sampleIds) /* If the user uploaded enough samples to make a meaningful tree, then read in singleSubtreeFile * and prune all nodes that have no leaf descendants in sampleIds to get the tree of only the * uploaded samples. */ { struct phyloTree *tree = NULL; if (slCount(sampleIds) >= minSamplesForOwnTree) { tree = phyloOpenTree(singleSubtreeFile); tree = phyloPruneToIds(tree, sampleIds); } return tree; } +static void saveTrashFile(struct tempName *tn) +/* If hg.conf specifies a long-term storage place for user data, then save tn there and make its + * original location a symbolic link to the new location. Note: tn has "hgPhyloPlace" in the path + * already, and we're not associating these with hgLogin user IDs, so there's no need to build + * the path up past sessionDataDir (unlike in hgSession). */ +{ +char *sessionDataDir = cfgOption("sessionDataDir"); +if (isNotEmpty(sessionDataDir)) + { + sessionDataSaveTrashFile(tn->forCgi, sessionDataDir); + } +} + static void phyloPlaceSamplesOneRef(struct lineFile *lf, char *db, char *org, char *refName, char *defaultProtobuf, boolean doMeasureTiming, int subtreeSize, struct trackLayout *tl, struct cart *cart, boolean *retSuccess) /* Given a lineFile that contains either FASTA, VCF, or a list of sequence names/ids: * If FASTA/VCF, then prepare VCF for usher; if that goes well then run usher, report results, * make custom track files and return the top-level custom track file. * If list of seq names/ids, then attempt to find their full names in the protobuf, run matUtils * to make subtrees, show subtree results, and return NULL. Set retSuccess to TRUE if we were * able to get at least some results for the user's input. */ { char *ctFile = NULL; measureTiming = doMeasureTiming; int startTime = clock1000(); struct tempName *vcfTn = NULL; @@ -3434,54 +3448,59 @@ } if (results && results->singleSubtreeInfo) { if (retSuccess) *retSuccess = TRUE; puts("<p></p>"); readQcThresholds(org, refName); int subtreeCount = slCount(results->subtreeInfoList); // Sort subtrees by number of user samples (largest first). slSort(&results->subtreeInfoList, subTreeInfoUserSampleCmp); // Make Nextstrain/auspice JSON file for each subtree. char *bigGenePredFile = phyloPlaceRefSettingPath(org, refName, "bigGenePredFile"); struct geneInfo *geneInfoList = getGeneInfoList(bigGenePredFile, refGenome); struct seqWindow *gSeqWin = memSeqWindowNew(refGenome->name, refGenome->dna); + boolean subtreePersist = cartUsualBoolean(cart, "subtreePersist", FALSE); struct hash *sampleUrls = hashNew(0); struct tempName *jsonTns[subtreeCount]; struct subtreeInfo *ti; int ix; for (ix = 0, ti = results->subtreeInfoList; ti != NULL; ti = ti->next, ix++) { AllocVar(jsonTns[ix]); char subtreeName[512]; safef(subtreeName, sizeof(subtreeName), "subtreeAuspice%d", ix+1); - trashDirFile(jsonTns[ix], "ct", subtreeName, ".json"); + trashDirFile(jsonTns[ix], "hgPhyloPlace", subtreeName, ".json"); treeToAuspiceJson(ti, org, refName, geneInfoList, gSeqWin, sampleMetadata, NULL, results->samplePlacements, jsonTns[ix]->forCgi, source); + if (subtreePersist) + saveTrashFile(jsonTns[ix]); // Add a link for every sample to this subtree, so the single-subtree JSON can // link to subtree JSONs char *subtreeUrl = nextstrainUrlFromTn(jsonTns[ix]); struct slName *sample; for (sample = ti->subtreeUserSampleIds; sample != NULL; sample = sample->next) hashAdd(sampleUrls, sample->name, subtreeUrl); } struct tempName *singleSubtreeJsonTn; AllocVar(singleSubtreeJsonTn); - trashDirFile(singleSubtreeJsonTn, "ct", "singleSubtreeAuspice", ".json"); + trashDirFile(singleSubtreeJsonTn, "hgPhyloPlace", "singleSubtreeAuspice", ".json"); treeToAuspiceJson(results->singleSubtreeInfo, org, refName, geneInfoList, gSeqWin, sampleMetadata, sampleUrls, results->samplePlacements, singleSubtreeJsonTn->forCgi, source); + if (subtreePersist) + saveTrashFile(singleSubtreeJsonTn); reportTiming(&startTime, "make Auspice JSON"); 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, results->subtreeInfoList, 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 "