2521d696f5073ce8cee3f59f423f161fdb77d550
max
Fri Sep 25 02:48:37 2026 -0700
VCF tracks: the bigBed trackDb filters (filter.*, filterByRange, filterLimits, filterValues, filterType, filterText, filterLabel) now work on INFO fields, plus ID and QUAL. The field list and types come from the VCF header; the bigBed filter code is reused, bigBed behavior unchanged, refs #37617
diff --git src/hg/lib/hui.c src/hg/lib/hui.c
index 54b082a5564..8cacb88d5c3 100644
--- src/hg/lib/hui.c
+++ src/hg/lib/hui.c
@@ -4031,30 +4031,41 @@
safef(labelSetting, sizeof labelSetting, "%s.%s", FILTER_LABEL_NAME_LOW, field);
char *trackDbLabel = cartOrTdbString(cart, tdb, labelSetting, NULL);
if (trackDbLabel == NULL)
{
safef(labelSetting, sizeof labelSetting, "%s.%s", field, FILTER_LABEL_NAME_CAP);
trackDbLabel = cartOrTdbString(cart, tdb, labelSetting, NULL);
}
if (trackDbLabel == NULL)
{
safef(labelSetting, sizeof labelSetting, "%s%s", field, FILTER_LABEL_NAME_CAP);
trackDbLabel = cartOrTdbString(cart, tdb, labelSetting, NULL);
}
return trackDbLabel;
}
+struct asObject *asForTrackDbFilters(struct cart *cart, struct sqlConnection *conn, struct trackDb *tdb)
+/* Return the autoSql object whose column names the trackDb filter settings (filter.,
+ * filterText., filterValues., ...) refer to, or NULL. For VCF tracks these are the
+ * INFO fields declared in the VCF header (plus ID and QUAL), for all other tracks the track's
+ * autoSql. */
+{
+if (tdbIsVcf(tdb))
+ return vcfInfoAsObj(cart, tdb);
+return asForTdb(conn, tdb);
+}
+
static filterBy_t *buildFilterBy(struct trackDb *tdb, struct cart *cart, struct asObject *as, struct trackDbFilter *tdbFilter, char *name)
/* Build a filterBy_t structure from a FilterValues statement. */
{
boolean isHighlight = startsWith("highlightValues.", tdbFilter->name);
char *field = tdbFilter->fieldName;
if (isEmpty(tdbFilter->setting))
errAbort("track %s: FilterValues setting of field '%s' must have a value.", tdb->track, tdbFilter->fieldName);
char *value = cartUsualStringClosestToHome(cart, tdb, FALSE, tdbFilter->name, tdbFilter->setting);
filterBy_t *filterBy;
AllocVar(filterBy);
filterBy->column = cloneString(field);
filterBy->title = cloneString(field); /// title should come from AS file, or trackDb variable
struct asColumn *asCol = (as != NULL) ? asColumnFind(as, field) : NULL;
@@ -4100,31 +4111,31 @@
struct dyString *dy = dyStringNew(128);
dyStringPrintf(dy, "%s.%s.%s", name, isHighlight ? "highlightBy": "filterBy", filterBy->column);
filterBy->htmlName = dy->string;
return filterBy;
}
filterBy_t *filterByValues(struct trackDb *tdb, struct cart *cart, struct trackDbFilter *trackDbFilters, char *name)
/* Build a filterBy_t list from tdb variables of the form *FilterValues */
{
// Not every tdb has an autoSql: superTracks and tracks pointing at a
// bigData file that isn't reachable at UI time both return NULL here.
// That's fine for filterValues.* settings as long as a filterLabel.*
// override is provided; buildFilterBy() already tolerates a NULL `as`.
-struct asObject *as = asForTdb(NULL, tdb);
+struct asObject *as = asForTrackDbFilters(cart, NULL, tdb);
filterBy_t *filterByList = NULL, *filter;
struct trackDbFilter *fieldFilter;
while ((fieldFilter = slPopHead(&trackDbFilters)) != NULL)
{
// slAddTail (not slAddHead) keeps the priority-sorted order from tdbGetTrackFilters
// since filterBySetCfgUi displays the list head-to-tail.
if ((filter = buildFilterBy(tdb, cart, as, fieldFilter, name)) != NULL)
slAddTail(&filterByList, filter);
}
return filterByList;
}
filterBy_t *filterBySetGetGuts(struct trackDb *tdb, struct cart *cart, char *name, char *subName, char *settingName)
// Gets one or more "filterBy" settings (ClosestToHome). returns NULL if not found
{
@@ -7038,31 +7049,31 @@
// Shows all *Filter style filters. Note that these are in random order and have no graceful title
{
int count = 0;
struct trackDbFilter *trackDbFilters = NULL;
if (isHighlight)
trackDbFilters = tdbGetTrackNumHighlights(tdb);
else
trackDbFilters = tdbGetTrackNumFilters(tdb);
if (trackDbFilters)
{
puts("
");
struct trackDbFilter *filter = NULL;
struct sqlConnection *conn = NULL;
if (!isHubTrack(db) && !isGenArk(db))
conn = hAllocConnTrack(db, tdb);
- struct asObject *as = asForTdb(conn, tdb);
+ struct asObject *as = asForTrackDbFilters(cart, conn, tdb);
hFreeConn(&conn);
while ((filter = slPopHead(&trackDbFilters)) != NULL)
{
char *field = filter->fieldName;
char *scoreName = cloneString(filter->name);
char *trackDbLabel = getLabelSetting(cart, tdb, field);
if (as != NULL)
{
struct asColumn *asCol = asColumnFind(as, field);
if (asCol != NULL)
{ // Found label so replace field; strip "|..." suffix used for detail page
field = asCol->comment;
char *pipe = strchr(field, '|');
@@ -7182,31 +7193,31 @@
/* Show all the text filters for this track. */
{
int count = 0;
struct trackDbFilter *trackDbFilters = NULL;
if (isHighlight)
trackDbFilters = tdbGetTrackTextHighlights(tdb);
else
trackDbFilters = tdbGetTrackTextFilters(tdb);
if (trackDbFilters)
{
puts("
");
struct trackDbFilter *filter = NULL;
struct sqlConnection *conn = NULL;
if (!isHubTrack(db) && !isGenArk(db))
conn = hAllocConnTrack(db, tdb);
- struct asObject *as = asForTdb(conn, tdb);
+ struct asObject *as = asForTrackDbFilters(cart, conn, tdb);
hFreeConn(&conn);
while ((filter = slPopHead(&trackDbFilters)) != NULL)
{
char *trackDbLabel = getLabelSetting(cart, tdb, filter->fieldName);
char *value = cartUsualStringClosestToHome(cart, tdb, FALSE, filter->name, filter->setting);
if (as != NULL)
{
struct asColumn *asCol = asColumnFind(as, filter->fieldName);
if (asCol != NULL)
{
if (trackDbLabel == NULL)
trackDbLabel = asCol->comment;
}
else if (defaultFieldLocation(filter->fieldName) < 0)
errAbort("Building filter on field %s which is not in AS file.", filter->fieldName);
@@ -7232,69 +7243,103 @@
printf("");
printf(" \n", tdb->track);
printf("
");
}
// using jquery id= syntax to make sure that selector works even if trackname has a dot in it
jsInlineF("$('[class=\"buttonClear-%s\"]').click( function(ev) { \n"
"$(ev.target).prevAll('input').val('*').trigger('change');\n"
"$(ev.target).prevAll('select').val('%s');\n"
"ev.preventDefault();\n"
"});", tdb->track, FILTERTEXT_WILDCARD);
}
return count;
}
+static boolean trackDbFiltersShowAll(char *db, struct cart *cart, struct trackDb *tdb,
+ boolean *pIsBoxOpened, boolean boxed, boolean parentLevel,
+ char *name, char *title)
+// Show the numeric (filter.*), text (filterText.*) and multi-select (filterValues.*, filterBy)
+// filters of a track, but no highlights. Returns TRUE if any filter was shown.
+{
+boolean gotFilter = FALSE;
+if (numericFiltersShowAll(db, cart, tdb, pIsBoxOpened, boxed, parentLevel, name, title, FALSE) > 0)
+ gotFilter = TRUE;
+
+if (textFiltersShowAll(db, cart, tdb, FALSE))
+ gotFilter = TRUE;
+
+// Add any multi-selects next
+filterBy_t *filterBySet = filterBySetGet(tdb,cart,name);
+if (filterBySet != NULL)
+ {
+ if (!tdbIsComposite(tdb) && cartOptionalString(cart, "ajax") == NULL)
+ jsIncludeFile("hui.js",NULL);
+
+ if (!*pIsBoxOpened) // Note filterBy boxes are not double "boxed",
+ printf("
"); // if there are no other filters
+ filterBySetCfgUi(cart,tdb,filterBySet,TRUE, name);
+ filterBySetFree(&filterBySet);
+ gotFilter = TRUE;
+ }
+return gotFilter;
+}
+
+boolean trackDbFiltersCfgUi(char *db, struct cart *cart, struct trackDb *tdb, char *name,
+ boolean boxed)
+// Put up only the generic trackDb filter controls of a track (filter.*, filterText.*,
+// filterValues.* and their older *Filter forms), without the score filter and highlights.
+// For track types with their own configuration UI, e.g. VCF, where the fields are INFO keys.
+// Returns TRUE if any filter control was shown.
+{
+if (cartOptionalString(cart, "ajax") == NULL)
+ {
+ webIncludeResourceFile("ui.dropdownchecklist.css");
+ jsIncludeFile("ui.dropdownchecklist.js",NULL);
+ jsIncludeFile("ddcl.js",NULL);
+ }
+boolean parentLevel = isNameAtParentLevel(tdb,name);
+boolean isBoxOpened = FALSE;
+boolean gotFilter = trackDbFiltersShowAll(db, cart, tdb, &isBoxOpened, boxed, parentLevel,
+ name, NULL);
+if (isBoxOpened)
+ cfgEndBox(boxed);
+return gotFilter;
+}
+
void scoreCfgUi(char *db, struct cart *cart, struct trackDb *tdb, char *name, char *title,
int maxScore, boolean boxed)
// Put up UI for filtering bed track based on a score
{
char option[256];
if (cartOptionalString(cart, "ajax") == NULL)
{
webIncludeResourceFile("ui.dropdownchecklist.css");
jsIncludeFile("ui.dropdownchecklist.js",NULL);
jsIncludeFile("ddcl.js",NULL);
}
boolean parentLevel = isNameAtParentLevel(tdb,name);
if (parentLevel)
if (trackDbSettingOn(tdb->parent, "noParentConfig"))
return;
boolean skipScoreFilter = FALSE;
-// Numeric filters are first
+// Numeric filters are first, then text filters, then the multi-selects
boolean isBoxOpened = FALSE;
-if (numericFiltersShowAll(db, cart, tdb, &isBoxOpened, boxed, parentLevel, name, title, FALSE) > 0)
- skipScoreFilter = TRUE;
-
-if (textFiltersShowAll(db, cart, tdb, FALSE))
- skipScoreFilter = TRUE;
-
-// Add any multi-selects next
-filterBy_t *filterBySet = filterBySetGet(tdb,cart,name);
-if (filterBySet != NULL)
- {
- if (!tdbIsComposite(tdb) && cartOptionalString(cart, "ajax") == NULL)
- jsIncludeFile("hui.js",NULL);
-
- if (!isBoxOpened) // Note filterBy boxes are not double "boxed",
- printf("
"); // if there are no other filters
- filterBySetCfgUi(cart,tdb,filterBySet,TRUE, name);
- filterBySetFree(&filterBySet);
+if (trackDbFiltersShowAll(db, cart, tdb, &isBoxOpened, boxed, parentLevel, name, title))
skipScoreFilter = TRUE;
- }
// add any highlights:
// Numeric highlights are first
if (numericFiltersShowAll(db, cart, tdb, &isBoxOpened, boxed, parentLevel, name, title, TRUE) > 0)
{
didHighlightSelector = TRUE;
skipScoreFilter = TRUE;
}
if (textFiltersShowAll(db, cart, tdb, TRUE))
{
didHighlightSelector = TRUE;
skipScoreFilter = TRUE;
}