3c13015353004b4de688ff6c7d6225e8941f1da7 braney Tue Aug 14 16:28:52 2018 -0700 add <column>FilterValues tdb statemnt allowing for filtering on values diff --git src/hg/lib/hui.c src/hg/lib/hui.c index be3fe1f..14b77c4 100644 --- src/hg/lib/hui.c +++ src/hg/lib/hui.c @@ -3565,77 +3565,35 @@ if (first) *first = strtod(a,NULL); freeMem(a); } if (b!=NULL) { if (second) *second = strtod(b,NULL); freeMem(b); } return TRUE; } return FALSE; } -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 -{ -filterBy_t *filterBySet = NULL; - -char *setting = trackDbSettingClosestToHome(tdb, settingName); -if(setting == NULL) - return NULL; -if ( name == NULL ) - name = tdb->track; - -setting = cloneString(setting); -char *filters[10]; -// multiple filterBys are delimited by space but spaces inside filter can be protected "by quotes" -int filterCount = chopByWhiteRespectDoubleQuotes(setting, filters, ArraySize(filters)); -int ix; -for (ix=0;ix<filterCount;ix++) - { - char *filter = cloneString(filters[ix]); - filterBy_t *filterBy; - AllocVar(filterBy); - char *first = strchr(filter,':'); - if (first != NULL) - *first = '\0'; - else - errAbort("filterBySetGet() expected ':' divider between table column and label: %s", filters[ix]); - filterBy->column = filter; - filter += strlen(filter) + 1; - first = strchr(filter,'='); - if (first != NULL) - *first = '\0'; - else - errAbort("filterBySetGet() expected '=' divider between table column and options list: %s", filters[ix]); - filterBy->title = strSwapChar(filter,'_',' '); // Title does not have underscores - filter += strlen(filter) + 1; - - // Are values indexes to the string titles? - if (filter[0] == '+') +static void chopUpValues(filterBy_t *filterBy) +/* Chop up strings in filterBy or <column>FilterValues statement. We look for optional labels + * and optional CSS inside curly braces. + */ { - filter += 1; - filterBy->useIndex = TRUE; - } - - // Now set up each of the values which may have 1-3 parts (value|label{style}) - // the slName list will have the 3 parts delimited by null value\0label\0style\0 - stripString(filter, "\""); // Remove any double quotes now and chop by commmas - filterBy->slValues = slNameListFromComma(filter); struct slName *val = filterBy->slValues; for (;val!=NULL;val=val->next) { // chip the style off the end of value or value|label char *chipper = strrchr(val->name,'{'); if (chipper != NULL) { if (val == filterBy->slValues) // First one { filterBy->styleFollows = (lastChar(chipper) == '}'); if (filterBy->styleFollows == FALSE) // Must be closed at the end of the string or filterBy->styleFollows = (*(chipper + 1) == '#'); // Legacy: color only } if (filterBy->styleFollows == FALSE) errAbort("filterBy values either all end in {CSS style} or none do."); @@ -3658,30 +3616,140 @@ if (chipper != NULL) { if (val == filterBy->slValues) // First one filterBy->valueAndLabel = TRUE; if (filterBy->valueAndLabel == FALSE) errAbort("filterBy values either all have labels (as value|label) " "or none do."); *chipper++ = 0; // The label is found inside filters->svValues as the next string strSwapChar(chipper,'_',' '); // Title does not have underscores } else if (filterBy->valueAndLabel) errAbort("filterBy values either all have labels in form of value|label " "or none do."); } } +} + +filterBy_t *buildFilterBy(struct trackDb *tdb, struct cart *cart, struct asObject *as, char *filterName) +/* Build a filterBy_t structure from a <column>FilterValues statement. */ +{ +char *setting = trackDbSetting(tdb, filterName); +char *value = cartUsualStringClosestToHome(cart, tdb, FALSE, filterName, setting); +char *field = cloneString(filterName); +int ix = strlen(field) - strlen("FilterValues"); +assert(ix > 0); +field[ix] = '\0'; + +filterBy_t *filterBy; +AllocVar(filterBy); +filterBy->column = field; +filterBy->title = field; /// title should come from AS file, or trackDb variable +struct asColumn *asCol = asColumnFind(as, field); +if (asCol != NULL) + filterBy->title = asCol->comment; +filterBy->useIndex = FALSE; +filterBy->slValues = slNameListFromComma(value); +chopUpValues(filterBy); +if (cart != NULL) + { + char suffix[256]; + safef(suffix, sizeof(suffix), "%s.%s", "filterBy", filterBy->column); + boolean parentLevel = isNameAtParentLevel(tdb,tdb->track); + if (cartLookUpVariableClosestToHome(cart,tdb,parentLevel,suffix,&(filterBy->htmlName))) + { + filterBy->slChoices = cartOptionalSlNameList(cart,filterBy->htmlName); + freeMem(filterBy->htmlName); + } + } + +// Note: cannot use found name above because that may be at a higher (composite/view) level +int len = strlen(tdb->track) + strlen(filterBy->column) + 15; +filterBy->htmlName = needMem(len); +safef(filterBy->htmlName, len, "%s.%s.%s", tdb->track,"filterBy",filterBy->column); +freeMem(setting); + +return filterBy; +} + +filterBy_t *filterByValues(struct trackDb *tdb, struct cart *cart, struct slName *filterValues) +/* Build a filterBy_t list from tdb variables of the form *FilterValues */ +{ +struct asObject *as = asForTdb(NULL, tdb); +filterBy_t *filterByList = NULL, *filter; +struct slName *fieldFilter; +while ((fieldFilter = slPopHead(&filterValues)) != NULL) + { + if ((filter = buildFilterBy(tdb, cart, as, fieldFilter->name)) != NULL) + slAddHead(&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 +{ +// first check to see if this tdb is using "new" FilterValues cart variables +struct slName *filterValues = trackDbSettingsWildMatch(tdb, "*FilterValues"); +if (filterValues) + return filterByValues(tdb, cart, filterValues); + +filterBy_t *filterBySet = NULL; +char *setting = trackDbSettingClosestToHome(tdb, settingName); +if(setting == NULL) + return NULL; +if ( name == NULL ) + name = tdb->track; + +setting = cloneString(setting); +char *filters[10]; +// multiple filterBys are delimited by space but spaces inside filter can be protected "by quotes" +int filterCount = chopByWhiteRespectDoubleQuotes(setting, filters, ArraySize(filters)); +int ix; +for (ix=0;ix<filterCount;ix++) + { + char *filter = cloneString(filters[ix]); + filterBy_t *filterBy; + AllocVar(filterBy); + char *first = strchr(filter,':'); + if (first != NULL) + *first = '\0'; + else + errAbort("filterBySetGet() expected ':' divider between table column and label: %s", filters[ix]); + filterBy->column = filter; + filter += strlen(filter) + 1; + first = strchr(filter,'='); + if (first != NULL) + *first = '\0'; + else + errAbort("filterBySetGet() expected '=' divider between table column and options list: %s", filters[ix]); + filterBy->title = strSwapChar(filter,'_',' '); // Title does not have underscores + filter += strlen(filter) + 1; + + // Are values indexes to the string titles? + if (filter[0] == '+') + { + filter += 1; + filterBy->useIndex = TRUE; + } + + // Now set up each of the values which may have 1-3 parts (value|label{style}) + // the slName list will have the 3 parts delimited by null value\0label\0style\0 + stripString(filter, "\""); // Remove any double quotes now and chop by commmas + filterBy->slValues = slNameListFromComma(filter); + + chopUpValues(filterBy); slAddTail(&filterBySet,filterBy); // Keep them in order (only a few) if (cart != NULL) { char suffix[256]; safef(suffix, sizeof(suffix), "%s.%s", subName, filterBy->column); boolean parentLevel = isNameAtParentLevel(tdb,name); if (cartLookUpVariableClosestToHome(cart,tdb,parentLevel,suffix,&(filterBy->htmlName))) { filterBy->slChoices = cartOptionalSlNameList(cart,filterBy->htmlName); freeMem(filterBy->htmlName); } }