3e968a25a2e9635f916fb3eeac0b3e2147cfba7c angie Mon Jun 28 08:43:35 2021 -0700 Add links to outbreak.info for lineages in results table & details. diff --git src/hg/hgPhyloPlace/phyloPlace.c src/hg/hgPhyloPlace/phyloPlace.c index 17fd160..101496c 100644 --- src/hg/hgPhyloPlace/phyloPlace.c +++ src/hg/hgPhyloPlace/phyloPlace.c @@ -831,39 +831,52 @@ * and fill in those two fields of placementInfo. */ { if (!bigTree) return; struct hashCookie cookie = hashFirst(samplePlacements); struct hashEl *hel; while ((hel = hashNext(&cookie)) != NULL) { struct placementInfo *info = hel->val; info->nearestNeighbor = findNearestNeighbor(bigTree, info->sampleId, info->variantPath); if (isNotEmpty(info->nearestNeighbor)) info->neighborLineage = lineageForSample(sampleMetadata, info->nearestNeighbor); } } +static void printLineageUrl(char *lineage) +/* If lineage is not empty/NULL, print ": lineage <lineage>" and link to outbreak.info + * (unless lineage is "None") */ +{ +if (isNotEmpty(lineage)) + { + if (differentString(lineage, "None")) + printf(": lineage <a href='"OUTBREAK_INFO_URLBASE"%s' target=_blank>%s</a>", + lineage, lineage); + else + printf(": lineage %s", lineage); + } +} + static void displayNearestNeighbors(struct placementInfo *info, char *source) /* Use info->variantPaths to find sample's nearest neighbor(s) in tree. */ { if (isNotEmpty(info->nearestNeighbor)) { printf("<p>Nearest neighboring %s sequence already in phylogenetic tree: %s", source, info->nearestNeighbor); - if (isNotEmpty(info->neighborLineage)) - printf(": lineage %s", info->neighborLineage); + printLineageUrl(info->neighborLineage); puts("</p>"); } } static void displayBestNodes(struct placementInfo *info, struct hash *sampleMetadata) /* Show the node(s) most closely related to sample. */ { if (info->bestNodeCount == 1) printf("<p>The placement in the tree is unambiguous; " "there are no other parsimony-optimal placements in the phylogenetic tree.</p>\n"); else if (info->bestNodeCount > 1) printf("<p>This placement is not the only parsimony-optimal placement in the tree; " "%d other placements exist.</p>\n", info->bestNodeCount - 1); if (showBestNodePaths && info->bestNodes) { @@ -871,32 +884,31 @@ errAbort("Inconsistent bestNodeCount (%d) and number of bestNodes (%d)", info->bestNodeCount, slCount(info->bestNodes)); if (info->bestNodeCount > 1) printf("<ul><li><b>used for placement</b>: "); if (differentString(info->bestNodes->name, "?") && !isAllDigits(info->bestNodes->name)) printf("%s ", info->bestNodes->name); printVariantPathNoNodeNames(stdout, info->bestNodes->variantPath); struct bestNodeInfo *bn; for (bn = info->bestNodes->next; bn != NULL; bn = bn->next) { printf("\n<li>"); if (differentString(bn->name, "?") && !isAllDigits(bn->name)) printf("%s ", bn->name); printVariantPathNoNodeNames(stdout, bn->variantPath); char *lineage = lineageForSample(sampleMetadata, bn->name); - if (isNotEmpty(lineage)) - printf(": lineage %s", lineage); + printLineageUrl(lineage); } if (info->bestNodeCount > 1) puts("</ul>"); puts("</p>"); } } static int placementInfoRefCmpSampleMuts(const void *va, const void *vb) /* Compare slRef->placementInfo->sampleMuts lists. Shorter lists first. Using alpha sort * to distinguish between different sampleMuts contents arbitrarily; the purpose is to * clump samples with identical lists. */ { struct slRef * const *rra = va; struct slRef * const *rrb = vb; struct placementInfo *pa = (*rra)->val; @@ -1472,30 +1484,41 @@ printf(TOOLTIP("%s"), text); } static void appendExcludingNs(struct dyString *dy, struct seqInfo *si) /* Append a note to dy about how many N bases and start and/or end are excluded from statistic. */ { dyStringAppend(dy, "excluding "); if (si->nCountStart) dyStringPrintf(dy, "%d N bases at start", si->nCountStart); if (si->nCountStart && si->nCountEnd) dyStringAppend(dy, " and "); if (si->nCountEnd) dyStringPrintf(dy, "%d N bases at end", si->nCountEnd); } +static void printLineageTd(char *lineage, char *alt) +/* Print a table cell with lineage (& link to outbreak.info if not 'None') or alt if no lineage. */ +{ +if (lineage && differentString(lineage, "None")) + printf("<td><a href='"OUTBREAK_INFO_URLBASE"%s' target=_blank>%s</a></td>", lineage, lineage); +else if (lineage) + printf("<td>%s</td>", lineage); +else + printf("<td>%s</td>", alt); +} + static void summarizeSequences(struct seqInfo *seqInfoList, boolean isFasta, struct usherResults *ur, struct tempName *jsonTns[], struct hash *sampleMetadata, struct dnaSeq *refGenome) /* Show a table with composition & alignment stats for each sequence that passed basic QC. */ { if (seqInfoList) { puts("<table class='seqSummary'>"); boolean gotClades = FALSE, gotLineages = FALSE; lookForCladesAndLineages(seqInfoList, ur->samplePlacements, &gotClades, &gotLineages); printSummaryHeader(isFasta, gotClades, gotLineages); puts("<tbody>"); struct dyString *dy = dyStringNew(0); struct seqInfo *si; for (si = seqInfoList; si != NULL; si = si->next) @@ -1614,34 +1637,34 @@ { replaceChar(reason->name, '_', ' '); dyStringPrintf(dy, ", %s", reason->name); } dyStringAppendC(dy, ')'); } printTooltip(dy->string); } printf("</td>"); struct placementInfo *pi = hashFindVal(ur->samplePlacements, si->seq->name); if (pi) { if (gotClades) printf("<td>%s</td>", pi->nextClade ? pi->nextClade : "n/a"); if (gotLineages) - printf("<td>%s</td>", pi->pangoLineage ? pi->pangoLineage : "n/a"); - printf("<td>%s</td><td>%s</td>", - pi->nearestNeighbor ? replaceChars(pi->nearestNeighbor, "|", " | ") : "?", - pi->neighborLineage ? pi->neighborLineage : "?"); + printLineageTd(pi->pangoLineage, "n/a"); + printf("<td>%s</td>", + pi->nearestNeighbor ? replaceChars(pi->nearestNeighbor, "|", " | ") : "?"); + printLineageTd(pi->neighborLineage, "?"); int imputedCount = slCount(pi->imputedBases); printf("<td class='%s'>%d", qcClassForImputedBases(imputedCount), imputedCount); if (imputedCount > 0) { dyStringClear(dy); struct baseVal *bv; for (bv = pi->imputedBases; bv != NULL; bv = bv->next) { dyStringAppendSep(dy, ", "); dyStringPrintf(dy, "%d: %s", bv->chromStart+1, bv->val); } printTooltip(dy->string); } printf("</td><td class='%s'>%d",