d3a829cef9072d6e29bb45961a360ec2f9d3d1c2 angie Thu Sep 5 11:21:45 2013 -0700 Adding 'Artificial Example Variants' as an option for variant track.Since there is now at least one source of variants from which to choose, we no longer need to hide the rest of the UI until the user provides some variants. The variants are generated from genes in the user's cart position if possible. Variants are cached in trash files unique to database and cart position. refs #11110 diff --git src/hg/hgVai/hgVai.c src/hg/hgVai/hgVai.c index 66c3f23..1ed0fc5 100644 --- src/hg/hgVai/hgVai.c +++ src/hg/hgVai/hgVai.c @@ -6,60 +6,63 @@ #include "jksql.h" #include "htmshell.h" #include "web.h" #include "cheapcgi.h" #include "cart.h" #include "hui.h" #include "grp.h" #include "hCommon.h" #include "hgFind.h" #include "hPrint.h" #include "jsHelper.h" #include "memalloc.h" #include "textOut.h" #include "trackHub.h" #include "hubConnect.h" +#include "twoBit.h" +#include "gpFx.h" #include "udc.h" #include "knetUdc.h" #include "annoGratorQuery.h" #include "annoGratorGpVar.h" #include "annoFormatVep.h" #include "annoStreamBigBed.h" #include "libifyMe.h" /* Global Variables */ struct cart *cart; /* CGI and other variables */ struct hash *oldVars = NULL; /* The cart before new cgi stuff added. */ char *genome = NULL; /* Name of genome - mouse, human, etc. */ char *database = NULL; /* Current genome database - hg17, mm5, etc. */ char *regionType = NULL; /* genome, ENCODE pilot regions, or specific position range. */ struct grp *fullGroupList = NULL; /* List of all groups. */ struct trackDb *fullTrackList = NULL; /* List of all tracks in database. */ static struct pipeline *compressPipeline = (struct pipeline *)NULL; // Null terminated list of CGI Variables we don't want to save permanently: char *excludeVars[] = {"Submit", "submit", "hgva_startQuery", NULL,}; #define hgvaRange "position" #define hgvaRegionType "hgva_regionType" #define hgvaRegionTypeEncode "encode" #define hgvaRegionTypeGenome "genome" #define hgvaRegionTypeRange "range" #define hgvaPositionContainer "positionContainer" - +#define hgvaSampleVariants "hgva_internally_generated_sample_variants" +#define hgvaSampleVariantsLabel "Artificial Example Variants" void addSomeCss() /*#*** This should go in a .css file of course. */ { printf("<style>\n" "div.sectionLite { border-width: 1px; border-color: #"HG_COL_BORDER"; border-style: solid;" " background-color: #"HG_COL_INSIDE"; padding-left: 10px; padding-right: 10px; " " padding-top: 8px; padding-bottom: 5px; margin-top: 5px; margin-bottom: 5px }\n" ".sectionLiteHeader { font-weight: bold; font-size:larger; color:#000000;" " text-align:left; vertical-align:bottom; white-space:nowrap; }\n" "div.sectionLiteHeader.noReorderRemove { padding-bottom:5px; }\n" "div.sourceFilter { padding-top: 5px; padding-bottom: 5px }\n" "</style>\n"); } @@ -228,50 +231,36 @@ printf("<FORM ACTION=\"%s\" NAME=\"mainForm\" ID=\"mainForm\" METHOD=%s>\n", cgiScriptName(), cartUsualString(cart, "formMethod", "GET")); cartSaveSession(cart); //#*** ------------------ end verbatim --------------- printf("<div class='sectionLiteHeader noReorderRemove'>" "Select Genome Assembly and Region</div>\n"); /* Print clade, genome and assembly line. */ hgGatewayCladeGenomeDb(); //#*** No longer ending form here... } -void askUserForVariantCustomTrack() -/* Tell the user that we need a custom track of variants to work on. */ -{ -puts("<BR>"); -printf("<div class='sectionLiteHeader'>Upload Variants</div>\n"); -printf("Please provide a set of variant calls to annotate, as either a custom track in " - "<A HREF='../FAQ/FAQformat.html#format10' TARGET=_BLANK>pgSnp</A> or " - "<A HREF='../goldenPath/help/vcf.html' TARGET=_BLANK>VCF</A> format, " - "or a VCF track on your " - "<A HREF='../goldenPath/help/hgTrackHubHelp.html' TARGET=_BLANK>track hub</A>:<BR>\n"); -printCtAndHubButtons(); -puts("<BR>"); -} - typedef boolean TdbFilterFunction(struct trackDb *tdb, void *filterData); /* Return TRUE if tdb passes filter criteria. */ void rFilterTrackList(struct trackDb *trackList, struct slRef **pPassingRefs, TdbFilterFunction *filterFunc, void *filterData) -/* Recursively apply filterFunc and filterData to all tracks in fullTrackList and +/* Recursively apply filterFunc and filterData to all tracks in trackList and * their subtracks. Add an slRef to pPassingRefs for each track/subtrack that passes. * Caller should slReverse(pPassingRefs) when recursion is all done. */ { struct trackDb *tdb; for (tdb = trackList; tdb != NULL; tdb = tdb->next) { if (tdb->subtracks != NULL) rFilterTrackList(tdb->subtracks, pPassingRefs, filterFunc, filterData); if (filterFunc(tdb, filterData)) slAddHead(pPassingRefs, slRefNew(tdb)); } } void tdbFilterGroupTrack(struct trackDb *fullTrackList, struct grp *fullGroupList, TdbFilterFunction *filterFunc, void *filterData, @@ -320,30 +309,31 @@ { printf("<div class='sectionLiteHeader'>Select Variants</div>\n"); printf("If you have more than one custom track or hub track in " "<A HREF='../FAQ/FAQformat.html#format10' TARGET=_BLANK>pgSnp</A> or " "<A HREF='../goldenPath/help/vcf.html' TARGET=_BLANK>VCF</A> format, " "please select the one you wish to annotate:<BR>\n"); printf("<B>variants: </B>"); printf("<SELECT ID='hgva_variantTrack' NAME='hgva_variantTrack'>\n"); char *selected = cartUsualString(cart, "hgva_variantTrack", ""); struct slRef *ref; for (ref = varTrackList; ref != NULL; ref = ref->next) { struct trackDb *tdb = ref->val; printOption(tdb->track, selected, tdb->longLabel); } +printOption(hgvaSampleVariants, selected, hgvaSampleVariantsLabel); printf("</SELECT><BR>\n"); printf("<B>maximum number of variants to be processed:</B>\n"); char *curLimit = cartUsualString(cart, "hgva_variantLimit", "10000"); char *limitLabels[] = { "10", "100", "1,000", "10,000", "100,000" }; char *limitValues[] = { "10", "100", "1000", "10000", "100000" }; cgiMakeDropListWithVals("hgva_variantLimit", limitLabels, limitValues, ArraySize(limitLabels), curLimit); printCtAndHubButtons(); puts("<BR>"); } boolean isGeneTrack(struct trackDb *tdb, void *filterData) /* This is a TdbFilterFunction to get genePred tracks. */ { return (startsWith("genePred", tdb->type)); @@ -823,48 +813,41 @@ jsIncludeFile("jquery-ui.js", NULL); webIncludeResourceFile("jquery-ui.css"); jsIncludeFile("hgVarAnnogrator.js", NULL); boolean alreadyAgreed = cartUsualBoolean(cart, "hgva_agreedToDisclaimer", FALSE); printf("<script>\n" "$(document).ready(function() { hgva.disclaimer.init(%s, hgva.userClickedAgree); });\n" "</script>\n", alreadyAgreed ? "true" : "false"); addSomeCss(); printAssemblySection(); /* Check for variant custom tracks. If there are none, tell user they need to * upload at least one. */ struct slRef *varTrackList = NULL, *varGroupList = NULL; tdbFilterGroupTrack(fullTrackList, fullGroupList, isVariantCustomTrack, NULL, &varGroupList, &varTrackList); -if (varTrackList == NULL) - { - askUserForVariantCustomTrack(); - } -else - { puts("<BR>"); // Make wrapper table for collapsible sections: selectVariants(varGroupList, varTrackList); boolean gotGP = selectGenes(); if (gotGP) { selectAnnotations(); selectFilters(); selectOutput(); submitAndDisclaimer(); } - } printf("</FORM>"); jsReloadOnBackButton(cart); webNewSection("Using the Variant Annotation Integrator"); webIncludeHelpFile("hgVaiHelpText", FALSE); } void doUi() /* Set up globals and make web page */ { cartWebStart(cart, database, "Variant Annotation Integrator"); doMainPage(); cartWebEnd(); /* Save variables. */ @@ -1139,98 +1122,390 @@ char *consElTrack = cartString(cart, "hgva_require_consEl_track"); struct annoGrator *grator = hashFindVal(gratorsByName, consElTrack); if (grator == NULL) { struct trackDb *tdb = tdbForTrack(database, consElTrack, &fullTrackList); if (tdb != NULL) grator = gratorFromTrackDb(assembly, tdb->table, tdb, chrom, NO_MAXROWS, NULL, agoMustOverlap); updateGratorList(grator, pGratorList); } else grator->setOverlapRule(grator, agoMustOverlap); } } -void doQuery() -/* Translate simple form inputs into anno* components and execute query. */ +static void getCartPosOrDie(char **retChrom, uint *retStart, uint *retEnd) +/* Get chrom:start-end from cart, errAbort if any problems. */ +{ +char *position = cartString(cart, hgvaRange); +if (! parsePosition(position, retChrom, retStart, retEnd)) + errAbort("Expected position to be chrom:start-end but got '%s'", position); +} + +static char *sampleVariantsPath(struct annoAssembly *assembly) +/* Construct a path for a trash file of contrived example variants for this assembly. */ { char *chrom = NULL; uint start = 0, end = 0; -if (sameString(regionType, hgvaRegionTypeRange)) +getCartPosOrDie(&chrom, &start, &end); +char *subDir = "hgv"; +mkdirTrashDirectory(subDir); +struct dyString *dy = dyStringCreate("%s/%s/%s_%s_%u-%u.vcf", trashDir(), subDir, assembly->name, + chrom, start, end); +return dyStringCannibalize(&dy); +} + +static void writeMinimalVcfHeader(FILE *f, char *db) +/* Write header for VCF with no meaningful qual, filter, info or genotype data. */ { - char *position = cartString(cart, hgvaRange); - if (! parsePosition(position, &chrom, &start, &end)) - errAbort("Expected position to be chrom:start-end but got '%s'", position); +fputs("##fileformat=VCFv4.1\n", f); +fprintf(f, "##reference=%s\n", db); +fputs("#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\n", f); } -char *variantTrack = cartString(cart, "hgva_variantTrack"); -char *geneTrack = cartString(cart, "hgva_geneTrack"); -struct annoAssembly *assembly = getAnnoAssembly(database); +static void mutateBase(char *pBase) +/* Change *pBase into a different base. */ +{ +static char *bases = "ACGT"; +char c; +// In real life the probabilities aren't even, but this is easier. +while ((c = bases[(uint)(floor(drand48() * 4.0))]) == *pBase) + ; +*pBase = c; +} + +static void writeMinimalVcfRowFromBed(FILE *f, struct bed4 *bed, struct dnaSeq *seq, uint seqOffset) +/* Write a minimalist VCF row with a mutation of the reference sequence at the given + * position. */ +{ +uint indelBase = 0, start = bed->chromStart, end = bed->chromEnd; +if (end != start+1) + { + // VCF treats indels in a special way: pos is the base to the left of the indel, + // and both ref and alt alleles include the base to the left of the indel. + indelBase = 1; + } +// Get reference allele sequence: +uint refAlLen = end - start + indelBase; +char ref[refAlLen+1]; +uint seqStart = start - indelBase - seqOffset; +safencpy(ref, sizeof(ref), seq->dna+seqStart, refAlLen); +touppers(ref); +// Alternate allele seq needs extra room in case of single-base insertion: +char alt[refAlLen+2]; +alt[0] = ref[0]; +alt[1] = '\0'; +if (start == end) + // insertion: alt gets extra base + safecpy(alt+1, sizeof(alt)-1, "A"); +else if (end == start+1) + // single nucleotide variant + mutateBase(alt); +// else deletion: leave alt truncated after the shared base to left of indel + +// VCF columns: #CHROM POS ID REF ALT QUAL FILTER INFO +fprintf(f, "%s\t%u\t%s\t%s\t%s\t.\t.\t.\n", + bed->chrom, start+1-indelBase, bed->name, ref, alt); +} + +static void writeArbitraryVariants(FILE *f, struct annoAssembly *assembly) +/* Concoct some variants at an arbitrary position on some assembly sequence. + * We shouldn't ever get here unless the selected geneTrack is empty. */ +{ +char *chrom = "NULL"; +uint start = 0, end = 0; +getCartPosOrDie(&chrom, &start, &end); +struct bed4 *bed = bed4New(chrom, start, start, "ex_ins"); +struct dnaSeq *seq = twoBitReadSeqFragLower(assembly->tbf, chrom, start-1, start+100); +writeMinimalVcfRowFromBed(f, bed, seq, start-1); +dnaSeqFree(&seq); +} + +static void addGpFromRow(struct genePred **pGpList, struct annoRow *row, + boolean *pNeedCoding, boolean *pNeedNonCoding) +/* If row is coding and we need a coding gp, add it to pGpList and update pNeedCoding; + * likewise for noncoding. */ +{ +struct genePred *gp = genePredLoad(row->data); +if (gp->cdsStart != gp->cdsEnd && *pNeedCoding) + { + slAddHead(pGpList, gp); + *pNeedCoding = FALSE; + } +else if (gp->cdsStart == gp->cdsEnd && *pNeedNonCoding) + { + slAddHead(pGpList, gp); + *pNeedNonCoding = FALSE; + } +} + +static void addGpFromPos(struct annoStreamer *geneStream, char *chrom, uint start, uint end, + struct genePred **pGpList, boolean *pNeedCoding, boolean *pNeedNonCoding, + struct lm *lm) +/* Get up to 10 rows from position; if we find one coding and one non-coding gene, + * add them to pGpList and update pNeed* accordingly. */ +{ +geneStream->setRegion(geneStream, chrom, start, end); +int rowCount = 0; +struct annoRow *row = NULL; +while (rowCount < 10 && (*pNeedCoding || *pNeedNonCoding) + && (row = geneStream->nextRow(geneStream, NULL, 0, lm)) != NULL) + addGpFromRow(pGpList, row, pNeedCoding, pNeedNonCoding); +} + +static struct genePred *genesFromPosition(struct annoStreamer *geneStream, + boolean *retGotCoding, boolean *retGotNonCoding) +/* Try to get a coding and non-coding gene from geneStream at cart position. + * If there are none, try whole chrom; if none there, first in assembly. */ +{ +struct genePred *gpList = NULL; +struct lm *lm = lmInit(0); +char *chrom = NULL; +uint start = 0, end = 0; +getCartPosOrDie(&chrom, &start, &end); +boolean needCoding = TRUE, needNonCoding = TRUE; +// First, look for both coding and noncoding genes at cart position: +addGpFromPos(geneStream, chrom, start, end, &gpList, &needCoding, &needNonCoding, lm); +// If that didn't do it, try querying whole chrom +if (needCoding || needNonCoding) + addGpFromPos(geneStream, chrom, 0, 0, &gpList, &needCoding, &needNonCoding, lm); +// If we still haven't found either coding or non-coding on the cart's current chrom, +// try whole genome: +if (needCoding && needNonCoding) + addGpFromPos(geneStream, NULL, 0, 0, &gpList, &needCoding, &needNonCoding, lm); +slSort(&gpList, genePredCmp); +lmCleanup(&lm); +if (retGotCoding) + *retGotCoding = !needCoding; +if (retGotNonCoding) + *retGotNonCoding = !needNonCoding; +return gpList; +} + +static void addSnv(struct bed4 **pBedList, char *chrom, uint start, char *name) +/* Add a single-nucleotide bed4 item. */ +{ +slAddHead(pBedList, bed4New(chrom, start, start+1, name)); +} + +// Stuff within this range (see gpFx.h) is considered upstream/downstream of a transcript: +#define UPSTREAMFUDGE GPRANGE + +static struct bed4 *sampleVarBedFromGenePred(struct genePred *gp, struct annoAssembly *assembly, + uint txSeqStart, uint txSeqEnd, boolean exonOnly) +/* Construct imaginary variants that hit various parts of the transcript + * described in gp, using reference base values from assembly. */ +{ +uint basesLeft = gp->txStart - txSeqStart, basesRight = txSeqEnd - gp->txEnd; +struct bed4 *bedList = NULL; +if (!exonOnly && basesLeft > UPSTREAMFUDGE) + // SNV, intergenic to the left + addSnv(&bedList, gp->chrom, txSeqStart, "ex_igLeft"); +if (!exonOnly && basesLeft > 0) + // SNV, up/downstream to the left + addSnv(&bedList, gp->chrom, gp->txStart - 1, "ex_updownLeft"); +if (!exonOnly && gp->exonCount > 1) + { + // SNV, intron + uint start = (gp->exonEnds[0] + gp->exonStarts[1]) / 2; + addSnv(&bedList, gp->chrom, start, "ex_intron"); + // SNV, splice + addSnv(&bedList, gp->chrom, gp->exonStarts[1] - 2, "ex_splice"); + } +boolean isCoding = (gp->cdsStart != gp->cdsEnd); +if (isCoding) + { + if (gp->txStart < gp->cdsStart) + // SNV, UTR to the left + addSnv(&bedList, gp->chrom, gp->txStart + 1, "ex_utrLeft"); + int cdsExon = 0; + while (gp->cdsStart > gp->exonEnds[cdsExon] && cdsExon < gp->exonCount) + cdsExon++; + uint start = gp->cdsStart + 2; + // single-base insertion, leftmost codon + slAddHead(&bedList, bed4New(gp->chrom, start, start, "ex_codonLeftIns")); + // 3-base deletion leftmost codon + slAddHead(&bedList, bed4New(gp->chrom, start, start+3, "ex_codonLeftDel")); + // SNV, leftmost codon + addSnv(&bedList, gp->chrom, start+1, "ex_codonLeft"); + if (gp->txEnd > gp->cdsEnd) + // SNV, UTR to the right + addSnv(&bedList, gp->chrom, gp->txEnd - 1, "ex_utrRight"); + } +else + { + // noncoding exon variant + uint start = (gp->exonStarts[0] + gp->exonEnds[0]) / 2; + addSnv(&bedList, gp->chrom, start, "ex_nce"); + } +if (!exonOnly && basesRight > 0) + // SNV, up/downstream to the left + addSnv(&bedList, gp->chrom, gp->txEnd + 1, "ex_updownRight"); +return bedList; +} + +// margin for intergenic variants around transcript: +#define IGFUDGE 5 + +static struct annoStreamer *makeSampleVariantsStreamer(struct annoAssembly *assembly, + struct trackDb *geneTdb, int maxOutRows) +/* Construct a VCF file of example variants for db (unless it already exists) + * and return an annoStreamer for that file. If possible, make the variants hit a gene. */ +{ +char *sampleFile = sampleVariantsPath(assembly); +boolean forceRebuild = cartUsualBoolean(cart, "hgva_rebuildSampleVariants", FALSE); +if (! fileExists(sampleFile) || forceRebuild) + { + FILE *f = mustOpen(sampleFile, "w"); + writeMinimalVcfHeader(f, assembly->name); + struct annoStreamer *geneStream = streamerFromTrack(assembly, geneTdb->table, geneTdb, NULL, + NO_MAXROWS); + boolean gotCoding = FALSE, gotNonCoding = FALSE; + struct genePred *gpList = genesFromPosition(geneStream, &gotCoding, &gotNonCoding); + if (gpList == NULL) + { + warn("Unable to find any gene transcripts in '%s' (%s)", + geneTdb->shortLabel, geneTdb->track); + writeArbitraryVariants(f, assembly); + } + else + { + struct bed4 *bedList = NULL; + uint chromSize = annoAssemblySeqSize(assembly, gpList->chrom); + uint minSeqStart = chromSize, maxSeqEnd = 0; + struct genePred *gp; + for (gp = gpList; gp != NULL; gp = gp->next) + { + int txSeqStart = gp->txStart - (UPSTREAMFUDGE + IGFUDGE); + uint txSeqEnd = gp->txEnd + UPSTREAMFUDGE + IGFUDGE; + if (txSeqStart < 0) + txSeqStart = 0; + if (txSeqEnd > chromSize) + txSeqEnd = chromSize; + if (txSeqStart < minSeqStart) + minSeqStart = txSeqStart; + if (txSeqEnd > maxSeqEnd) + maxSeqEnd = txSeqEnd; + // If we got a coding gp, but this gp is non-coding, just do its exon variant: + boolean exonOnly = gotCoding && (gp->cdsStart == gp->cdsEnd); + slCat(&bedList, sampleVarBedFromGenePred(gp, assembly, txSeqStart, txSeqEnd, exonOnly)); + } + slSort(&bedList, bedCmp); + struct dnaSeq *txSeq = twoBitReadSeqFragLower(assembly->tbf, gpList->chrom, + minSeqStart, maxSeqEnd); + struct bed4 *bed; + for (bed = bedList; bed != NULL; bed = bed->next) + { + writeMinimalVcfRowFromBed(f, bed, txSeq, minSeqStart); + } + dnaSeqFree(&txSeq); + bed4FreeList(&bedList); + } + geneStream->close(&geneStream); + carefulClose(&f); + } +return annoStreamVcfNew(sampleFile, FALSE, assembly, maxOutRows); +} + +static struct trackDb *getVariantTrackDb(char *variantTrack) +/* Get a tdb for the user's selected variant track, or warn and return NULL + * (main page will be displayed) */ +{ struct trackDb *varTdb = tdbForTrack(database, variantTrack, &fullTrackList); if (varTdb == NULL) { if (isHubTrack(variantTrack)) - warn("Can't find hub track %s; try the \"check hub\" button in " + warn("Can't find hub track '%s'; try the \"check hub\" button in " "<A href=\"%s?%s&%s=%s\">Track Data Hubs</A>.", variantTrack, hgHubConnectName(), cartSidUrlString(cart), hgHubConnectCgiDestUrl, cgiScriptName()); else - warn("Can't find tdb for variant track %s", variantTrack); - doUi(); - return; + warn("Can't find tdb for variant track '%s'", variantTrack); } +else checkVariantTrack(varTdb); -int maxVarRows = cartUsualInt(cart, "hgva_variantLimit", 10); -struct annoStreamer *primary = streamerFromTrack(assembly, varTdb->table, varTdb, chrom, - maxVarRows); +return varTdb; +} +void doQuery() +/* Translate simple form inputs into anno* components and execute query. */ +{ +char *chrom = NULL; +uint start = 0, end = 0; +if (sameString(regionType, hgvaRegionTypeRange)) + getCartPosOrDie(&chrom, &start, &end); +struct annoAssembly *assembly = getAnnoAssembly(database); + +char *geneTrack = cartString(cart, "hgva_geneTrack"); struct trackDb *geneTdb = tdbForTrack(database, geneTrack, &fullTrackList); if (geneTdb == NULL) { warn("Can't find tdb for gene track %s", geneTrack); doUi(); return; } + +int maxVarRows = cartUsualInt(cart, "hgva_variantLimit", 10); +struct annoStreamer *primary = NULL; +char *primaryLongLabel = NULL; +char *variantTrack = cartString(cart, "hgva_variantTrack"); +if (sameString(variantTrack, hgvaSampleVariants)) + { + primary = makeSampleVariantsStreamer(assembly, geneTdb, maxVarRows); + primaryLongLabel = hgvaSampleVariantsLabel; + } +else + { + struct trackDb *varTdb = getVariantTrackDb(variantTrack); + if (varTdb == NULL) + { + doUi(); + return; + } + primary = streamerFromTrack(assembly, varTdb->table, varTdb, chrom, maxVarRows); + primaryLongLabel = varTdb->longLabel; + } + enum annoGratorOverlap geneOverlapRule = agoMustOverlap; struct annoGrator *gpVarGrator = gratorFromTrackDb(assembly, geneTdb->table, geneTdb, chrom, NO_MAXROWS, primary->asObj, geneOverlapRule); setGpVarFuncFilter(gpVarGrator); // Some grators may be used as both filters and output values. To avoid making // multiple grators for the same source, hash them by trackName: struct hash *gratorsByName = hashNew(8); struct annoGrator *snpGrator = NULL; char *snpDesc = NULL; if (cartUsualBoolean(cart, "hgva_rsId", FALSE)) snpGrator = gratorForSnpBed4(gratorsByName, "", assembly, chrom, agoNoConstraint, &snpDesc); // Now construct gratorList in the order in which annoFormatVep wants to see them, // i.e. first the gpVar, then the snpNNN, then whatever else: struct annoGrator *gratorList = NULL; slAddHead(&gratorList, gpVarGrator); if (snpGrator != NULL) slAddHead(&gratorList, snpGrator); // Text or HTML output? char *outFormat = cartUsualString(cart, "hgva_outFormat", "vepTab"); boolean doHtml = sameString(outFormat, "vepHtml"); // Initialize VEP formatter: struct annoFormatter *vepOut = annoFormatVepNew("stdout", doHtml, - primary, varTdb->longLabel, + primary, primaryLongLabel, (struct annoStreamer *)gpVarGrator, geneTdb->longLabel, (struct annoStreamer *)snpGrator, snpDesc); addOutputTracks(&gratorList, gratorsByName, vepOut, assembly, chrom, doHtml); addFilterTracks(&gratorList, gratorsByName, assembly, chrom); slReverse(&gratorList); if (doHtml) { webStart(cart, database, "Annotated Variants in VEP/HTML format"); } else {