dccec2a514b8c7aeecffbfac722f6c718f204fba max Thu Apr 25 12:08:23 2013 -0700 support for urls in bigBed+ fields via a new tdb statement "urls" diff --git src/hg/hgc/hgc.c src/hg/hgc/hgc.c index c32a693..d0cb384 100644 --- src/hg/hgc/hgc.c +++ src/hg/hgc/hgc.c @@ -927,46 +927,32 @@ * return itemName. */ { char *sql = trackDbSetting(tdb, "idInUrlSql"); char *id = itemName; if (sql != NULL) { char buf[256]; safef(buf, sizeof(buf), sql, itemName); struct sqlConnection *conn = hAllocConn(database); id = sqlQuickString(conn, buf); hFreeConn(&conn); } return id; } -void printCustomUrlWithLabel(struct trackDb *tdb, char *itemName, char *itemLabel, char *urlSetting, boolean encode) -/* Print custom URL specified in trackDb settings. */ -{ -char *url; -char urlLabelSetting[32]; - -/* check the url setting prefix and get the correct url setting from trackDb */ -if (sameWord(urlSetting, "url")) - url = tdb->url; -else - url = trackDbSetting(tdb, urlSetting); - -if (url != NULL && url[0] != 0) - { - char *idInUrl = getIdInUrl(tdb, itemName); - if (idInUrl != NULL) +char* replaceInUrl(struct trackDb *tdb, char *url, char *idInUrl, boolean encode) +/* replace $$ in url with idInUrl. Supports many other wildchards */ { struct dyString *uUrl = NULL; struct dyString *eUrl = NULL; char startString[64], endString[64]; char *ins[9], *outs[9]; char *eItem = (encode ? cgiEncode(idInUrl) : cloneString(idInUrl)); sprintf(startString, "%d", winStart); sprintf(endString, "%d", winEnd); ins[0] = "$$"; outs[0] = idInUrl; ins[1] = "$T"; outs[1] = tdb->track; ins[2] = "$S"; outs[2] = seqName; @@ -987,53 +973,88 @@ char *nextColon = stringIn(":", suffixClone+1); if (nextColon) /* terminate suffixClone suffix */ *nextColon = '\0'; /* when next colon is present */ *suffix = '\0'; /* terminate itemClone prefix */ outs[7] = itemClone; outs[8] = suffixClone; /* small memory leak here for these cloned strings */ /* not important for a one-time operation in a CGI that will exit */ } else { outs[7] = idInUrl; /* otherwise, these are not expected */ outs[8] = idInUrl; /* to be used */ } uUrl = subMulti(url, ArraySize(ins), ins, outs); outs[0] = eItem; eUrl = subMulti(url, ArraySize(ins), ins, outs); +freeDyString(&uUrl); +freeMem(eItem); +return eUrl->string; +} + +char* constructUrl(struct trackDb *tdb, char *urlSetting, char *idInUrl, boolean encode) +{ +/* construct the url by replacing $$, etc in the url given by urlSetting. + * Replace $$ with itemName. */ + +// check the url setting prefix and get the correct url setting from trackDb +char *url; +if (sameWord(urlSetting, "url")) + url = tdb->url; +else + url = trackDbSetting(tdb, urlSetting); + +if (url == NULL || url[0] == 0) + return NULL; + +char* eUrl = replaceInUrl(tdb, url, idInUrl, encode); +return eUrl; +} + +void printCustomUrlWithLabel(struct trackDb *tdb, char *itemName, char *itemLabel, char *urlSetting, boolean encode) +/* Print custom URL specified in trackDb settings. */ +{ +char urlLabelSetting[32]; + +// first try to resolve itemName via an optional sql statement to something else +char *idInUrl = getIdInUrl(tdb, itemName); +if (idInUrl == NULL) + return; + +// replace the $$ and other wildchards with the url given in tdb +char* eUrl = constructUrl(tdb, urlSetting, idInUrl, encode); +if (eUrl==NULL) + return; + /* create the url label setting for trackDb from the url setting prefix */ safef(urlLabelSetting, sizeof(urlLabelSetting), "%sLabel", urlSetting); printf("%s ", trackDbSettingOrDefault(tdb, urlLabelSetting, "Outside Link:")); - printf("", eUrl->string); +printf("", eUrl); if (sameWord(tdb->table, "npredGene")) { printf("%s (%s)
\n", idInUrl, "NCBI MapView"); } else { char *label = idInUrl; if (isNotEmpty(itemLabel) && !sameString(itemName, itemLabel)) label = itemLabel; printf("%s
\n", label); } - freeMem(eItem); - freeDyString(&uUrl); - freeDyString(&eUrl); - } - } +//freeMem(&eUrl); small memory leak } void printCustomUrl(struct trackDb *tdb, char *itemName, boolean encode) /* Wrapper to call printCustomUrlWithLabel using the url setting in trackDb */ { char urlSetting[10]; safef(urlSetting, sizeof(urlSetting), "url"); printCustomUrlWithLabel(tdb, itemName, itemName, urlSetting, encode); } void printOtherCustomUrl(struct trackDb *tdb, char *itemName, char* urlSetting, boolean encode) /* Wrapper to call printCustomUrlWithLabel to use another url setting other than url in trackDb e.g. url2, this allows the use of multiple urls for a track to be set in trackDb. */ { @@ -1410,102 +1431,151 @@ char dbOnly[4096]; diff = comp2->start - (comp1->start + comp1->size); safef(dbOnly, sizeof(dbOnly), "%s", comp1->src); chopPrefix(dbOnly); printf("%-20s %d\n",hOrganism(dbOnly), diff); } printf("
"); } } } } +struct hash* hashFromString(char* string) +/* parse a whitespace-separated string with tuples in the format name=val or + * name="val" to a hash name->val */ +{ +if (string==NULL) + return NULL; + +struct slPair *keyVals = slPairListFromString(string, TRUE); +if (keyVals==NULL) + return NULL; + +struct hash *nameToVal = newHash(0); +struct slPair *kv; +for (kv = keyVals; kv != NULL; kv = kv->next) + hashAdd(nameToVal, kv->name, kv->val); +return nameToVal; +} + +void printIdOrLinks(struct asColumn *col, struct hash *fieldToUrl, struct trackDb *tdb, char *idList) +/* if trackDb does not contain a "urls" entry for current column name, just print idList as it is. + * Otherwise treat idList as a comma-sep list of IDs and print one row per id, with a link to url, + * ($$ in url is OK, wildcards like $P, $p, are also OK) + * */ +{ +// try to find a fieldName=url setting in the "urls" tdb statement, print id if not found +char *url = NULL; +if (fieldToUrl!=NULL) + url = (char*)hashFindVal(fieldToUrl, col->name); +if ((url==NULL) + { + printf("%s\n", idList); + return; + } + +// split the id into parts and print each part as a link +struct slName *slIds = slNameListFromComma(idList); +struct slName *itemId = NULL; +printf(""); +for (itemId = slIds; itemId!=NULL; itemId = itemId->next) + { + if (itemId!=slIds) + printf(", "); + char *idUrl = replaceInUrl(tdb, url, itemId->name, TRUE); + printf("%s", idUrl, itemId->name); + } +printf("\n"); +freeMem(slIds); +} + 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 sqlConnection *conn = NULL ; if (!trackHubDatabase(database)) conn = hAllocConnTrack(database, tdb); struct asObject *as = asForTdb(conn, tdb); hFreeConn(&conn); if (as == NULL) return 0; // We are trying to print extra fields so we need to figure out how many fields to skip int start = 0; char *type = cloneString(tdb->type); char *word = nextWord(&type); if (word && (sameWord(word,"bed") || sameWord(word,"bigBed"))) { if (NULL != (word = nextWord(&type))) start = sqlUnsigned(word); else // custom beds and bigBeds may not have full type "begBed 9 +" start = max(0,slCount(as->columnList) - fieldCount); } int count = 0; struct asColumn *col = as->columnList; +char *urlsStr = trackDbSetting(tdb, "urls"); +struct hash* fieldToUrl = hashFromString(urlsStr); + 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; } // Print as table rows if (count == 0) printf("
"); count++; printf("", col->comment); if (col->isList || col->isArray || col->lowType->stringy) - { - printf("\n", fields[ix]); - } + printIdOrLinks(col, fieldToUrl, tdb, fields[ix]); else if (asTypesIsInt(col->lowType->type)) { long valInt = strtol(fields[ix],NULL,10); if (errno == 0 && valInt != 0) printf("\n", valInt); else printf("\n", fields[ix]); // decided not to print error } else if (asTypesIsFloating(col->lowType->type)) { double valDouble = strtod(fields[ix],NULL); if (errno == 0 && valDouble != 0) printf("\n", valDouble); else printf("\n", fields[ix]); // decided not to print error } else - { printf("\n", fields[ix]); } - } asObjectFree(&as); +freeMem(fieldToUrl); if (count > 0) printf("
%s:%s
%ld
%s
%g
%s
%s
\n"); return count; } void genericBedClick(struct sqlConnection *conn, struct trackDb *tdb, char *item, int start, int bedSize) /* Handle click in generic BED track. */ { char table[64]; boolean hasBin; struct bed *bed; char query[512]; struct sqlResult *sr;