e260a83c1023f097f5fb1f3c0a8b3d464c536c3c
braney
  Wed Sep 25 20:52:50 2019 -0700
support <columnName>.Filter* format for trackDb filtering variables

diff --git src/hg/lib/hui.c src/hg/lib/hui.c
index c00d0e2..09ca6aa 100644
--- src/hg/lib/hui.c
+++ src/hg/lib/hui.c
@@ -3620,47 +3620,60 @@
             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.");
         }
     }
 }
 
+char *extractFieldName(char *cartVariable, char *filterType)
+/* Extract field name from a filter cart variable.  Variables can either be
+ * <columnName>Filter* or <columnName>.Filter* */
+{
+char *field = cloneString(cartVariable);
+int ix = strlen(field) - strlen(filterType); 
+assert(ix > 1);
+field[ix] = '\0';
+if (field[ix - 1] == '.')
+    field[ix - 1] = '\0';
+
+return field;
+}
+
 filterBy_t *buildFilterBy(struct trackDb *tdb, struct cart *cart, struct asObject *as, char *filterName, char *name)
 /* 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(FILTER_VALUES_NAME);
-assert(ix > 0);
-field[ix] = '\0';
+char *field = extractFieldName(filterName, FILTER_VALUES_NAME);
 
 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;
+else
+    errAbort("Building filter on field %s which is not in AS file.", field);
 filterBy->useIndex = FALSE;
 filterBy->slValues = slNameListFromCommaEscaped(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);
         }
     }
 
@@ -5872,56 +5885,55 @@
     if (!isHubTrack(db))
         conn = hAllocConnTrack(db, tdb);
     struct asObject *as = asForTdb(conn, tdb);
     hFreeConn(&conn);
 #endif///ndef EXTRA_FIELDS_SUPPORT
 
     while ((filter = slPopHead(&filterSettings)) != NULL)
         {
         if (differentString(filter->name,NO_SCORE_FILTER))
             {
             // Determine floating point or integer
             char *setting = trackDbSetting(tdb, filter->name);
             boolean isFloat = (strchr(setting,'.') != NULL);
 
             char *scoreName = cloneString(filter->name);
-            char *field = filter->name;   // No need to clone: will be thrown away at end of cycle
-            int ix = strlen(field) - strlen(FILTER_NUMBER_NAME);
-            assert(ix > 0);
-            field[ix] = '\0';
+            char *field = extractFieldName(filter->name, FILTER_NUMBER_NAME);
 
         #ifdef EXTRA_FIELDS_SUPPORT
             if (extras != NULL)
                 {
                 struct extraField *extra = extraFieldsFind(extras, field);
                 if (extra != NULL)
                     { // Found label so replace field
                     field = extra->label;
                     if (!isFloat)
                         isFloat = (extra->type == ftFloat);
                     }
                 }
         #else///ifndef EXTRA_FIELDS_SUPPORT
             if (as != NULL)
                 {
                 struct asColumn *asCol = asColumnFind(as, field);
                 if (asCol != NULL)
                     { // Found label so replace field
                     field = asCol->comment;
                     if (!isFloat)
                         isFloat = asTypesIsFloating(asCol->lowType->type);
                     }
+                else 
+                    errAbort("Building filter on field %s which is not in AS file.", field);
                 }
         #endif///ndef EXTRA_FIELDS_SUPPORT
             // FIXME: Label munging should be localized to showScoreFilter()
             //  when that function is simplified
             char varName[256];
             char label[128];
             safef(varName, sizeof(varName), "%s%s", scoreName, _BY_RANGE);
             boolean filterByRange = trackDbSettingClosestToHomeOn(tdb, varName);
             safef(label, sizeof(label),"%s%s", filterByRange ? "": "Minimum ", field);
 
             showScoreFilter(cart,tdb,opened,boxed,parentLevel,name,title,label,scoreName,isFloat);
             freeMem(scoreName);
             count++;
             }
         slNameFree(&filter);
@@ -5972,38 +5984,42 @@
         return TRUE;
     }
 if (!blocked)  // scoreFilter is implicit unless NO_SCORE_FILTER
     return TRUE;
 
 return FALSE;
 }
 
 
 void textFiltersShowAll(char *db, struct cart *cart, struct trackDb *tdb)
 /* Show all the text filters for this track. */
 {
 struct slName *filter, *filterSettings = trackDbSettingsWildMatch(tdb, FILTER_TEXT_WILDCARD);
 if (filterSettings)
     {
+    struct sqlConnection *conn = NULL;
+    if (!isHubTrack(db))
+        conn = hAllocConnTrack(db, tdb);
     while ((filter = slPopHead(&filterSettings)) != NULL)
         {
         char *setting = trackDbSetting(tdb, filter->name);
         char *value = cartUsualStringClosestToHome(cart, tdb, FALSE, filter->name, setting);
-        char *field = cloneString(filter->name);
-        int ix = strlen(field) - strlen(FILTER_TEXT_NAME);
-        assert(ix > 0);
-        field[ix] = '\0';
+        char *field = extractFieldName(filter->name, FILTER_TEXT_NAME);
+        struct asObject *as = asForTdb(conn, tdb);
+        struct asColumn *asCol = asColumnFind(as, field);
+        if (asCol == NULL)
+            errAbort("Building filter on field %s which is not in AS file.", field);
 
         printf("<P><B>Filter items in '%s' field: ", field);
 
         char cgiVar[128];
         safef(cgiVar,sizeof(cgiVar),"%s.%s",tdb->track,filter->name);
         cgiMakeTextVar(cgiVar, value, 45);
 
         char settingString[4096];
         safef(settingString, sizeof settingString, "%s%s", field, FILTER_TYPE_NAME);
         setting = cartOrTdbString(cart, tdb, settingString, FILTERTEXT_WILDCARD);
         safef(cgiVar,sizeof(cgiVar),"%s.%s",tdb->track,settingString);
         printf(" using ");
         printf("<SELECT name='%s'> ", cgiVar);
         printf("<OPTION %s>%s</OPTION>", sameString(setting, FILTERTEXT_WILDCARD) ? "SELECTED" : "",  FILTERTEXT_WILDCARD );
         printf("<OPTION %s>%s</OPTION>", sameString(setting, FILTERTEXT_REGEXP) ? "SELECTED" : "",  FILTERTEXT_REGEXP );