eef3ac987538affd38eb38ccddf6ce6721f4c6f4 jcasper Sun Nov 23 22:03:29 2025 -0800 Removing bigCompositeUpdate CGI (folded into cartDump), and some kent-ifying of the remaining code. refs #36320 diff --git src/hg/hgTrackUi/hgTrackUi.c src/hg/hgTrackUi/hgTrackUi.c index dcba862c95d..fea1821b26e 100644 --- src/hg/hgTrackUi/hgTrackUi.c +++ src/hg/hgTrackUi/hgTrackUi.c @@ -2003,36 +2003,36 @@ char options[7][256]; int thisHeightPer; float thisMinYRange, thisMaxYRange; char *interpolate, *fill; char **row; int rowOffset; struct sample *sample; struct sqlResult *sr; char option[64]; struct sqlConnection *conn = hAllocConn(database); char newRow = 0; -snprintf( &options[0][0], 256, "%s.heightPer", tdb->track ); -snprintf( &options[1][0], 256, "%s.linear.interp", tdb->track ); -snprintf( &options[3][0], 256, "%s.fill", tdb->track ); -snprintf( &options[4][0], 256, "%s.min.cutoff", tdb->track ); -snprintf( &options[5][0], 256, "%s.max.cutoff", tdb->track ); -snprintf( &options[6][0], 256, "%s.interp.gap", tdb->track ); +safef(&options[0][0], 256, "%s.heightPer", tdb->track ); +safef(&options[1][0], 256, "%s.linear.interp", tdb->track ); +safef(&options[3][0], 256, "%s.fill", tdb->track ); +safef(&options[4][0], 256, "%s.min.cutoff", tdb->track ); +safef(&options[5][0], 256, "%s.max.cutoff", tdb->track ); +safef(&options[6][0], 256, "%s.interp.gap", tdb->track ); thisHeightPer = atoi(cartUsualString(cart, &options[0][0], "50")); interpolate = cartUsualString(cart, &options[1][0], "Linear Interpolation"); fill = cartUsualString(cart, &options[3][0], "1"); thisMinYRange = atof(cartUsualString(cart, &options[4][0], "0.0")); thisMaxYRange = atof(cartUsualString(cart, &options[5][0], "1000.0")); printf("<p><b>Interpolation: </b> "); wiggleDropDown(&options[1][0], interpolate ); printf(" "); printf("<br><br>"); printf(" <b>Fill Blocks</b>: "); cgiMakeRadioButton(&options[3][0], "1", sameString(fill, "1")); printf(" on "); @@ -2042,31 +2042,31 @@ printf("<p><b>Track Height</b>: "); cgiMakeIntVar(&options[0][0], thisHeightPer, 5 ); printf(" pixels"); printf("<p><b>Vertical Range</b>: \nmin:"); cgiMakeDoubleVar(&options[4][0], thisMinYRange, 6 ); printf(" max:"); cgiMakeDoubleVar(&options[5][0], thisMaxYRange, 6 ); printf("<p><b>Toggle Species on/off</b><br>" ); sr = hRangeQuery(conn, tdb->table, chromosome, 0, 1877426, NULL, &rowOffset); while ((row = sqlNextRow(sr)) != NULL) { sample = sampleLoad(row + rowOffset); - snprintf( option, sizeof(option), "zooSpecies.%s", sample->name ); + safef(option, sizeof(option), "zooSpecies.%s", sample->name ); if( cartUsualBoolean(cart, option, TRUE ) ) cgiMakeCheckBox(option, TRUE ); else cgiMakeCheckBox(option, FALSE ); printf("%s ", sample->name ); newRow++; if( newRow % 5 == 0 ) printf("<br>"); sampleFree(&sample); } } void chainColorUi(struct trackDb *tdb) @@ -2273,36 +2273,36 @@ { char *enz = cartUsualString(cart, cutterVar, cutterDefault); puts("<P><B>Filter display by enzymes (separate with commas):</B><BR>"); cgiMakeTextVar(cutterVar, enz, 100); } void genericWiggleUi(struct trackDb *tdb, int optionNum ) /* put up UI for any standard wiggle track (a.k.a. sample track)*/ { char options[7][256]; int thisHeightPer, thisLineGap; float thisMinYRange, thisMaxYRange; char *interpolate, *fill; -snprintf( &options[0][0], 256, "%s.heightPer", tdb->track ); -snprintf( &options[1][0], 256, "%s.linear.interp", tdb->track ); -snprintf( &options[3][0], 256, "%s.fill", tdb->track ); -snprintf( &options[4][0], 256, "%s.min.cutoff", tdb->track ); -snprintf( &options[5][0], 256, "%s.max.cutoff", tdb->track ); -snprintf( &options[6][0], 256, "%s.interp.gap", tdb->track ); +safef(&options[0][0], 256, "%s.heightPer", tdb->track ); +safef(&options[1][0], 256, "%s.linear.interp", tdb->track ); +safef(&options[3][0], 256, "%s.fill", tdb->track ); +safef(&options[4][0], 256, "%s.min.cutoff", tdb->track ); +safef(&options[5][0], 256, "%s.max.cutoff", tdb->track ); +safef(&options[6][0], 256, "%s.interp.gap", tdb->track ); thisHeightPer = atoi(cartUsualString(cart, &options[0][0], "50")); interpolate = cartUsualString(cart, &options[1][0], "Linear Interpolation"); fill = cartUsualString(cart, &options[3][0], "1"); thisMinYRange = atof(cartUsualString(cart, &options[4][0], "0.0")); thisMaxYRange = atof(cartUsualString(cart, &options[5][0], "1000.0")); thisLineGap = atoi(cartUsualString(cart, &options[6][0], "200")); printf("<p><b>Interpolation: </b> "); wiggleDropDown(&options[1][0], interpolate ); printf(" "); printf("<br><br>"); printf(" <b>Fill Blocks</b>: "); cgiMakeRadioButton(&options[3][0], "1", sameString(fill, "1")); @@ -2340,36 +2340,36 @@ wigCfgUi(cart,tdb,tdb->track, "<span style='text-decoration:underline;'>Graph Plotting options:</span>",FALSE); printf("<p><b><span style='text-decoration:underline;'>" "View/Hide individual cell lines:</span></b>"); } void humMusUi(struct trackDb *tdb, int optionNum ) /* put up UI for human/mouse conservation sample tracks (humMusL and musHumL)*/ { char options[7][256]; int thisHeightPer, thisLineGap; float thisMinYRange, thisMaxYRange; char *interpolate, *fill; -snprintf( &options[0][0], 256, "%s.heightPer", tdb->track ); -snprintf( &options[1][0], 256, "%s.linear.interp", tdb->track ); -snprintf( &options[3][0], 256, "%s.fill", tdb->track ); -snprintf( &options[4][0], 256, "%s.min.cutoff", tdb->track ); -snprintf( &options[5][0], 256, "%s.max.cutoff", tdb->track ); -snprintf( &options[6][0], 256, "%s.interp.gap", tdb->track ); +safef(&options[0][0], 256, "%s.heightPer", tdb->track ); +safef(&options[1][0], 256, "%s.linear.interp", tdb->track ); +safef(&options[3][0], 256, "%s.fill", tdb->track ); +safef(&options[4][0], 256, "%s.min.cutoff", tdb->track ); +safef(&options[5][0], 256, "%s.max.cutoff", tdb->track ); +safef(&options[6][0], 256, "%s.interp.gap", tdb->track ); thisHeightPer = atoi(cartUsualString(cart, &options[0][0], "50")); interpolate = cartUsualString(cart, &options[1][0], "Linear Interpolation"); fill = cartUsualString(cart, &options[3][0], "1"); thisMinYRange = atof(cartUsualString(cart, &options[4][0], "0.0")); thisMaxYRange = atof(cartUsualString(cart, &options[5][0], "8.0")); thisLineGap = atoi(cartUsualString(cart, &options[6][0], "200")); printf("<p><b>Interpolation: </b> "); wiggleDropDown(&options[1][0], interpolate ); printf(" "); printf("<br><br>"); printf(" <b>Fill Blocks</b>: "); cgiMakeRadioButton(&options[3][0], "1", sameString(fill, "1")); @@ -2833,207 +2833,218 @@ char **ancestors; AllocArray(ancestors, count); count = 0; for(sp=speciesList; sp; sp = sp->next) { ancestors[count] = sp->name; count++; } char *coalescent = cartOptionalString(cart, codeVarName); printf("<B>Set Coalescent Ancestor to:</B>"); cgiMakeDropListFull(codeVarName, ancestors, ancestors, count, coalescent, NULL, NULL); } #endif -static char ** -parseDataTypes(struct trackDb *tdb, int *out_count) { +static char **parseDataTypes(struct trackDb *tdb, int *out_count) +{ // ADS: almost certainly a function already exists to do this?? /* Get entries 'nDataTypes' and 'dataTypes' from the 'settings' * field for the given 'trackDb' entry. 'nDataTypes' is a count, and * 'dataTypes' is a space separated list of words, each indicating a * data type. This function returns the array of data types. The * caller must free the returned array and each member. If * nDataTypes does not match the number of words parsed from * 'dataTypes' it is an error. Return value is NULL on error. */ - const char *n_datatypes_str = - (const char *)hashMustFindVal(tdb->settingsHash, "nDataTypes"); +const char *n_datatypes_str = (const char *)hashMustFindVal(tdb->settingsHash, "nDataTypes"); const int n_datatypes = atoi(n_datatypes_str); - if (n_datatypes <= 0) return NULL; +if (n_datatypes <= 0) + return NULL; - const char *datatypes_str = - (const char *)hashMustFindVal(tdb->settingsHash, "dataTypes"); - if (!datatypes_str) return NULL; +const char *datatypes_str = (const char *)hashMustFindVal(tdb->settingsHash, "dataTypes"); +if (!datatypes_str) + return NULL; // returned, must be freed char **datatypes = calloc(n_datatypes, sizeof(char *)); - if (!datatypes) return NULL; +if (!datatypes) + return NULL; const char *name_start = datatypes_str; int observed_count = 0; - for (int i = 0; i < n_datatypes; ++i) { +for (int i = 0; i < n_datatypes; ++i) + { const char *name_end = strchr(name_start, ' '); - if (!name_end) name_end = strchr(name_start, '\0'); + if (!name_end) + name_end = strchr(name_start, '\0'); const int name_len = name_end - name_start; - if (name_len <= 0) break; // skip empty segments + if (name_len <= 0) + break; // skip empty segments datatypes[i] = calloc(name_len + 1, sizeof(char)); - if (!datatypes[i]) { // cleanup on failure + if (!datatypes[i]) + { // cleanup on failure for (int j = 0; j < i; ++j) free(datatypes[j]); free(datatypes); return NULL; } memcpy(datatypes[i], name_start, name_len); datatypes[i][name_len] = '\0'; ++observed_count; - if (*name_end == '\0') break; + if (*name_end == '\0') + break; name_start = name_end + 1; } - if (n_datatypes != observed_count) { // cleanup on failure - for (int i = 0; i < n_datatypes; ++i) free(datatypes[i]); +if (n_datatypes != observed_count) + { // cleanup on failure + for (int i = 0; i < n_datatypes; ++i) + free(datatypes[i]); free(datatypes); return NULL; } - if (out_count) *out_count = observed_count; +if (out_count) + *out_count = observed_count; return datatypes; } unsigned int cartDbParseId(char *, char **); // ADS: avoid extra include #define COMMA_IF(x) (((x)++) ? "," : "") // ADS: pattern for JSON comma -static void -bigCompositeCfgUi(struct trackDb *tdb) { - /* ADS: How bigComposite differs from other track types - * - track name is bigComposite (!) +static void facetedCompositeUi(struct trackDb *tdb) +{ +/* ADS: How facetedComposite differs from other track types + * - compositeTrack track setting is "faceted" * - Required fields in the 'settings' longblob for the trackDb entry: * - 'metaDataUrl': a non-blocked URL (can be server-local) with - * metadata to generate the table. + * metadata to generate the table. This might change to an existing metadata + * setting in the future. * - 'nDataTypes': positive integer counting the data types for each sample. * - 'dataTypes': the names of the data types, ordered and space separated. * * This function will embed sessionDb.settings/cart data in the * generated HTML. Instead of embedding all relevant tracks, it * parses the tracks named like: * - * bigCompositeName_dataElementName_dataTypeName_sel=1 + * facetedCompositeName_dataElementName_dataTypeName_sel=1 * * And only sends unique dataElement and dataTypes, which might be - * much smaller. The bigComposite assumes that selection of + * much smaller. The faceted composite assumes that selection of * dataTypes applies to all dataElements, but within the cart, these * are separate tracks, and each must be present to be drawn. But * the JS doesn't need the product {dataType} x {dataElement}, just * the union {datType} U {dataElement}. These are put in two arrays * in a JSON section of the HTML. */ const int token_size = 64; const int query_buff_size = 256; // html elements for the controls page (from singleCellMerged) const char pageStyle[] = "<style>body.cgi { background: #F0F0F0; }" "table.hgInside { background: #FFFFFF; }</style>"; const char placeholderDiv[] = "<div id='metadata-placeholder'></div>\n"; const char openJSON[] = "<script id=\"app-data\" type=\"application/json\">{"; const char closeJSON[] = "}</script>\n"; const char openDataTypesJSON[] = "\"dataTypes\":{"; const char closeDataTypesJSON[] = "}"; // closing a dict const char openDataElementsJSON[] = "\"dataElements\":["; const char closeDataElementsJSON[] = "]"; // closing an array const char metadataTableScriptElement[] = - "<script type='text/javascript' src='/js/bigComposite.js'></script>\n"; - // const char metaDataUrlFmt[] = "\"metadata_url\": \"%s\""; + "<script type='text/javascript' src='/js/facetedComposite.js'></script>\n"; /* ADS: maybe cart should be used below, but I don't know how from here */ // parse the hgsid as id and sessionKey - char *hgsid = cgiString("hgsid"); +char *hgsid = cartSessionId(cart); char *sessionKey = NULL; const int id = cartDbParseId(hgsid, &sessionKey); if (!sessionKey) errAbort("Failed to parse session key from: %s", hgsid); // --- Get data from 'settings' field in 'trackDb' entry --- // required - const char *metaDataUrl = - (const char *)hashMustFindVal(tdb->settingsHash, "metaDataUrl"); - const char *primaryKey = - (const char *)hashMustFindVal(tdb->settingsHash, "primaryKey"); +const char *metaDataUrl = trackDbSetting(tdb, "metaDataUrl"); +// (const char *)hashMustFindVal(tdb->settingsHash, "metaDataUrl"); +const char *primaryKey = trackDbSetting(tdb, "primaryKey"); +// (const char *)hashMustFindVal(tdb->settingsHash, "primaryKey"); + int nDataTypes = 0; char **dataTypes = parseDataTypes(tdb, &nDataTypes); if (!dataTypes) - errAbort("Failed to parse data types from bigComposite settings for: %s", - tdb->track); + errAbort("Failed to parse data types from faceted composite settings for: %s", tdb->track); // optional - const char *colorSettingsUrl = - (const char *)hashFindVal(tdb->settingsHash, "colorSettingsUrl"); - const char *maxCheckboxes = - (const char *)hashFindVal(tdb->settingsHash, "maxCheckboxes"); +const char *colorSettingsUrl = (const char *)hashFindVal(tdb->settingsHash, "colorSettingsUrl"); +const char *maxCheckboxes = (const char *)hashFindVal(tdb->settingsHash, "maxCheckboxes"); // --- done parsing values from trackDb.settings --- const char *metaDataId = tdb->track; - char queryFmt[] = - "SELECT contents FROM sessionDb WHERE id='%d' AND sessionKey='%s';"; +char queryFmt[] = "SELECT contents FROM sessionDb WHERE id='%d' AND sessionKey='%s';"; char query[query_buff_size]; sqlSafef(query, query_buff_size, queryFmt, id, sessionKey); struct sqlConnection *conn = hConnectCentral(); const char *contents = sqlQuickString(conn, query); struct cgiParsedVars *varList = cgiParsedVarsNew((char *)contents); printf(pageStyle); // css printf(placeholderDiv); // placholder /* --- START embedded JSON data --- */ printf(openJSON); printf(openDataTypesJSON); // find selected data types int not_first = 0; int anySelDataType = -1; // non-neg val will be used as index and flag - for (int i = 0; i < nDataTypes; ++i) { +for (int i = 0; i < nDataTypes; ++i) + { char toMatch[token_size]; - snprintf(toMatch, token_size, "_%s_sel", dataTypes[i]); + safef(toMatch, token_size, "_%s_sel", dataTypes[i]); boolean dataTypeSel = FALSE; for (struct cgiVar *le = varList->list; !dataTypeSel && le; le = le->next) if (startsWith(metaDataId, le->name) && endsWith(le->name, toMatch)) dataTypeSel = TRUE; printf("%s\"%s\": %d", COMMA_IF(not_first), dataTypes[i], dataTypeSel ? 1 : 0); anySelDataType = dataTypeSel ? i : anySelDataType; } printf(closeDataTypesJSON); printf(","); // add separator // find selected data sets printf(openDataElementsJSON); not_first = 0; - if (anySelDataType >= 0) { +if (anySelDataType >= 0) + { char suffix[token_size]; - snprintf(suffix, token_size, "_%s_sel", dataTypes[anySelDataType]); + safef(suffix, token_size, "_%s_sel", dataTypes[anySelDataType]); for (struct cgiVar *le = varList->list; le; le = le->next) - if (startsWith(metaDataId, le->name) && endsWith(le->name, suffix)) { - const char *nameStart = strchr(le->name, '_'); - if (nameStart) { + if (startsWith(metaDataId, le->name) && endsWith(le->name, suffix)) + { + const char *nameStart = strchr(le->name+strlen(metaDataId), '_'); + if (nameStart) + { ++nameStart; // move past '_' const char *nameEnd = strchr(nameStart, '_'); - if (nameEnd && nameEnd > nameStart) { + if (nameEnd && nameEnd > nameStart) + { const int nameLen = nameEnd - nameStart; printf("%s\"%.*s\"", COMMA_IF(not_first), nameLen, nameStart); } } } } printf(closeDataElementsJSON); printf(",\"mdid\": \"%s\"", tdb->track); // metadata id is track name printf(",\"primaryKey\": \"%s\"", primaryKey); // must exist if (maxCheckboxes) // only if present in trackDb.settings entry printf(",\"maxCheckboxes\": \"%s\"", maxCheckboxes); if (colorSettingsUrl) // only if present in trackDb.settings entry printf(",\"colorSettingsUrl\": \"%s\"", colorSettingsUrl); printf(",\"metadataUrl\": \"%s\"", metaDataUrl); printf(closeJSON); @@ -3058,34 +3069,32 @@ if (liftDb != NULL) db = liftDb; // Ideally check cfgTypeFromTdb()/cfgByCfgType() first, but with all these special cases already in // place, lets be cautious at this time. // NOTE: Developer, please try to use cfgTypeFromTdb()/cfgByCfgType(). boolean boxed = trackDbSettingClosestToHomeOn(tdb, "boxedCfg"); boolean isGencode3 = trackDbSettingOn(tdb, "isGencode3"); // UI precedence: // 1) supers to get them out of the way: they have no controls // 2) special cases based upon track name (developer please avoid) // 3) cfgTypeFromTdb()/cfgByCfgType() <== prefered method // 4) special cases falling through the cracks but based upon type if (tdbIsSuperTrack(tdb)) superTrackUi(tdb, tdbList); -else if (sameString(tdb->type, "bigComposite")) - // ADS: switching on 'type' here, instead of track name as for all - // other cases (except hic and ld2) - bigCompositeCfgUi(tdb); +else if (tdbIsComposite(tdb) && sameOk(trackDbLocalSetting(tdb, "compositeTrack"), "faceted")) + facetedCompositeUi(tdb); else if (sameString(track, "stsMap")) stsMapUi(tdb); else if (sameString(track, "affyTxnPhase2")) affyTxnPhase2Ui(tdb); else if (sameString(track, "cgapSage")) cgapSageUi(tdb); else if (sameString(track, "stsMapMouseNew")) stsMapMouseUi(tdb); else if (sameString(track, "stsMapRat")) stsMapRatUi(tdb); else if (sameString(track, "snpMap")) snpMapUi(tdb); else if (sameString(track, "snp")) snpUi(tdb); else if (snpVersion(track) >= 125) @@ -3244,34 +3253,34 @@ else if (startsWith("sample", tdb->type)) genericWiggleUi(tdb,7); else if (startsWithWord("array",tdb->type)) // not quite the same as "expRatio" (custom tracks) expRatioCtUi(tdb); else if (startsWithWord("factorSource",tdb->type)) factorSourceUi(db,tdb); else if (startsWithWord("bigBed",tdb->type)) labelCfgUi(db, cart, tdb, tdb->track); } if (!ajax) // ajax asks for a simple cfg dialog for right-click popup or hgTrackUi subtrack cfg { // Composites *might* have had their top level controls just printed, // but almost certainly have additional controls boolean isLogo = (trackDbSetting(tdb, "logo") != NULL); - // ADS: added 'bigComposite' below to avoid generating the - // hCompositeUi controls for bigComposite. Better solution would - // be define bigComposite to avoid using 'compositeTrack on' - if (!sameString(tdb->type, "bigComposite") && tdbIsComposite(tdb) && !isLogo) // for the moment generalizing this to include other containers... + // It'd be nice to handle faceted composites as a separate container type, but practically so much + // of the display features we want are identical to composites - it's easier to special case the UI. + if (tdbIsComposite(tdb) && !isLogo) // for the moment generalizing this to include other containers... + if (!sameOk(trackDbLocalSetting(tdb, "compositeTrack"), "faceted")) // but not faceted containers ... hCompositeUi(db, cart, tdb, NULL, NULL, MAIN_FORM); // Additional special case navigation links may be added extraUiLinks(db, tdb, cart); } } #ifdef UNUSED static void findSuperChildrenAndSettings(struct trackDb *tdbList, struct trackDb *super) /* Find the tracks that have super as a parent and stuff references to them on * super's children list. Also do some visibility and parentName futzing. */ { struct trackDb *tdb; for (tdb = tdbList; tdb != NULL; tdb = tdb->next) {