0d3b8feb6074f6caccc2d4aadd020961fbd7eb82 chmalee Wed Jun 10 14:39:38 2020 -0700 Add drag reorder of VCF samples to VCF trio display. Also fixes bug in haplotype sort that I didn't discover until adding drag reorder. Sometimes when sorting haplotypes it is the case that the initial best match to a child allele is the same for each parent. Previously I would just advance the first drawn parent to the next best match but now I also check whether advancing the first drawn is actually the best idea and potentially advance the other parent instead, refs #25582 diff --git src/hg/lib/vcfUi.c src/hg/lib/vcfUi.c index 4d579eb..f5b777e 100644 --- src/hg/lib/vcfUi.c +++ src/hg/lib/vcfUi.c @@ -372,30 +372,40 @@ static void vcfCfgMinAlleleFreq(struct cart *cart, struct trackDb *tdb, struct vcfFile *vcff, char *name, boolean parentLevel) /* Show input for minimum allele frequency, if we can extract it from the VCF INFO column. */ { printf("Minimum minor allele frequency (if INFO column includes AF or AC+AN):\n"); double cartMinFreq = cartOrTdbDouble(cart, tdb, VCF_MIN_ALLELE_FREQ_VAR, VCF_DEFAULT_MIN_ALLELE_FREQ); char varName[1024]; safef(varName, sizeof(varName), "%s." VCF_MIN_ALLELE_FREQ_VAR, name); cgiMakeDoubleVarInRange(varName, cartMinFreq, "minor allele frequency between 0.0 and 0.5", 5, "0.0", "0.5"); puts("
"); } +static char *getChildSample(struct trackDb *tdb) +/* Return just the VCF sample name of the phased trio child setting */ +{ +char *childSampleMaybeAlias = cloneString(trackDbLocalSetting(tdb, VCF_PHASED_CHILD_SAMPLE_SETTING)); +char *pt = strchr(childSampleMaybeAlias, '|'); +if (pt != NULL) + *pt = '\0'; +return childSampleMaybeAlias; +} + static struct slPair *vcfPhasedGetSamplesFromTdb(struct trackDb *tdb, boolean hideOtherSamples) /* Get the different VCF Phased Trio setings out of trackDb onto a list */ { // cloneString here because we will be munging the result if there are alternate labels char *childSampleMaybeAlias = cloneString(trackDbLocalSetting(tdb, VCF_PHASED_CHILD_SAMPLE_SETTING)); char *parentSamplesMaybeAlias = cloneString(trackDbLocalSetting(tdb, VCF_PHASED_PARENTS_SAMPLE_SETTING)); char *samples[VCF_PHASED_MAX_OTHER_SAMPLES+1]; // for now only allow at most two parents int numOthers = 0; if (parentSamplesMaybeAlias && !hideOtherSamples) { numOthers = chopCommas(cloneString(parentSamplesMaybeAlias), samples); if (numOthers > VCF_PHASED_MAX_OTHER_SAMPLES) { warn("More than %d other samples specified for phased trio", VCF_PHASED_MAX_OTHER_SAMPLES); numOthers = VCF_PHASED_MAX_OTHER_SAMPLES; @@ -419,67 +429,165 @@ if (val != NULL) { if (foundAlias != gotAlias) errAbort("Either all samples have aliases or none."); else *val++ = 0; } char *name = samples[i]; struct slPair *temp = slPairNew(cloneString(name), cloneString(val)); slAddHead(&ret, temp); } slReverse(&ret); return ret; } -struct slPair *vcfPhasedGetSampleOrder(struct cart *cart, struct trackDb *tdb, boolean parentLevel) -/* Parse out a trio sample order from either trackDb or the cart */ +struct slPair *vcfPhasedGetSampleOrder(struct cart *cart, struct trackDb *tdb, boolean parentLevel, boolean hideOtherSamples) +/* Parse out a trio sample order from either trackDb or the cart. + * If the trackName.sortChildBelow cart variable is true, then ensure + * the vcfChildSample sample is last in the order, otherwise, use what's + * in the trackName.vcfSampleOrder cart variable. */ { -char sampleOrderVar[1028],hideParentsVar[1028]; +char sampleOrderVar[1024]; safef(sampleOrderVar, sizeof(sampleOrderVar), "%s.%s", tdb->track, VCF_PHASED_SAMPLE_ORDER_VAR); -safef(hideParentsVar, sizeof(hideParentsVar), "%s.%s", tdb->track, VCF_PHASED_HIDE_OTHER_VAR); -boolean hideOtherSamples = cartUsualBooleanClosestToHome(cart, tdb, parentLevel, VCF_PHASED_HIDE_OTHER_VAR, FALSE); char *cartOrder = cartOptionalString(cart, sampleOrderVar); +boolean childBelow = cartUsualBooleanClosestToHome(cart, tdb, parentLevel, VCF_PHASED_CHILD_BELOW_VAR, FALSE); struct slPair *tdbOrder = vcfPhasedGetSamplesFromTdb(tdb, hideOtherSamples); -if (cartOrder != NULL && !hideOtherSamples) +if (!hideOtherSamples) + { + // if the user used drag and drop to reorder the trios then that takes precedence + // over the childBelow checkbox + if (cartOrder != NULL) { - struct slName *name; - struct slName *fromCart = slNameListFromComma(cartOrder); + struct slName *name, *fromCart = slNameListFromComma(cartOrder); struct slPair *ret = NULL; for (name = fromCart; name != NULL; name = name->next) { struct slPair *temp = slPairFind(tdbOrder, name->name); - slAddHead(&ret, temp); + struct slPair *toAdd = slPairNew(temp->name, temp->val); + slAddHead(&ret, toAdd); } - slReverse(ret); + slReverse(&ret); return ret; } + else if (childBelow) + { + char *childName = getChildSample(tdb); + struct slPair *ret = NULL, *child = NULL, *temp = NULL; + for (temp = tdbOrder; temp != NULL; temp = temp->next) + { + struct slPair *toAdd = slPairNew(temp->name, temp->val); + if (sameString(temp->name, childName)) + child = toAdd; else + slAddHead(&ret, toAdd); + } + if (child) + slAddHead(&ret, child); + slReverse(&ret); + return ret; + } + } +// we're hiding the parents OR (we unchecked the childBelow checkbox AND we didn't drag reorder) return tdbOrder; } static boolean hasSampleAliases(struct trackDb *tdb) /* Check whether trackDb has aliases for the sample names */ { struct slPair *nameVals = vcfPhasedGetSamplesFromTdb(tdb,FALSE); return nameVals->val != NULL; } +static void vcfPhasedSampleSortUi(struct cart *cart, struct trackDb *tdb, struct vcfFile *vcff, char *name, + boolean parentLevel) +/* Put up the UI for sorting the samples */ +{ +struct dyString *sortOrder = dyStringNew(0); +struct slPair *pair, *tdbOrder = vcfPhasedGetSampleOrder(cart, tdb, parentLevel, FALSE); +if (slCount(tdbOrder) == 1) // no sorting if there are no parents + return; +char childBelowSortOrder[1024]; +safef(childBelowSortOrder, sizeof(childBelowSortOrder), "%s.%s", name, VCF_PHASED_CHILD_BELOW_VAR); +boolean isBelowChecked = cartUsualBooleanClosestToHome(cart, tdb, parentLevel, VCF_PHASED_CHILD_BELOW_VAR, FALSE); +printf("Show child haplotypes below parents:\n"); +cgiMakeCheckBox(childBelowSortOrder, isBelowChecked); +char *infoText = "Check this box to sort the child haplotypes below the parents, leave unchecked" + " to use the default sort order of the child in the middle. Click into each subtrack to arbitrarily" + " order the samples which overrides this setting."; +printInfoIcon(infoText); +printf("
"); +if (!parentLevel) + { + printf("or:
\n"); + printf("Click and drag to change order:\n"); + printf("
\n"); + printf("\n", tdb->track); + for (pair = tdbOrder; pair != NULL; pair = pair->next) + { + char id[256]; + safef(id, sizeof(id), "%s_drag", pair->name); + printf("\n", pair->name, id, pair->name, (char *)pair->val); + dyStringPrintf(sortOrder, "%s,", pair->name); + } + printf("
%s - %s
\n"); + printf("
\n"); + printf("",tdb->track, VCF_PHASED_SAMPLE_ORDER_VAR, dyStringCannibalize(&sortOrder)); + // add the hidden variable for setting the order and the javascript to change it + jsInlineF("" + "dragReorder.init();\n" + "var imgTable = $(\"#%s_table\");\n" + "if ($(imgTable).length > 0) {\n" + " $(imgTable).tableDnD({\n" + " onDragClass: \"trDrag\",\n" + " dragHandle: \"dragHandle\",\n" + " scrollAmount: 40,\n" + " onDragStart: function(ev, table, row) {\n" + " mouse.saveOffset(ev);\n" + " table.tableDnDConfig.dragObjects = [ row ]; // defaults to just the one\n" + " },\n" + " onDrop: function(table, row, dragStartIndex) {\n" + " if ($(row).attr('rowIndex') !== dragStartIndex) {\n" + " // NOTE Even if dragging a contiguous set of rows,\n" + " // still only need to check the one under the cursor.\n" + " if (dragReorder.setOrder) {\n" + " dragReorder.setOrder(table);\n" + " }\n" + " // save the order of the samples into the input variable named above\n" + " var newVal = \"\";\n" + " var inp = $(\"input[name='%s.%s']\")[0];\n" + " for (i = 0; i < table.rows.length; i++) {\n" + " newVal += table.rows[i].id.slice(0,-4) + \",\";\n" + " }\n" + " if (newVal.slice(-1) === \",\") {\n" + " newVal = newVal.slice(0,-1);\n" + " }\n" + " inp.value = newVal;\n" + " }\n" + " }\n" + " });\n" + "}\n" + "", tdb->track, tdb->track, VCF_PHASED_SAMPLE_ORDER_VAR); + } +} + static void vcfCfgPhasedTrioUi(struct cart *cart, struct trackDb *tdb, struct vcfFile *vcff, char *name, boolean parentLevel) /* Put up the phased trio specific config settings */ { +//if (!parentLevel) // don't put up this display at the composite level +vcfPhasedSampleSortUi(cart, tdb, vcff, name, parentLevel); if (hasSampleAliases(tdb)) { printf("Label samples by:"); char defaultLabel[1024], aliasLabel[1024]; safef(defaultLabel, sizeof(defaultLabel), "%s.%s", name, VCF_PHASED_DEFAULT_LABEL_VAR); safef(aliasLabel, sizeof(aliasLabel), "%s.%s", name, VCF_PHASED_ALIAS_LABEL_VAR); boolean isDefaultChecked = cartUsualBooleanClosestToHome(cart, tdb, parentLevel, VCF_PHASED_DEFAULT_LABEL_VAR, FALSE); boolean isAliasChecked = cartUsualBooleanClosestToHome(cart, tdb, parentLevel, VCF_PHASED_ALIAS_LABEL_VAR, TRUE); cgiMakeCheckBox(defaultLabel, isDefaultChecked); printf("VCF file sample names  "); cgiMakeCheckBox(aliasLabel, isAliasChecked); printf("Family Labels"); printf("
"); } if (trackDbSetting(tdb,VCF_PHASED_PARENTS_SAMPLE_SETTING))