06d7be056190c14b85e71bc12523f18ea6815b5e markd Mon Dec 7 00:50:29 2020 -0800 BLAT mmap index support merge with master diff --git src/hg/hgc/hgc.c src/hg/hgc/hgc.c index 5da2e8a..4fea33b 100644 --- src/hg/hgc/hgc.c +++ src/hg/hgc/hgc.c @@ -814,30 +814,42 @@ if (bedSize >= 6) { strand = bed->strand; } printPos(bed->chrom, bed->chromStart, bed->chromEnd, strand, TRUE, bed->name); } void genericHeader(struct trackDb *tdb, char *item) /* Put up generic track info. */ { if (item != NULL && item[0] != 0) cartWebStart(cart, database, "%s (%s)", tdb->longLabel, item); else cartWebStart(cart, database, "%s", tdb->longLabel); + +// QA noticed that clicking the +- buttons to collapse item detail tables was +// generating messages in the Apache log if you went directly to an item page +// without first visiting hgTracks. Clicking those buttons causes a cartDump +// in order to save the state of visibility of the table, which in +// turn needs an hgsid in order to save the state correctly. However, because +// we aren't in a form, we have never saved the hgsid to a hidden +// input element, and so the javascript that creates the cartDump link attaches +// an empty 'hgsid=' parameter, which cartDump doesn't like. Since we aren't in +// a form, use the 'common' object to store the parameter so the links to cartDump +// are correct: +jsInlineF("var common = {hgsid:\"%s\"};\n", cartSessionId(cart)); } void printItemDetailsHtml(struct trackDb *tdb, char *itemName) /* if track has an itemDetailsHtml, retrieve and print the HTML */ { char *tableName = trackDbSetting(tdb, "itemDetailsHtmlTable"); if (tableName != NULL) { struct sqlConnection *conn = hAllocConn(database); struct itemDetailsHtml *html, *htmls; // if the details table has chrom/start/end columns, then use these to lookup html if (sqlColumnExists(conn, tableName, "chrom")) { char *chrom = cgiString("c"); int start = cgiInt("o"); @@ -1538,85 +1550,157 @@ for (count = 0; col != NULL && count < fieldCount; col = col->next) { struct slPair *field; AllocVar(field); char *fieldName = col->name; char *fieldVal = row[count]; field->name = fieldName; field->val = fieldVal; slAddHead(&fields, field); count++; } slReverse(fields); return fields; } +void printExtraDetailsTable(char *trackName, char *tableName, char *fileName, struct dyString *tableText) +// convert a tab-sep table to HTML +{ +struct lineFile *lf = lineFileOnString(fileName, TRUE, tableText->string); +char *description = tableName != NULL ? tableName : "Additional Details"; +printf("<table>"); +jsBeginCollapsibleSection(cart, trackName, "extraTbl", description, FALSE); +printf("<table class=\"bedExtraTbl\">\n"); +char *line; +while (lineFileNext(lf, &line, NULL)) + { + printf("<tr><td>"); + char *toPrint = replaceChars(line, "\t", "</td><td>"); + printf("%s", toPrint); + printf("</td></tr>\n"); + } +printf("</table>\n"); // closes bedExtraTbl +jsEndCollapsibleSection(); +printf("</table>\n"); // close wrapper table +} + +static struct slName *findFieldsInExtraFile(char *detailsTableUrl, struct asColumn *col, struct dyString *ds) +// return a list of the ${}-enclosed fields from an extra file +{ +struct slName *foundFields = NULL; +char *table = netReadTextFileIfExists(hReplaceGbdb(detailsTableUrl)); +if (table) + { + for (; col != NULL; col = col->next) + { + char field[256]; + safef(field, sizeof(field), "${%s}", col->name); + if (stringIn(field, table)) + { + struct slName *replaceField = slNameNew(col->name); + slAddHead(&foundFields, replaceField); + } + } + dyStringPrintf(ds, "%s", table); + slReverse(foundFields); + } +return foundFields; +} + int extraFieldsPrintAs(struct trackDb *tdb,struct sqlResult *sr,char **fields,int fieldCount, struct asObject *as) // Any extra bed or bigBed fields (defined in as and occurring after N in bed N + types. // sr may be null for bigBeds. // Returns number of extra fields actually printed. { // We are trying to print extra fields so we need to figure out how many fields to skip int start = extraFieldsStart(tdb, fieldCount, as); struct asColumn *col = as->columnList; char *urlsStr = trackDbSettingClosestToHomeOrDefault(tdb, "urls", NULL); struct hash* fieldToUrl = hashFromString(urlsStr); boolean skipEmptyFields = trackDbSettingOn(tdb, "skipEmptyFields"); // make list of fields to skip char *skipFieldsStr = trackDbSetting(tdb, "skipFields"); struct slName *skipIds = NULL; if (skipFieldsStr) skipIds = slNameListFromComma(skipFieldsStr); // make list of fields that are separated from other fields char *sepFieldsStr = trackDbSetting(tdb, "sepFields"); struct slName *sepFields = NULL; if (sepFieldsStr) sepFields = slNameListFromComma(sepFieldsStr); +// make list of fields that we want to substitute +// this setting has format description|URLorFilePath, with the stuff before the pipe optional +char *extraDetailsTableName = NULL, *extraDetails = cloneString(trackDbSetting(tdb, "extraDetailsTable")); +if (extraDetails && strchr(extraDetails,'|')) + { + extraDetailsTableName = extraDetails; + extraDetails = strchr(extraDetails,'|'); + *extraDetails++ = 0; + } +struct dyString *extraTblStr = dyStringNew(0); +struct slName *detailsTableFields = NULL; +if (extraDetails) + detailsTableFields = findFieldsInExtraFile(extraDetails, col, extraTblStr); + // iterate over fields, print as table rows int count = 0; for (;col != NULL && count < fieldCount;col=col->next) { if (start > 0) // skip past already known fields { start--; continue; } int ix = count; if (sr != NULL) { ix = sqlFieldColumn(sr, col->name); // If sr provided, name must match sql columnn name! if (ix == -1 || ix > fieldCount) // so extraField really just provides a label continue; } char *fieldName = col->name; if (count == 0) printf("<br><table class='bedExtraTbl'>"); count++; // do not print a row if the fieldName from the .as file is in the "skipFields" list // or if a field name starts with _. This maked bigBed extra fields consistent with // external extra fields in that _ field names have some meaning and are not shown if (startsWith("_", fieldName) || (skipIds && slNameInList(skipIds, fieldName))) continue; + // don't print this field if we are gonna print it later in a custom table + if (detailsTableFields && slNameInList(detailsTableFields, fieldName)) + { + int fieldLen = strlen(fieldName); + char *replaceField = needMem(fieldLen+4); + replaceField[0] = '$'; + replaceField[1] = '{'; + strcpy(replaceField+2, fieldName); + replaceField[fieldLen+2] = '}'; + replaceField[fieldLen+3] = 0; + extraTblStr = dyStringSub(extraTblStr->string, replaceField, fields[ix]); + continue; + } + // skip this row if it's empty and "skipEmptyFields" option is set if (skipEmptyFields && isEmpty(fields[ix])) continue; // split this table to separate current row from the previous one, if the trackDb option is set if (sepFields && slNameInList(sepFields, fieldName)) printf("</tr></table>\n<p>\n<table class='bedExtraTbl'>"); // field description char *entry; if (sameString(fieldName, "cdsStartStat") && sameString("enum('none','unk','incmpl','cmpl')", col->comment)) entry = "Status of CDS start annotation (none, unknown, incomplete, or complete)"; else if (sameString(fieldName, "cdsEndStat") && sameString("enum('none','unk','incmpl','cmpl')", col->comment)) entry = "Status of CDS end annotation (none, unknown, incomplete, or complete)"; else @@ -1632,30 +1716,36 @@ printf("<td>%g</td></tr>\n", valDouble); else printf("<td>%s</td></tr>\n", fields[ix]); // decided not to print error } else printf("<td>%s</td></tr>\n", fields[ix]); } if (skipIds) slFreeList(skipIds); if (sepFields) slFreeList(sepFields); if (count > 0) printf("</table>\n"); +if (detailsTableFields) + { + printf("<br>\n"); + printExtraDetailsTable(tdb->track, extraDetailsTableName, extraDetails, extraTblStr); + } + return count; } int extraFieldsPrint(struct trackDb *tdb,struct sqlResult *sr,char **fields,int fieldCount) // Any extra bed or bigBed fields (defined in as and occurring after N in bed N + types. // sr may be null for bigBeds. // Returns number of extra fields actually printed. { struct asObject *as = asForDb(tdb, database); if (as == NULL) return 0; int ret = extraFieldsPrintAs(tdb, sr, fields,fieldCount, as); //asObjectFree(&as); @@ -4143,31 +4233,31 @@ headerItem = NULL; else if (( sameString(type, "narrowPeak") || sameString(type, "broadPeak") || sameString(type, "gappedPeak") ) && headerItem && sameString(headerItem, ".") ) headerItem = NULL; } /* Print header. */ genericHeader(tdb, headerItem); if (differentString(type, "bigInteract") && differentString(type, "interact")) { // skip generic URL code as these may have multiple items returned for a click itemForUrl = getIdInUrl(tdb, item); - if (itemForUrl != NULL && trackDbSetting(tdb, "url")) + if (itemForUrl != NULL && trackDbSetting(tdb, "url") && differentString(type, "bigBed")) { printCustomUrl(tdb, itemForUrl, item == itemForUrl); printIframe(tdb, itemForUrl); } } if (plus != NULL) { fputs(plus, stdout); } if (container != NULL) { genericContainerClick(conn, container, tdb, item, itemForUrl); } else if (wordCount > 0) { @@ -11577,31 +11667,33 @@ } } return NULL; } char *getRefSeqSummary(struct sqlConnection *conn, char *acc) /* RefSeq summary or NULL if not available; free result */ { char * summary = NULL; if (sqlTableExists(conn, refSeqSummaryTable)) { char query[256]; sqlSafef(query, sizeof(query), "select summary from %s where mrnaAcc = '%s'", refSeqSummaryTable, acc); summary = sqlQuickString(conn, query); + summary = abbreviateRefSeqSummary(summary); } + return summary; } char *geneExtraImage(char *geneFileBase) /* check if there is a geneExtra image for the specified gene, if so return * the relative URL in a static buffer, or NULL if it doesn't exist */ { static char *imgExt[] = {"png", "gif", "jpg", NULL}; static char path[256]; int i; for (i = 0; imgExt[i] != NULL; i++) { safef(path, sizeof(path), "../htdocs/geneExtra/%s.%s", geneFileBase, imgExt[i]); if (access(path, R_OK) == 0) @@ -21379,31 +21471,31 @@ doLongTabix(ct->tdb, item); else if (sameWord(type, "encodePeak")) doEncodePeak(ct->tdb, ct, fileName); else if (sameWord(type, "bigNarrowPeak")) doBigEncodePeak(ct->tdb, NULL, item); else if (sameWord(type, "bigWig")) bigWigCustomClick(ct->tdb); else if (sameWord(type, "bigChain")) genericChainClick(NULL, ct->tdb, item, start, "seq"); else if (sameWord(type, "bigPsl")) genericBigPslClick(NULL, ct->tdb, item, start, end); else if (sameWord(type, "bigMaf")) genericMafClick(NULL, ct->tdb, item, start); else if (sameWord(type, "bigDbSnp")) doBigDbSnp(ct->tdb, item); -else if (sameWord(type, "bigBed") || sameWord(type, "bigGenePred")) +else if (sameWord(type, "bigBed") || sameWord(type, "bigGenePred") || sameWord(type, "bigLolly")) bigBedCustomClick(ct->tdb); else if (sameWord(type, "bigBarChart") || sameWord(type, "barChart")) doBarChartDetails(ct->tdb, item); else if (sameWord(type, "bigInteract") || sameWord(type, "interact")) doInteractDetails(ct->tdb, item); else if (sameWord(type, "bam")) doBamDetails(ct->tdb, itemName); else if (sameWord(type, "vcfTabix") || sameWord(type, "vcfPhasedTrio")) doVcfTabixDetails(ct->tdb, itemName); else if (sameWord(type, "vcf")) doVcfDetails(ct->tdb, itemName); else if (sameWord(type, "makeItems")) doMakeItemsDetails(ct, fileName); // fileName is first word, which is, go figure, id else if (ct->wiggle) {