329cebf37d4a04c2a490121e21d166cab69e8562 max Sat Jul 25 18:22:02 2026 -0700 hui: warn when a numeric filter setting has no matching filter., refs #37927 A numeric range filter is only discovered from a filter. (or Filter) setting. Emit a warning for orphaned filter settings that will otherwise be silently ignored. diff --git src/hg/lib/hui.c src/hg/lib/hui.c index 03f819249e5..905060ba5c6 100644 --- src/hg/lib/hui.c +++ src/hg/lib/hui.c @@ -6949,41 +6949,81 @@ * Try to guess what the user means. */ { if (sameString("score", field)) return 4; if (sameString("signal", field)) return 6; if (sameString("signalValue", field)) return 6; if (sameString("pValue", field)) return 7; if (sameString("qValue", field)) return 8; return -1; } +static void warnOrphanedRangeFilters(struct trackDb *tdb) +/* A numeric filter is only DISCOVERED from a filter. (or Filter) + * setting -- see FILTER_NUMBER_WILDCARD and tdbGetTrackNumFilters(). A + * filterByRange. or filterLimits. with no matching filter. + * is silently ignored and its control never appears, which is an easy and + * confusing mistake to make. Warn so the track/hub developer sees why a filter + * they configured did not show up. */ +{ +struct slName *orphans = slCat(trackDbSettingsWildMatch(tdb, "filterByRange.*"), + trackDbSettingsWildMatch(tdb, "filterLimits.*")); +struct hash *seen = hashNew(0); +struct slName *s; +for (s = orphans; s != NULL; s = s->next) + { + char *field = strchr(s->name, '.'); + if (field == NULL) + continue; + field++; // skip past the '.' + if (hashLookup(seen, field) != NULL) + continue; + hashAdd(seen, field, NULL); + char setting[512]; + safef(setting, sizeof setting, "filter.%s", field); + if (trackDbSettingClosestToHome(tdb, setting) != NULL) + continue; + safef(setting, sizeof setting, "%s%s", field, FILTER_NUMBER_NAME_CAP); + if (trackDbSettingClosestToHome(tdb, setting) != NULL) + continue; + warn("Track %s: a filterByRange/filterLimits setting exists for field '%s' " + "but there is no filter.%s (the default range), so this filter is not " + "shown. Add 'filter.%s min:max' to enable it.", + tdb->track, field, field, field); + } +hashFree(&seen); +slFreeList(&orphans); +} + static int numericFiltersShowAll(char *db, struct cart *cart, struct trackDb *tdb, boolean *opened, boolean boxed, boolean parentLevel,char *name, char *title, boolean isHighlight) // 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); + warnOrphanedRangeFilters(tdb); + } if (trackDbFilters) { puts("
"); struct trackDbFilter *filter = NULL; struct sqlConnection *conn = NULL; if (!isHubTrack(db)) conn = hAllocConnTrack(db, tdb); struct asObject *as = asForTdb(conn, tdb); hFreeConn(&conn); while ((filter = slPopHead(&trackDbFilters)) != NULL) { char *field = filter->fieldName; char *scoreName = cloneString(filter->name); char *trackDbLabel = getLabelSetting(cart, tdb, field);