d3752edc12da1bf08427946150f564dbdd5d2254 angie Thu Oct 24 13:55:51 2019 -0700 bigDbSnp track handler code - initial commit. refs #23283 * dnautil: Added trimRefAltLeft to get left-justified trimming (a la VCF not HGVS). * bigBedClick: do hReplaceGbdb up front in parseDetailsTablUrls instead of waiting until endpoint. * trackDbCustom.c: consolidating type-handling for wig/bigWig vs. bigBed-based big*. diff --git src/hg/lib/hui.c src/hg/lib/hui.c index 19b1548..2798893 100644 --- src/hg/lib/hui.c +++ src/hg/lib/hui.c @@ -38,30 +38,31 @@ #include "errCatch.h" #include "samAlignment.h" #include "makeItemsItem.h" #include "bedDetail.h" #include "pgSnp.h" #include "memgfx.h" #include "trackHub.h" #include "gtexUi.h" #include "genbank.h" #include "htmlPage.h" #include "longRange.h" #include "barChartUi.h" #include "interactUi.h" #include "interact.h" #include "hicUi.h" +#include "bigDbSnp.h" #include "customComposite.h" #include "trackVersion.h" #include "hubConnect.h" #include "bigBedFilter.h" #define SMALLBUF 256 #define MAX_SUBGROUP 9 #define ADD_BUTTON_LABEL "add" #define CLEAR_BUTTON_LABEL "clear" #define JBUFSIZE 2048 #define DEF_BUTTON "<IMG id=\"btn_%s\" src=\"../images/%s\" alt=\"%s\">\n" #define DEF_BUTTON_JS "setCheckBoxesThatContain('%s',true,false,'%s','','%s');" \ "setCheckBoxesThatContain('%s',false,false,'%s','_defOff','%s');" @@ -3938,106 +3939,123 @@ printf(" value='%s'",name); if (filterBy->styleFollows) { char *styler = label + strlen(label)+1; if (*styler != '\0') { if (*styler == '#') // Legacy: just the color that follows printf(" style='color: %s;'",styler); else printf(" style='%s'",styler); } } printf(">%s</OPTION>\n",label); } +static boolean filterByColumnIsMultiple(struct cart *cart, struct trackDb *tdb, char *column) +{ +char settingString[4096]; +safef(settingString, sizeof settingString, "%s%s", column, FILTER_TYPE_NAME); +char *setting = cartOrTdbString(cart, tdb, settingString, NULL); +if (setting == NULL) + { + safef(settingString, sizeof settingString, "%s.%s", column, FILTER_TYPE_NAME); + setting = cartOrTdbString(cart, tdb, settingString, FILTERBY_MULTIPLE_LIST_AND); + } +return (sameString(setting, FILTERBY_MULTIPLE) || + sameString(setting, FILTERBY_MULTIPLE_LIST_OR) || + sameString(setting, FILTERBY_MULTIPLE_LIST_AND)); +} + void filterBySetCfgUiGuts(struct cart *cart, struct trackDb *tdb, filterBy_t *filterBySet, boolean onOneLine, char *filterTypeTitle, char *selectIdPrefix, char *allLabel, char *prefix) // Does the UI for a list of filterBy structure for either filterBy or highlightBy controls { if (filterBySet == NULL) return; #define FILTERBY_HELP_LINK "<A HREF=\"../goldenPath/help/multiView.html\" TARGET=ucscHelp>help</A>" int count = slCount(filterBySet); if (count == 1) puts("<TABLE cellpadding=3><TR valign='top'>"); else printf("<B>%s items by:</B> (select multiple categories and items - %s)" - "<TABLE cellpadding=3><TR valign='top'>\n",filterTypeTitle,FILTERBY_HELP_LINK); + "<TABLE cellpadding=3><TR valign='bottom'>\n",filterTypeTitle,FILTERBY_HELP_LINK); if (tdbIsBigBed(tdb)) { char varName[1024]; safef(varName, sizeof(varName), "%s.doAdvanced", tdb->track); puts(" "); printf("<a id='%s' style='text-decoration: underline; color: #121E9A' title='Show advanced options..'>%s<img src='../images/downBlue.png'/></a>" ,varName,"Advanced "); printf("<BR>"); jsInlineF("$(function () { advancedSearchOnChange('%s'); });\n", varName); } filterBy_t *filterBy = NULL; if (cartOptionalString(cart, "ajax") == NULL) { webIncludeResourceFile("ui.dropdownchecklist.css"); jsIncludeFile("ui.dropdownchecklist.js",NULL); jsIncludeFile("ddcl.js",NULL); } -int ix=0; -for(filterBy = filterBySet;filterBy != NULL; filterBy = filterBy->next, ix++) - { - char settingString[4096]; - safef(settingString, sizeof settingString, "%s%s", filterBy->column, FILTER_TYPE_NAME); - char *setting = cartOrTdbString(cart, tdb, settingString, NULL); - if (setting == NULL) - { - safef(settingString, sizeof settingString, "%s.%s", filterBy->column, FILTER_TYPE_NAME); - setting = cartOrTdbString(cart, tdb, settingString, FILTERBY_MULTIPLE_LIST_AND); - } - - boolean isMultiple = sameString(setting, FILTERBY_MULTIPLE) ||sameString(setting, FILTERBY_MULTIPLE_LIST_OR) ||sameString(setting, FILTERBY_MULTIPLE_LIST_AND); +// TODO: columnCount (Number of filterBoxes per row) should be configurable through tdb setting +for (filterBy = filterBySet; filterBy != NULL; filterBy = filterBy->next) + { puts("<TD>"); char selectStatement[4096]; - if (isMultiple) + if (filterByColumnIsMultiple(cart, tdb, filterBy->column)) safef(selectStatement, sizeof selectStatement, " (select multiple items - %s)", FILTERBY_HELP_LINK); else selectStatement[0] = 0; if(count == 1) printf("<B>%s by %s</B>%s",filterTypeTitle,filterBy->title,selectStatement); else printf("<B>%s</B>",filterBy->title); - printf("<BR>\n"); - - if (isMultiple && tdbIsBigBed(tdb)) + puts("</TD>"); + } +puts("</tr><tr>"); +for (filterBy = filterBySet; filterBy != NULL; filterBy = filterBy->next) { + puts("<td>"); + if (filterByColumnIsMultiple(cart, tdb, filterBy->column) && tdbIsBigBed(tdb)) + { + char settingString[4096]; + safef(settingString, sizeof settingString, "%s%s", filterBy->column, FILTER_TYPE_NAME); + char *setting = cartOrTdbString(cart, tdb, settingString, FILTERBY_MULTIPLE_LIST_AND); char cartSettingString[4096]; safef(cartSettingString, sizeof cartSettingString, "%s.%s", prefix, settingString); printf("<div class='advanced' style='display:none'><b>Match if "); cgiMakeRadioButton(cartSettingString, FILTERBY_MULTIPLE_LIST_AND, sameString(setting, FILTERBY_MULTIPLE_LIST_AND)); printf(" all "); cgiMakeRadioButton(cartSettingString, FILTERBY_MULTIPLE_LIST_OR, sameString(setting, FILTERBY_MULTIPLE_LIST_OR)); printf(" one or more match</b></div> "); } - // TODO: columnCount (Number of filterBoxes per row) should be configurable through tdb setting - + puts("</td>"); + } +puts("</tr><tr>"); +int ix=0; +for (filterBy = filterBySet; filterBy != NULL; filterBy = filterBy->next, ix++) + { + puts("<td>"); // value is always "All", even if label is different, to simplify javascript code int valIx = 0; - if (isMultiple) + if (filterByColumnIsMultiple(cart, tdb, filterBy->column)) { printf( "<SELECT id='%s%d' name='%s' multiple style='display: none; font-size:.9em;' class='filterBy'><BR>\n", selectIdPrefix,ix,filterBy->htmlName); printf("<OPTION%s value=\"All\">%s</OPTION>\n", (filterByAllChosen(filterBy)?" SELECTED":""), allLabel); valIx = 1; } else { printf( "<SELECT id='%s%d' name='%s' style='font-size:.9em;'<BR>\n", selectIdPrefix,ix,filterBy->htmlName); valIx = 0; } struct slName *slValue; for (slValue=filterBy->slValues;slValue!=NULL;slValue=slValue->next,valIx++) { char varName[32]; @@ -4061,32 +4079,33 @@ if (filterBy->useIndex || filterBy->valueAndLabel) printf(" value='%s'",name); if (filterBy->styleFollows) { char *styler = label + strlen(label)+1; if (*styler != '\0') { if (*styler == '#') // Legacy: just the color that follows printf(" style='color: %s;'",styler); else printf(" style='%s'",styler); } } printf(">%s</OPTION>\n",label); } - } printf("</SELECT>\n"); + puts("</td>"); + } puts("</TR></TABLE>"); } void filterBySetCfgUi(struct cart *cart, struct trackDb *tdb, filterBy_t *filterBySet, boolean onOneLine, char *prefix) /* Does the filter UI for a list of filterBy structure */ { filterBySetCfgUiGuts(cart, tdb, filterBySet, onOneLine, "Filter", "fbc", "All", prefix); } void highlightBySetCfgUi(struct cart *cart, struct trackDb *tdb, filterBy_t *filterBySet, boolean onOneLine, char *prefix) /* Does the highlight UI for a list of filterBy structure */ { @@ -4273,30 +4292,112 @@ /* printf("<TR valign=center><th align=right>Drawing method:</th><td align=left>"); safef(option, sizeof(option), "%s.%s", name, POPMETHOD); char *popMethodVal = cartOrTdbString(cart, tdb, "popMethod", NULL); cgiMakeDropListFull(option, popMethodLabels, popMethodValues, ArraySize(popMethodValues), popMethodVal, NULL, NULL); */ puts("</td></TR>"); printf("</TABLE>"); cfgEndBox(boxed); } +void labelMakeCheckBox(struct cart *cart, struct trackDb *tdb, char *sym, char *desc, + boolean defaultOn) +/* add a checkbox for the user to select a component of a label (e.g. ID, name, other info). + * NOTE: This does not have a track name argument, so the correct tdb must be passed in: + * if setting is at composite level, then pass in composite tdb, likewise for view. */ +{ +char suffix[512]; +safef(suffix, sizeof(suffix), "label.%s", sym); +boolean option = cartUsualBooleanClosestToHome(cart, tdb, FALSE, suffix, defaultOn); +char cartVar[1024]; +safef(cartVar, sizeof cartVar, "%s.%s", tdb->track, suffix); +cgiMakeCheckBox(cartVar, option); +printf(" %s ", desc); +} + +static void freqSourceSelect(struct cart *cart, struct trackDb *tdb, char *name) +/* Make a select input for preferred source of allele frequencies from + * trackDb setting freqSourceOrder. */ +{ +char *freqSourceOrder = cloneString(trackDbSetting(tdb, "freqSourceOrder")); +if (isEmpty(freqSourceOrder)) + return; +int fsCount = countSeparatedItems(freqSourceOrder, ','); +char *menu[fsCount]; +chopCommas(freqSourceOrder, menu); +boolean parentLevel = isNameAtParentLevel(tdb, name); +char *freqProj = cartOptionalStringClosestToHome(cart, tdb, parentLevel, "freqProj"); +puts("<b>Frequency source/project to use for Minor Allele Frequency (MAF):</b>"); +char cartVar[1024]; +safef(cartVar, sizeof cartVar, "%s.freqProj", name); +cgiMakeDropList(cartVar, menu, ArraySize(menu), freqProj); +puts("<br>"); +} + +static struct trackDb *tdbOrAncestorByName(struct trackDb *tdb, char *name) +/* For reasons Angie cannot fathom, if a composite or view is passed to cfgByCfgType then + * cfgByCfgType passes a leaf subtrack to its callees like bigDbSnpCfgUi. That is why we + * see so many calls to isNameAtParentLevel, which returns true if the tdb was originally + * at the composite or view level, which we can only tell by comparing with the original track name. + * labelMakeCheckBox, called by many handlers in hgTrackUi that must be always top-level + * (or have a special handler that bypasses cfgByCfgType like refSeqComposite), + * is blissfully unaware of this. It uses the same tdb for looking in cart ClosestToHome + * and for making the HTML element's cart var name, trusting that the correct tdb has been + * handed to it. + * So in order for a callee of cfgByCfgType to call labelMakeCheckBox with the correct tdb, + * we need to walk back up comparing name like isNameAtParentLevel does. + * If name doesn't match tdb or any of its ancestors then this returns NULL. */ +{ +struct trackDb *correctTdb; +for (correctTdb = tdb; correctTdb != NULL; correctTdb = correctTdb->parent) + if (startsWithWordByDelimiter(correctTdb->track, '.', name)) + return correctTdb; +return NULL; +} + +void bigDbSnpCfgUi(char *db, struct cart *cart, struct trackDb *leafTdb, char *name, char *title, + boolean boxed) +/* UI for bigDbSnp a.k.a. "dbSNP 2.0". */ +{ +boxed = cfgBeginBoxAndTitle(leafTdb, boxed, title); +freqSourceSelect(cart, leafTdb, name); +puts("<br>"); +puts("<b>Label:</b>"); +struct trackDb *correctTdb = tdbOrAncestorByName(leafTdb, name); +labelMakeCheckBox(cart, correctTdb, "rsId", "rs# identifier", TRUE); +labelMakeCheckBox(cart, correctTdb, "refAlt", "reference/alternate allele", TRUE); +labelMakeCheckBox(cart, correctTdb, "majMin", "major/minor allele", FALSE); +labelMakeCheckBox(cart, correctTdb, "maf", "MAF if available", FALSE); +labelMakeCheckBox(cart, correctTdb, "func", "Most severe functional impact on gene if any", FALSE); +puts("<br>"); +scoreCfgUi(db, cart, leafTdb, name, "", 0, FALSE); +puts("<br>"); +puts("<b>Minimum MAF:</b>"); +boolean parentLevel = isNameAtParentLevel(leafTdb, name); +double minMaf = cartUsualDoubleClosestToHome(cart, leafTdb, parentLevel, "minMaf", 0.0); +char cartVar[1024]; +safef(cartVar, sizeof cartVar, "%s.minMaf", name); +cgiMakeDoubleVarWithLimits(cartVar, minMaf, "MAF", 0, 0.0, 0.5); +puts("range: 0.0 - 0.5"); +cfgEndBox(boxed); +} + void cfgByCfgType(eCfgType cType,char *db, struct cart *cart, struct trackDb *tdb,char *prefix, char *title, boolean boxed) // Methods for putting up type specific cfgs used by composites/subtracks in hui.c { // When only one subtrack, then show it's cfg settings instead of composite/view level settings // This simplifies the UI where hgTrackUi won't have 2 levels of cfg, // while hgTracks still supports rightClick cfg of the subtrack. if (configurableByAjax(tdb,cType) > 0) // Only if subtrack's configurable by ajax do we { // consider this option if (tdbIsComposite(tdb) // called for the composite && !isCustomComposite(tdb) && !tdbIsCompositeView(tdb->subtracks) // and there is no view level && slCount(tdb->subtracks) == 1) // and there is only one subtrack { @@ -4354,30 +4455,32 @@ case cfgLong: longRangeCfgUi(cart, tdb, prefix, title, boxed); break; case cfgSnake: snakeCfgUi(cart, tdb, prefix, title, boxed); break; case cfgPsl: pslCfgUi(db,cart,tdb,prefix,title,boxed); break; case cfgBarChart: barChartCfgUi(db,cart,tdb,prefix,title,boxed); break; case cfgInteract: interactCfgUi(db,cart,tdb,prefix,title,boxed); break; case cfgLollipop: lollyCfgUi(db,cart,tdb,prefix,title,boxed); scoreCfgUi(db, cart,tdb,prefix,title,1000,boxed); break; case cfgHic: hicCfgUi(db,cart,tdb,prefix,title,boxed); break; + case cfgBigDbSnp: bigDbSnpCfgUi(db, cart, tdb, prefix, title, boxed); + break; default: warn("Track type is not known to multi-view composites. type is: %d ", cType); break; } } char *encodeRestrictionDate(char *db,struct trackDb *trackDb,boolean excludePast) // Create a string for ENCODE restriction date of this track // if return is not null, then free it after use { if (!trackDb) return NULL; char *date = NULL; @@ -8893,38 +8996,34 @@ if (isCustomTrack(tdb->track)) { if (ct) { conn = hAllocConn(CUSTOM_TRASH); tableName = ct->dbTableName; } } else if (startsWith("big", tdb->type)) { char *tableName = hTableForTrack(database, tdb->table); struct sqlConnection *conn = hAllocConnTrack(database, tdb); char *bbiFileName = bbiNameFromSettingOrTable(tdb, conn, tableName); hFreeConn(&conn); struct bbiFile *bbi = NULL; - if (startsWith("bigBed", tdb->type) || sameString("bigBarChart", tdb->type) - || sameString("bigMaf", tdb->type) || sameString("bigPsl", tdb->type) - || sameString("bigChain", tdb->type) || sameString("bigGenePred", tdb->type) - || startsWith("bigLolly", tdb->type) - || sameString("bigInteract", tdb->type)) - bbi = bigBedFileOpen(bbiFileName); - else if (startsWith("bigWig", tdb->type)) + if (startsWith("bigWig", tdb->type)) bbi = bigWigFileOpen(bbiFileName); + else + bbi = bigBedFileOpen(bbiFileName); time_t timep = 0; if (bbi) { timep = bbiUpdateTime(bbi); bbiFileClose(&bbi); } printBbiUpdateTime(&timep); } else { tableName = hTableForTrack(database, tdb->table); conn = hAllocConnTrack(database, tdb); } if (tableName) {