582e9ba06d359ee167ca2a60b495cd6d4fe9d69f angie Wed Feb 12 10:56:52 2014 -0800 In snp125+ track controls, don't show options for class and validationcodes that don't appear in the table. For example, validation codes by-hapmap and by-1000genomes are not applicable for non-human genomes; and only a subset of the class codes are used, sometimes a small subset. refs #12490 diff --git src/hg/hgTrackUi/hgTrackUi.c src/hg/hgTrackUi/hgTrackUi.c index fb77ce3..6e09033 100644 --- src/hg/hgTrackUi/hgTrackUi.c +++ src/hg/hgTrackUi/hgTrackUi.c @@ -230,82 +230,183 @@ safef(cartVar, sizeof(cartVar), "%s.include_%s", track, attributeVar); jsMakeCheckboxGroupSetClearButton(cartVar, TRUE); puts(" "); jsMakeCheckboxGroupSetClearButton(cartVar, FALSE); printf("</TD></TR>\n<TR><TD>\n"); boolean foundInCart = FALSE; struct slName *selectedAttributes = snp125FilterFromCart(cart, track, attributeVar, &foundInCart); // Include all by default: if (! foundInCart) selectedAttributes = slNameListFromStringArray(values, menuSize); cgiMakeCheckboxGroupWithVals(cartVar, labels, values, menuSize, selectedAttributes, SNP125_FILTER_COLUMNS); printf("</TD></TR>\n"); } -static char *commaSepFromSqlSetTypeDecl(struct sqlFieldInfo *fi, char *table) -/* Destructively prepare fi->type for chopCommas: strip initial "set(" and final ")", - * informatively errAborting if not found, and strip the single-quote characters - * that mysql puts around each field. */ +static char *commaSepFromSqlEnumOrSetTypeDecl(struct sqlFieldInfo *fi, char *type, char *table) +/* Destructively prepare fi->type for chopCommas. type is either "enum" or "set". + * Strip the initial "set(" or "enum(" and final ")", informatively errAborting if not found, + * and strip the single-quote characters that mysql puts around each field. */ +{ +if (sameString(type, "enum")) + { + if (!startsWith("enum(", fi->type)) + errAbort("Expected %s.%s's type to begin with 'enum(' but got '%s'", + table, fi->field, fi->type); + } +else if (sameString(type, "set")) { if (!startsWith("set(", fi->type)) errAbort("Expected %s.%s's type to begin with 'set(' but got '%s'", table, fi->field, fi->type); -char *vals = fi->type + strlen("set("); + } +char *vals = fi->type + strlen(type) + 1; char *rightParen = strrchr(vals, ')'); if (rightParen == NULL || rightParen[1] != '\0') errAbort("Expected %s.%s's type to end with ')' but got '%s'", table, fi->field, fi->type); else *rightParen = '\0'; stripChar(vals, '\''); return vals; } +static struct slName *snp125FixClassGlobals(struct trackDb *tdb) +/* Fix snp125Class* global variables to contain only the classes that are present + * in the SQL enum type definition. Return a list of classes that are not present. */ +{ +struct sqlConnection *conn = hAllocConn(database); +struct sqlFieldInfo *fi, *fiList = sqlFieldInfoGet(conn, tdb->table); +hFreeConn(&conn); +struct slName *unusedList = NULL; +boolean foundClass = FALSE; +for (fi = fiList; fi != NULL; fi = fi->next) + { + if (sameString(fi->field, "class")) + { + foundClass = TRUE; + char *vals = commaSepFromSqlEnumOrSetTypeDecl(fi, "enum", tdb->table); + char *values[64]; // max 11 in older tables + int valCount = chopCommas(vals, values); + char *labels[valCount]; + char *defaults[valCount]; + char *oldVars[valCount]; + // Use labels from old static array + int i; + for (i = 0; i < valCount; i++) + { + int oldIx = stringArrayIx(values[i], snp125ClassDataName, snp125ClassArraySize); + labels[i] = snp125ClassLabels[oldIx]; + defaults[i] = snp125ClassDefault[oldIx]; + oldVars[i] = snp125ClassOldColorVars[oldIx]; + } + // Make a list of unused values; + for (i = 0; i < snp125ClassArraySize; i++) + { + if (stringArrayIx(snp125ClassDataName[i], values, valCount) < 0) + slAddHead(&unusedList, slNameNew(snp125ClassDataName[i])); + } + // Now overwrite old globals with the correct contents. + snp125ClassArraySize = valCount; + for (i = 0; i < valCount; i++) + { + snp125ClassDataName[i] = cloneString(values[i]); + snp125ClassLabels[i] = cloneString(labels[i]); + snp125ClassDefault[i] = cloneString(defaults[i]); + snp125ClassOldColorVars[i] = cloneString(oldVars[i]); + } + } + } +if (! foundClass) + errAbort("Didn't find definition of func field in %s", tdb->table); +return unusedList; +} + +static void snp125MakeHiddenInputsForUnused(char *cartVar, struct slName *unusedList) +/* If this db's snpNNN table uses only a small subset of the global arrays, but some + * other db's snpNNN table uses a larger subset, we don't want to have the effect of + * turning off the checkboxes that aren't used in this db's snpNNN. So make hidden + * inputs to pretend that all unused values' checkboxes are checked. */ +{ +struct slName *unused; +for (unused = unusedList; unused != NULL; unused = unused->next) + cgiMakeHiddenVar(cartVar, unused->name); +} + static void snp137PrintFunctionFilterControls(struct trackDb *tdb) /* As of snp137, show func filter choices based on sql field set * values and Sequence Ontology (SO) terms so we won't have to * hardcode menus as new functional categories are added. */ { struct sqlConnection *conn = hAllocConn(database); struct sqlFieldInfo *fi, *fiList = sqlFieldInfoGet(conn, tdb->table); hFreeConn(&conn); for (fi = fiList; fi != NULL; fi = fi->next) { if (sameString(fi->field, "func")) { - char *vals = commaSepFromSqlSetTypeDecl(fi, tdb->table); + char *vals = commaSepFromSqlEnumOrSetTypeDecl(fi, "set", tdb->table); char *values[128]; // 22 values as of snp137 int valCount = chopCommas(vals, values); char *labels[valCount]; int i; for (i = 0; i < valCount; i++) { if (sameString(values[i], "unknown")) labels[i] = "Unknown"; else labels[i] = snpMisoLinkFromFunc(values[i]); } snp125PrintFilterControls(tdb->track, "Function", "func", labels, values, valCount); - return;; + return; } } errAbort("Didn't find definition of func field in %s", tdb->table); } +int snp125ValidArraySize(int version) +/* Figure out how many validation options are applicable to this database and version. */ +{ +// Cache result since it costs a mysql query and won't change +static int size = 0; +if (size == 0) + { + size = snp125ValidArraySizeNonHuman; + if (sameString(hOrganism(database), "Human")) + { + size = snp125ValidArraySizeHuman; + if (version < 130) + size--; // no by-1000genomes + } + } +return size; +} + +static void snp125MakeHiddenInputsForValid(char *cartVar, int version) +/* Non-human dbs' snpNNN tables use only a subset of the validation codes, but human dbs' + * snpNNN tables use all of them. When making options for non-human dbs, we don't want + * to have the effect of turning off the checkboxes that aren't used (but would be for human). + * So make hidden inputs to pretend that all unused values' checkboxes are checked. */ +{ +int i; +for (i = snp125ValidArraySize(version); i < snp125ValidArraySizeHuman; i++) + cgiMakeHiddenVar(cartVar, snp125ValidDataName[i]); +} + static void snp125PrintFilterControlSection(struct trackDb *tdb, int version, - boolean molTypeHasMito) + boolean molTypeHasMito, + struct slName *snp125UnusedClasses) /* Print a collapsible section of filtering controls on SNP properties, first numeric * and then enum/set. */ { char cartVar[512]; printf("<TR><TD colspan=2><A name=\"filterControls\"></TD></TR>\n"); jsBeginCollapsibleSection(cart, tdb->track, "filterByAttribute", "Filtering Options", FALSE); printf("<BR>\n"); safef(cartVar, sizeof(cartVar), "%s.minAvHet", tdb->track); double minAvHet = cartUsualDouble(cart, cartVar, // Check old cart var name: cartUsualDouble(cart, "snp125AvHetCutoff", SNP125_DEFAULT_MIN_AVHET)); printf("<B>Minimum <A HREF=\"#AvHet\">Average Heterozygosity</A>:</B> "); cgiMakeDoubleVar(cartVar, minAvHet, 6); printf("<BR>\n"); @@ -350,32 +451,36 @@ printf("<B>Filter by attribute:</B><BR>\n"); printf("Check the boxes below to include SNPs with those attributes. " "In order to be displayed, a SNP must pass the filter for each " "category. \n" "Some assemblies may not contain any SNPs that have some of the " "listed attributes.\n" "<BR><BR>\n"); printf("<TABLE border=0 cellspacing=0 cellpadding=0>\n"); if (version <= 127) snp125PrintFilterControls(tdb->track, "Location Type", "locType", snp125LocTypeLabels, snp125LocTypeDataName, snp125LocTypeArraySize); snp125PrintFilterControls(tdb->track, "Class", "class", snp125ClassLabels, snp125ClassDataName, snp125ClassArraySize); +safef(cartVar, sizeof(cartVar), "%s.include_%s", tdb->track, "class"); +snp125MakeHiddenInputsForUnused(cartVar, snp125UnusedClasses); snp125PrintFilterControls(tdb->track, "Validation", "valid", snp125ValidLabels, - snp125ValidDataName, snp125ValidArraySize); + snp125ValidDataName, snp125ValidArraySize(version)); +safef(cartVar, sizeof(cartVar), "%s.include_%s", tdb->track, "valid"); +snp125MakeHiddenInputsForValid(cartVar, version); if (version < 137) { int funcArraySize = (version < 130) ? snp125FuncArraySize : (snp125FuncArraySize - 1); snp125PrintFilterControls(tdb->track, "Function", "func", snp125FuncLabels, snp125FuncDataName, funcArraySize); } else snp137PrintFunctionFilterControls(tdb); int molTypeArraySize = snp125MolTypeArraySize; if (! molTypeHasMito) molTypeArraySize--; snp125PrintFilterControls(tdb->track, "Molecule Type", "molType", snp125MolTypeLabels, snp125MolTypeDataName, molTypeArraySize); if (version >= 132) { @@ -443,31 +548,31 @@ static void snp125ResetColorVarsIfNecessary(struct trackDb *tdb, char *buttonVar, int version) /* If the 'Set defaults' button has been clicked, remove all color-control cart variables. */ { // Note we use CGI, not cart, to detect a click: if (isNotEmpty(cgiOptionalString(buttonVar))) { char cartVar[512]; safef(cartVar, sizeof(cartVar), "%s.colorSource", tdb->track); cartRemove(cart, cartVar); cartRemove(cart, snp125ColorSourceOldVar); snp125RemoveColorVars(cart, snp125LocTypeOldColorVars, TRUE, snp125LocTypeArraySize, tdb->track, "locType"); snp125RemoveColorVars(cart, snp125ClassOldColorVars, TRUE, snp125ClassArraySize, tdb->track, "class"); - snp125RemoveColorVars(cart, snp125ValidOldColorVars, TRUE, snp125ValidArraySize, + snp125RemoveColorVars(cart, snp125ValidOldColorVars, TRUE, snp125ValidArraySizeHuman, tdb->track, "valid"); int funcArraySize = (version < 130) ? snp125FuncArraySize : (snp125FuncArraySize - 1); snp125RemoveColorVars(cart, snp125FuncOldColorVars, TRUE, funcArraySize, tdb->track, "func"); snp125RemoveColorVars(cart, snp125MolTypeOldColorVars, TRUE, snp125MolTypeArraySize, tdb->track, "molType"); snp125RemoveColorVars(cart, snp132ExceptionVarName, FALSE, snp132ExceptionArraySize, tdb->track, "exceptions"); snp125RemoveColorVars(cart, snp132BitfieldVarName, FALSE, snp132BitfieldArraySize, tdb->track, "bitfields"); } } void snp125PrintColorControlSection(struct trackDb *tdb, int version, boolean molTypeHasMito) /* Print a collapsible section of color controls: user selects an attribute to color by, @@ -523,35 +628,37 @@ "options for the feature selected above will be used to color items;\n" "color options for other features will not be shown.\n"); if (version > 127 && colorSourceCart == snp125ColorSourceLocType) colorSourceCart = SNP125_DEFAULT_COLOR_SOURCE; switch (colorSourceCart) { int funcArraySize, excArraySize, molTypeArraySize; case snp125ColorSourceLocType: snp125PrintColorSpec(tdb->track, "locType", snp125LocTypeOldColorVars, TRUE, snp125LocTypeLabels, snp125LocTypeDefault, snp125LocTypeArraySize); break; case snp125ColorSourceClass: snp125PrintColorSpec(tdb->track, "class", snp125ClassOldColorVars, TRUE, - snp125ClassLabels, snp125ClassDefault, snp125ClassArraySize); + snp125ClassLabels, snp125ClassDefault, + snp125ClassArraySize); break; case snp125ColorSourceValid: snp125PrintColorSpec(tdb->track, "valid", snp125ValidOldColorVars, TRUE, - snp125ValidLabels, snp125ValidDefault, snp125ValidArraySize); + snp125ValidLabels, snp125ValidDefault, + snp125ValidArraySize(version)); break; case snp125ColorSourceFunc: funcArraySize = (version < 130) ? snp125FuncArraySize : (snp125FuncArraySize - 1); snp125PrintColorSpec(tdb->track, "func", snp125FuncOldColorVars, TRUE, snp125FuncLabels, snp125FuncDefault, funcArraySize); break; case snp125ColorSourceMolType: molTypeArraySize = snp125MolTypeArraySize; if (! molTypeHasMito) molTypeArraySize--; snp125PrintColorSpec(tdb->track, "molType", snp125MolTypeOldColorVars, TRUE, snp125MolTypeLabels, snp125MolTypeDefault, molTypeArraySize); break; case snp125ColorSourceExceptions: excArraySize = snp132ExceptionArraySize; @@ -589,57 +696,55 @@ if (sameString(*enumVals, "mito")) gotMito = TRUE; enumVals++; } hFreeConn(&conn); return gotMito; } void snp125Ui(struct trackDb *tdb) /* UI for dbSNP version 125 and later. */ { char *orthoTable = snp125OrthoTable(tdb, NULL); int version = snpVersion(tdb->track); char cartVar[512]; jsInit(); - -if (version < 130) - snp125ValidArraySize--; // no by-1000genomes +struct slName *snp125UnusedClasses = snp125FixClassGlobals(tdb); if (isNotEmpty(orthoTable) && hTableExists(database, orthoTable)) { printf("<BR><B>Include Chimp state and observed human alleles in name: </B> "); safef(cartVar, sizeof(cartVar), "%s.extendedNames", tdb->track); snp125ExtendedNames = cartUsualBoolean(cart, cartVar, // Check old cart var name for backwards compatibility w/ old sessions: cartUsualBoolean(cart, "snp125ExtendedNames", FALSE)); cgiMakeCheckBox(cartVar, snp125ExtendedNames); printf("<BR>(If enabled, chimp allele is displayed first, then '>', then human alleles)."); printf("<BR><BR>\n"); } else puts("<BR>"); // Make wrapper table for collapsible sections: puts("<TABLE border=0 cellspacing=0 cellpadding=0>"); snp125OfferGeneTracksForFunction(tdb); boolean molTypeHasMito = snp125CheckMolTypeForMito(tdb); puts("<TR><TD colspan=2><BR></TD></TR>"); -snp125PrintFilterControlSection(tdb, version, molTypeHasMito); +snp125PrintFilterControlSection(tdb, version, molTypeHasMito, snp125UnusedClasses); puts("<TR><TD colspan=2><BR></TD></TR>"); snp125PrintColorControlSection(tdb, version, molTypeHasMito); // End wrapper table for collapsible sections: puts("</TABLE>"); } void snpUi(struct trackDb *tdb) /* Put up UI snp data. */ { int snpSource = 0; int snpMolType = 0; int snpClass = 0; int snpValid = 0; int snpFunc = 0;