5cdeda3ceacbf093b325e8ebd48e17c4ac03e6c6
chmalee
Thu Nov 12 16:29:01 2020 -0800
Quality of life improvements on VCF details page, put the info tag annotations in a collapsible table, and add a border to said table so it's easier to distinguish between different tags. Also fix a bug in INFO tabular tables where the trailing separator was causing extra table cells to print, refs #26451
diff --git src/hg/hgc/vcfClick.c src/hg/hgc/vcfClick.c
index 1fb648f..a2064a2 100644
--- src/hg/hgc/vcfClick.c
+++ src/hg/hgc/vcfClick.c
@@ -89,137 +89,146 @@
printf("Filter: "NA"
\n");
else if (rec->filterCount == 1 && sameString(rec->filters[0], "PASS"))
printf("Filter: PASS
\n");
else
{
printf("Filter failures: ");
printf("\n");
struct vcfFile *vcff = rec->file;
printKeysWithDescriptions(vcff, rec->filterCount, rec->filters, vcff->filterDefs, FALSE);
printf("\n");
}
}
-static void printTabularHeaderRow(const struct vcfInfoDef *def)
+static int printTabularHeaderRow(const struct vcfInfoDef *def)
/* Parse the column header parts out of def->description and print as table header row;
- * call this only when looksTabular returns TRUE. */
+ * call this only when looksTabular returns TRUE.
+ * Returns the number of columns in the header */
{
regmatch_t substrArr[PATH_LEN];
if (regexMatchSubstr(def->description, COL_DESC_REGEX, substrArr, ArraySize(substrArr)))
{
puts("
");
// Make a copy of the part of def->description that matches the regex,
// then chop by '|' and print out header column tags:
int matchSize = substrArr[0].rm_eo - substrArr[0].rm_so;
char copy[matchSize+1];
safencpy(copy, sizeof(copy), def->description + substrArr[0].rm_so, matchSize);
// Turn '_' into ' ' so description words can wrap inside headers, saving some space
subChar(copy, '_', ' ');
char *words[PATH_LEN];
int descColCount = chopByChar(copy, '|', words, ArraySize(words));
int i;
for (i = 0; i < descColCount; i++)
printf("%s | ", words[i]);
puts("
");
+ return descColCount;
}
else
errAbort("printTabularHeaderRow: code bug, if looksTabular returns true then "
"regex should work here");
+ return -1;
}
-static void printTabularData(struct vcfInfoElement *el)
+static void printTabularData(struct vcfInfoElement *el, int headerCount)
/* Print a row for each value in el, separating columns by '|'. */
{
int j;
for (j = 0; j < el->count; j++)
{
puts("");
char *val = el->values[j].datString;
if (!isEmpty(val))
{
int len = strlen(val);
char copy[len+1];
safencpy(copy, sizeof(copy), val, len);
char *words[PATH_LEN];
- int colCount = chopByChar(copy, '|', words, ArraySize(words));
+ chopByChar(copy, '|', words, ArraySize(words));
int k;
- for (k = 0; k < colCount; k++)
+ // printTabularHeaderRow strips off (but still prints!) a trailing '|'
+ // because of the regex, so enforce that here too so the rows after
+ // the header don't get all out of whack
+ for (k = 0; k < headerCount; k++)
printf("%s | ", words[k]);
}
puts("
");
}
}
-static void vcfInfoDetails(struct vcfRecord *rec)
+static void vcfInfoDetails(struct vcfRecord *rec, char *trackName)
/* Expand info keys to descriptions, then print out keys and values. */
{
if (rec->infoCount == 0)
return;
struct vcfFile *vcff = rec->file;
-puts("INFO column annotations:
");
-puts("");
+puts(""); // wrapper table for collapsible section
+jsBeginCollapsibleSectionFontSize(cart, trackName, "infoFields", "INFO column annotations:", FALSE, "medium");
+puts("\n");
int i;
for (i = 0; i < rec->infoCount; i++)
{
struct vcfInfoElement *el = &(rec->infoElements[i]);
const struct vcfInfoDef *def = vcfInfoDefForKey(vcff, el->key);
printf("%s: | ",
el->key);
int j;
enum vcfInfoType type = def ? def->type : vcfInfoString;
if (type == vcfInfoFlag && el->count == 0)
printf("Yes"); // no values, so we can't call vcfPrintDatum...
// However, if this is older VCF, type vcfInfoFlag might have a value.
if (looksTabular(def, el))
{
// Make a special display below
printf("see below");
}
else
{
for (j = 0; j < el->count; j++)
{
if (j > 0)
printf(", ");
if (el->missingData[j])
printf(".");
else
vcfPrintDatum(stdout, el->values[j], type);
}
}
- if (def != NULL)
+ if (def != NULL && !looksTabular(def, el))
printf(" | %s", def->description);
else
printf(" | ");
printf(" |
\n");
}
puts("
");
+jsEndCollapsibleSection();
+puts("
"); // close the wrapper around the collapsible section
// Now show the tabular fields, if any
for (i = 0; i < rec->infoCount; i++)
{
struct vcfInfoElement *el = &(rec->infoElements[i]);
const struct vcfInfoDef *def = vcfInfoDefForKey(vcff, el->key);
if (looksTabular(def, el))
{
puts("
");
printf("%s: %s
\n", el->key, def->description);
puts("");
- printTabularHeaderRow(def);
- printTabularData(el);
+ int headerCount = printTabularHeaderRow(def);
+ printTabularData(el, headerCount);
puts("
");
}
}
}
static void vcfGenotypeTable(struct vcfRecord *rec, char *track, char **displayAls)
/* Put the table containing details about each genotype into a collapsible section. */
{
static struct dyString *tmp1 = NULL;
if (tmp1 == NULL)
tmp1 = dyStringNew(0);
jsBeginCollapsibleSection(cart, track, "genotypes", "Detailed genotypes", FALSE);
dyStringClear(tmp1);
dyStringAppend(tmp1, rec->format);
struct vcfFile *vcff = rec->file;
@@ -492,31 +501,31 @@
seqName, rec->chromStart, formName);
printf("
\n");
}
}
char leftBase = rec->alleles[0][0];
unsigned int vcfStart = vcfRecordTrimIndelLeftBase(rec);
boolean showLeftBase = (rec->chromStart == vcfStart+1);
(void)vcfRecordTrimAllelesRight(rec);
char *displayAls[rec->alleleCount];
makeDisplayAlleles(rec, showLeftBase, leftBase, 20, TRUE, FALSE, displayAls);
printPosOnChrom(seqName, rec->chromStart, rec->chromEnd, NULL, FALSE, rec->name);
printf("Reference allele: %s
\n", displayAls[0]);
vcfAltAlleleDetails(rec, displayAls);
vcfQualDetails(rec);
vcfFilterDetails(rec);
-vcfInfoDetails(rec);
+vcfInfoDetails(rec, tdb->track);
pgSnpCodingDetail(rec);
makeDisplayAlleles(rec, showLeftBase, leftBase, 5, FALSE, TRUE, displayAls);
vcfGenotypesDetails(rec, tdb, displayAls);
}
void doVcfDetailsCore(struct trackDb *tdb, char *fileOrUrl, boolean isTabix)
/* Show item details using fileOrUrl. */
{
genericHeader(tdb, NULL);
int start = cartInt(cart, "o");
int end = cartInt(cart, "t");
int vcfMaxErr = -1;
struct vcfFile *vcff = NULL;
/* protect against temporary network or parsing error */
struct errCatch *errCatch = errCatchNew();