7120a354ec4870b3bc2112c384bdb0af837ed44e braney Thu Apr 23 14:12:36 2026 -0700 hgc: make quickLifted NCBI RefSeq / UCSC RefSeq click details work. refs #36125 - trackHub.c isVetted(): accept refGene, ncbiRefSeq* subtracks, and ncbiOrtho so the generated quickLift hub.txt doesn't emit 'avoidHandler on' for them. Without this, hgc skips findNameBasedHandler and the detail click falls through to genericClickHandler, showing none of the rich RefSeq fields. - hgc.c findNameBasedHandler: when the tdb has 'quickLifted on', strip the hub_NNN_ prefix from the dispatch table before comparisons so refGene / ncbiRefSeq* match their native handlers. - doNcbiRefSeq / doRefGene: route hAllocConn to quickLiftDb (source), use trackHubSkipHubName for track/table name comparisons, and pass srcDb into replaceInUrl, hTableExists, AceView / MGIid / jaxOrtholog / hg-prefix checks -- otherwise these paths errAborted with "Unknown database hub_NNN_" on a quickLifted click. - printRefSeqInfo / prRefGeneInfo / gbCdnaGetVersion: use sqlGetDatabase(conn) rather than the global `database` for host-db checks. - Predicted Protein / Predicted mRNA links for quickLifted tracks now point at htcTranslatedPredMRna / htcGeneMrna so the sequences come from the destination genome at the lifted exon coordinates, rather than the NCBI-authored refPep / seqNcbiRefSeq extFiles on the source. Native behavior is unchanged. - showGenePos: fetch via quickLiftSql + calcLiftOverGenePreds when the track is quickLifted so the Position line is in destination coordinates. The swapped chainHash comes from the track's quickLiftUrl bigChain, matching the pattern getGenePredForPositionSql already uses. - Suppress the mRNA/Genomic Alignments block and its trailing
for quickLifted refGene / ncbiRefSeq* clicks. The PSLs come back in source coords and the htcCdnaAli links don't line up with the destination window. - getAlignmentsTName: use sqlGetDatabase(conn) rather than global `database` so split-table resolution looks up the right db. - printGeneCards: take a db argument; callers pass srcDb so the GeneCards link renders on quickLifted pages (the "startsWith hg" check was matching hub_NNN_hs1 as false). - geneReviewsClick.c prGRShortRefGene: take a struct sqlConnection * rather than opening its own on `database` (hub_NNN_hs1 -> Unknown database abort). Callers in prRefGeneInfo and doOmimGene2 pass their existing source-db conn, so the Related GeneReviews line now shows on both native and quickLifted refGene pages. Verified end-to-end on hgwdev-braney with hg38 -> hs1 quickLift of the refSeqComposite: ncbiRefSeqCurated and refGene clicks on SHH now render the full doNcbiRefSeq / doRefGene details (RefSeq / Status / Description / Synonyms / OMIM / Protein / HGNC / Entrez Gene / GeneCards / AceView / Summary / Position / Gene Symbol); Predicted mRNA and Predicted Protein links return destination-derived sequences; Genomic Sequence reaches the Get-DNA-in-window page; Ctrl/Cmd+drag zoom no longer ends up with an HTML error page inside the zoom dialog. Non-quickLifted hg38 refSeq details are unchanged (alignments still shown, GeneReviews still present). The duplicate-subtracks symptom from the ticket's step (i) is not addressed here -- it's an architectural consequence of the shared `refSeqComposite` cart key applying to both the destination's native composite and the quickLift hub's composite, and needs cart-scoping work in quickLift v2. Co-Authored-By: Claude Opus 4.7 (1M context) diff --git src/hg/hgc/hgc.c src/hg/hgc/hgc.c index af33696c651..98760f2f6c4 100644 --- src/hg/hgc/hgc.c +++ src/hg/hgc/hgc.c @@ -2952,45 +2952,65 @@ case cdsUnknown: /* "unk" - CDS is unknown (coding, but not known) */ printf("unknown (coding, but not known)
\n"); break; case cdsIncomplete: /* "incmpl" - CDS is not complete at this end */ printf("not complete
\n"); break; case cdsComplete: /* "cmpl" - CDS is complete at this end */ printf("complete
\n"); break; } } void showGenePos(char *name, struct trackDb *tdb) /* Show gene prediction position and other info. */ { -char *rootTable = tdb->table; char query[512]; char *liftDb = cloneString(trackDbSetting(tdb, "quickLiftDb")); char *db = (liftDb == NULL) ? database : liftDb; +// tdb->table carries the hub_NNN_ prefix for quickLifted tracks; the +// actual SQL table lives in the source assembly under the bare name. +char *rootTable = (liftDb == NULL) ? tdb->table : trackHubSkipHubName(tdb->table); struct sqlConnection *conn = hAllocConn(db); struct genePred *gpList = NULL, *gp = NULL; char table[HDB_MAX_TABLE_STRING]; struct sqlResult *sr = NULL; char **row = NULL; char *classTable = trackDbSetting(tdb, GENEPRED_CLASS_TBL); if (!hFindSplitTable(db, seqName, rootTable, table, sizeof table, NULL)) errAbort("showGenePos track %s not found", rootTable); sqlSafef(query, sizeof(query), "name = \"%s\"", name); +if (liftDb != NULL) + { + // Fetch via quickLiftSql so we get both the items and the set of + // swapped chains that map them back to the destination assembly, then + // lift the genePreds with calcLiftOverGenePreds. seqName/winStart/ + // winEnd are destination coords; quickLiftSql picks up the chains + // that overlap that window on the destination side. + char *quickLiftFile = trackDbSetting(tdb, "quickLiftUrl"); + struct hash *chainHash = newHash(8); + char extraWhere[512]; + sqlSafef(extraWhere, sizeof extraWhere, "name = \"%s\"", name); + extern struct genePred *genePredExtLoad15(char **row); + gpList = (struct genePred *)quickLiftSql(conn, quickLiftFile, rootTable, + seqName, winStart, winEnd, NULL, extraWhere, + (ItemLoader2)genePredExtLoad15, 0, chainHash); + calcLiftOverGenePreds(gpList, chainHash, 0.0, 0.0, TRUE, NULL, NULL, TRUE, FALSE); + } +else gpList = genePredReaderLoadQuery(conn, table, query); for (gp = gpList; gp != NULL; gp = gp->next) { printPos(gp->chrom, gp->txStart, gp->txEnd, gp->strand, FALSE, NULL); if(sameString(tdb->type,"genePred") && startsWith("ENCODE Gencode",tdb->longLabel) && startsWith("ENST",name)) { char *ensemblIdUrl = trackDbSetting(tdb, "ensemblIdUrl"); printf("Ensembl Transcript Id: "); if (ensemblIdUrl != NULL) printf("%s
", ensemblIdUrl,name,name); else printf("%s
",name); @@ -3136,64 +3156,69 @@ { puts("
  • \n"); linkToPal( track, chrom, start, end, geneName); puts("
  • \n"); } } void geneShowPosAndLinksPal(char *geneName, char *pepName, struct trackDb *tdb, char *pepTable, char *pepClick, char *mrnaClick, char *genomicClick, char *mrnaDescription, struct palInfo *palInfo) /* Show parts of gene common to everything. If pepTable is not null, * it's the old table name, but will check gbSeq first. */ { char *geneTable = tdb->table; +// For quickLifted tracks, SQL tables (gbSeq, yalePseudoAssoc, …) live in +// the source assembly; use that for table existence / hGenBank checks so +// we don't hit "Unknown database hub_NNN_". +char *liftDb = trackDbSetting(tdb, "quickLiftDb"); +char *srcDb = (liftDb != NULL) ? liftDb : database; boolean foundPep = FALSE; showGenePos(geneName, tdb); if (startsWith("ENCODE Gencode",tdb->longLabel)) { char *yaleTable = trackDbSetting(tdb, "yalePseudoAssoc"); - if ((yaleTable != NULL) && (hTableExists(database, yaleTable))) + if ((yaleTable != NULL) && (hTableExists(srcDb, yaleTable))) { - struct sqlConnection *conn = hAllocConn(database); + struct sqlConnection *conn = hAllocConn(srcDb); char query[512]; sqlSafef(query, sizeof(query), "select * from %s where transcript = '%s'", yaleTable, geneName); char buffer[512]; struct sqlResult *sr = sqlGetResult(conn, query); char *yaleUrl = trackDbSetting(tdb, "yaleUrl"); char **row; while ((row = sqlNextRow(sr)) != NULL) { struct yaleGencodeAssoc *ya = yaleGencodeAssocLoad(row); safef(buffer, sizeof buffer, "%s/%s",yaleUrl,ya->yaleId); printf("Yale pseudogene: %s
    \n", buffer, ya->yaleId); } sqlFreeResult(&sr); hFreeConn(&conn); } } printf("

    Links to sequence:

    \n"); printf("
      \n"); -if ((pepTable != NULL) && hGenBankHaveSeq(database, pepName, pepTable)) +if ((pepTable != NULL) && hGenBankHaveSeq(srcDb, pepName, pepTable)) { puts("
    • \n"); hgcAnchorSomewhere(pepClick, pepName, pepTable, seqName); printf("Predicted Protein \n"); puts("
    • \n"); foundPep = TRUE; } if (!foundPep) { char *autoTranslate = trackDbSetting(tdb, "autoTranslate"); if (autoTranslate == NULL || differentString(autoTranslate, "0")) { puts("
    • \n"); /* put out correct message to describe translated mRNA */ if ( sameString(geneTable, "ensGene") @@ -3228,31 +3253,31 @@ /* hack to put out a correct message describing the mRNA */ if (sameString(mrnaClick, "htcGeneMrna")) printf("%s from genomic sequences\n", mrnaDescription); else printf("%s (may be different from the genomic sequence)\n", mrnaDescription); puts("
    • \n"); puts("
    • \n"); hgcAnchorSomewhere(genomicClick, geneName, geneTable, seqName); printf("Genomic Sequence from assembly\n"); puts("
    • \n"); if (palInfo) { - struct sqlConnection *conn = hAllocConn(database); + struct sqlConnection *conn = hAllocConn(srcDb); addPalLink(conn, tdb->track, palInfo->chrom, palInfo->left, palInfo->right, palInfo->rnaName); hFreeConn(&conn); } printf("
    \n"); } void geneShowPosAndLinks(char *geneName, char *pepName, struct trackDb *tdb, char *pepTable, char *pepClick, char *mrnaClick, char *genomicClick, char *mrnaDescription) { geneShowPosAndLinksPal(geneName, pepName, tdb, pepTable, pepClick, mrnaClick, genomicClick, mrnaDescription, NULL); @@ -6608,34 +6633,36 @@ sqlSafef(query, sizeof(query), "select comment from rikenseq where id='%s';", seqid); sqlFreeResult(&sr); sr = sqlMustGetResult(conn, query); row = sqlNextRow(sr); if (row != NULL) { comment = row[0]; printf("Riken/comment: %s
    \n",comment); } } } -void printGeneCards(char *geneName) -/* Print out a link to GeneCards (Human only). */ +void printGeneCards(char *db, char *geneName) +/* Print out a link to GeneCards (Human only). + * Caller passes the assembly db so quickLifted callers can pass the source + * (quickLiftDb) rather than the destination hub_NNN_. */ { -if (startsWith("hg", database) && isNotEmpty(geneName)) +if (startsWith("hg", db) && isNotEmpty(geneName)) { printf("GeneCards: " "%s
    \n", geneName, geneName); } } int getImageId(struct sqlConnection *conn, char *acc) /* get the image id for a clone, or 0 if none */ { int imageId = 0; if (sqlTableExists(conn, imageCloneTable)) { struct sqlResult *sr; @@ -6996,31 +7023,35 @@ /* Print list of mRNA alignments. */ { printAlignmentsExtra(pslList, startFirst, hgcCommand, "htcCdnaAliInWindow", tableName, itemIn); } static struct psl *getAlignmentsTName(struct sqlConnection *conn, char *table, char *acc, char *tName) /* get the list of alignments for the specified acc and tName (if given). */ { struct sqlResult *sr = NULL; char **row; struct psl *psl, *pslList = NULL; boolean hasBin; char splitTable[HDB_MAX_TABLE_STRING]; char query[1024]; -if (!hFindSplitTable(database, seqName, table, splitTable, sizeof splitTable, &hasBin)) +// Use the conn's db rather than the global `database`; for quickLifted +// tracks the conn is on the source assembly while `database` is the +// destination (possibly hub_NNN_). +char *connDb = sqlGetDatabase(conn); +if (!hFindSplitTable(connDb, seqName, table, splitTable, sizeof splitTable, &hasBin)) errAbort("can't find table %s or %s_%s", table, seqName, table); if (isNotEmpty(tName)) sqlSafef(query, sizeof(query), "select * from %s where qName = '%s' and tName = '%s'", splitTable, acc, tName); else sqlSafef(query, sizeof(query), "select * from %s where qName = '%s'", splitTable, acc); sr = sqlGetResult(conn, query); while ((row = sqlNextRow(sr)) != NULL) { psl = pslLoad(row+hasBin); slAddHead(&pslList, psl); } sqlFreeResult(&sr); slReverse(&pslList); return pslList; @@ -12193,31 +12224,31 @@ if (printedCnt >= 1) printf("
    \n"); } sqlFreeResult(&sr); // show GeneReviews link(s) if (sqlTableExists(conn, "geneReviewsDetail")) { sqlSafef(query, sizeof(query), "select distinct r.name2 from %s l, omim2gene g, refGene r where l.omimId=%s and g.geneId=l.locusLinkId and g.entryType='gene' and chrom='%s' and txStart = %s and txEnd= %s", refLinkTable, itemName, chrom, chromStart, chromEnd); sr = sqlMustGetResult(conn, query); if (sr != NULL) { while ((row = sqlNextRow(sr)) != NULL) { - prGRShortRefGene(row[0]); + prGRShortRefGene(conn, row[0]); } } sqlFreeResult(&sr); } showOmimDisorderTable(conn, url, itemName); } printf(""); // #omimText } static void printOmimLocationDetails(struct trackDb *tdb, char *itemName, boolean encode) /* Print details of an OMIM Class 3 Gene entry. */ { char *liftDb = cloneString(trackDbSetting(tdb, "quickLiftDb")); @@ -13051,31 +13082,33 @@ int gbCdnaGetVersion(struct sqlConnection *conn, char *acc) /* return mrna/est version, or 0 if not available */ { int ver = 0; if (!sqlTableExists(conn, gbCdnaInfoTable)) { warn("Genbank information not shown below, the table %s is not installed " "on this server. ", gbCdnaInfoTable); //"The information below is a shortened version of the one shown on the " //"UCSC site", database); return 0; } -if (hHasField(database, gbCdnaInfoTable, "version")) +// Use the conn's db so we don't abort when called from quickLifted paths +// where `database` is the destination (possibly hub_NNN_). +if (hHasField(sqlGetDatabase(conn), gbCdnaInfoTable, "version")) { char query[128]; sqlSafef(query, sizeof(query), "select version from %s where acc = '%s'", gbCdnaInfoTable, acc); ver = sqlQuickNum(conn, query); } return ver; } static void prRefGeneXenoInfo(struct sqlConnection *conn, struct refLink *rl) /* print xeno refseq info, including linking to the browser, if any */ { char query[256]; sqlSafef(query, sizeof(query), "select o.name from %s g,%s o " "where (g.acc = '%s') and (o.id = g.organism)", @@ -13090,37 +13123,42 @@ printf("UCSC browser: \n"); linkToOtherBrowserSearch(xenoDb, rl->mrnaAcc); printf("%s on %s (%s) \n", rl->mrnaAcc, hOrganism(xenoDb), xenoDb); printf("
    "); } freeMem(org); } void prRefGeneInfo(struct sqlConnection *conn, char *rnaName, char *sqlRnaName, struct refLink *rl, boolean isXeno) /* print basic details information and links for a RefGene */ { struct sqlResult *sr; char **row; char query[256]; +// For quickLifted tracks the conn is on the source assembly, while the +// `database` global is the destination (possibly hub_NNN_, which is +// not a real MySQL db). Use the conn's db for hTableExists/startsWith +// checks so we don't abort on "Unknown database". +char *srcDb = sqlGetDatabase(conn); int ver = gbCdnaGetVersion(conn, rl->mrnaAcc); char *cdsCmpl = NULL; printf("\n"); if (isXeno) { - if (startsWith("panTro", database)) + if (startsWith("panTro", srcDb)) printf("

    Other RefSeq Gene %s

    \n", rl->name); else printf("

    Non-%s RefSeq Gene %s

    \n", organism, rl->name); } else printf("

    RefSeq Gene %s

    \n", rl->name); printf("RefSeq: mrnaAcc); if (ver > 0) printf("\" TARGET=_blank>%s.%d", rl->mrnaAcc, ver); else printf("\" TARGET=_blank>%s", rl->mrnaAcc); /* If refSeqStatus is available, report it: */ if (sqlTableExists(conn, refSeqStatusTable)) @@ -13177,88 +13215,88 @@ printf("%s
    \n",mgiID, mgiID); } else { /* per Carol Bult from Jackson Lab 4/12/02, JAX do not always agree * with Locuslink on seq to gene association. * Thus, not finding a MGIid even if a LocusLink ID * exists is always a possibility. */ } sqlFreeResult(&sr); } } if (!startsWith("Worm", organism)) { - if (startsWith("dm", database)) + if (startsWith("dm", srcDb)) { /* PubMed never seems to have BDGP gene IDs... so if that's all * that's given for a name/product, ignore name / truncate product. */ char *cgp = strstr(rl->product, "CG"); if (cgp != NULL) { char *cgWord = firstWordInLine(cloneString(cgp)); char *dashp = strchr(cgWord, '-'); if (dashp != NULL) *dashp = 0; if (isBDGPName(cgWord)) *cgp = 0; } if (! isBDGPName(rl->name)) medlineLinkedLine("PubMed on Gene", rl->name, rl->name); if (rl->product[0] != 0) medlineProductLinkedLine("PubMed on Product", rl->product); } else { medlineLinkedLine("PubMed on Gene", rl->name, rl->name); if (rl->product[0] != 0) medlineProductLinkedLine("PubMed on Product", rl->product); } printf("\n"); - printGeneCards(rl->name); + printGeneCards(srcDb, rl->name); } -if (hTableExists(database, "jaxOrtholog")) +if (hTableExists(srcDb, "jaxOrtholog")) { struct jaxOrtholog jo; char * sqlRlName = rl->name; /* Make sure to escape single quotes for DB parseability */ if (strchr(rl->name, '\'')) { sqlRlName = replaceChars(rl->name, "'", "''"); } sqlSafef(query, sizeof(query), "select * from jaxOrtholog where humanSymbol='%s'", sqlRlName); sr = sqlGetResult(conn, query); while ((row = sqlNextRow(sr)) != NULL) { jaxOrthologStaticLoad(row, &jo); printf("MGI Mouse Ortholog: "); printf("", jo.mgiId); printf("%s
    \n", jo.mouseSymbol); } sqlFreeResult(&sr); } -if (startsWith("hg", database)) +if (startsWith("hg", srcDb)) { printf("\n"); printf("AceView: "); printf("", rl->name); printf("%s
    \n", rl->name); } -prGRShortRefGene(rl->name); +prGRShortRefGene(conn, rl->name); } void prKnownGeneInfo(struct sqlConnection *conn, char *rnaName, char *sqlRnaName, struct refLink *rl) /* print basic details information and links for a Known Gene */ { struct sqlResult *sr; char **row; char query[256]; int ver = gbCdnaGetVersion(conn, rl->mrnaAcc); char *cdsCmpl = NULL; printf("\n"); @@ -13414,31 +13452,31 @@ geneShowPosAndLinksPal(rl->mrnaAcc, rl->protAcc, tdb, refPepTable, "htcTranslatedProtein", "htcRefMrna", "htcGeneInGenome", "mRNA Sequence",palInfo); printTrackHtml(tdb); hFreeConn(&conn); } static struct refLink *printRefSeqInfo( struct sqlConnection *conn, struct trackDb *tdb, char *rnaName, char *version) { struct sqlResult *sr; char **row; char query[256]; char *sqlRnaName = rnaName; char *summary = NULL; -boolean isXeno = sameString(tdb->table, "xenoRefGene"); +boolean isXeno = sameString(trackHubSkipHubName(tdb->table), "xenoRefGene"); struct refLink *rl; /* Make sure to escape single quotes for DB parseability */ if (strchr(rnaName, '\'')) { sqlRnaName = replaceChars(rnaName, "'", "''"); } /* get refLink entry */ if (version == NULL) { sqlSafef(query, sizeof(query), "select * from %s where mrnaAcc = '%s'", refLinkTable, sqlRnaName); sr = sqlGetResult(conn, query); if ((row = sqlNextRow(sr)) == NULL) errAbort("This accession (%s) is no longer in our database. Check NCBI for status on this accession.", rnaName); rl = refLinkLoad(row); @@ -13469,31 +13507,36 @@ if (summary != NULL) { htmlHorizontalLine(); printf("

    Summary of %s

    \n", rl->name); printf("

    %s

    \n", summary); freeMem(summary); } htmlHorizontalLine(); return rl; } void doNcbiRefSeq(struct trackDb *tdb, char *itemName) /* Process click on a NCBI RefSeq gene. */ { -struct sqlConnection *conn = hAllocConn(database); +// When the track is quickLifted, ncbiRefSeqLink and related tables live in +// the source assembly, not in the destination the user is viewing. +char *liftDb = trackDbSetting(tdb, "quickLiftDb"); +char *srcDb = (liftDb != NULL) ? liftDb : database; +char *bareTrack = trackHubSkipHubName(tdb->track); +struct sqlConnection *conn = hAllocConn(srcDb); struct sqlResult *sr; char **row; char query[256]; struct ncbiRefSeqLink *nrl; cartWebStart(cart, database, "%s - %s ", tdb->longLabel, itemName); /* get refLink entry */ sqlSafef(query, sizeof(query), "select * from ncbiRefSeqLink where id = '%s'", itemName); sr = sqlGetResult(conn, query); if ((row = sqlNextRow(sr)) == NULL) errAbort("Couldn't find %s in ncbiRefSeqLink table.", itemName); nrl = ncbiRefSeqLinkLoad(row); sqlFreeResult(&sr); @@ -13574,181 +13617,223 @@ } } if (sqlColumnExists(conn, "ncbiRefSeqLink", "externalId")) { if (differentWord(nrl->externalId, "")) { char *urlsStr = trackDbSetting(tdb, "dbPrefixUrls"); struct hash* dbToUrl = hashFromString(urlsStr); char *labelsStr = trackDbSetting(tdb, "dbPrefixLabels"); struct hash* dbToLabel = hashFromString(labelsStr); if (dbToUrl) { if (!dbToLabel) errAbort("can not find trackDb dbPrefixLabels to correspond with dbPrefixUrls\n"); - char *databasePrefix = cloneString(database); + char *databasePrefix = cloneString(srcDb); while (strlen(databasePrefix) && isdigit(lastChar(databasePrefix))) trimLastChar(databasePrefix); struct hashEl *hel = hashLookup(dbToUrl, databasePrefix); if (hel) { struct hashEl *label = hashLookup(dbToLabel, databasePrefix); if (!label) errAbort("missing trackDb dbPrefixLabels for database prefix: '%s'\n", databasePrefix); char *url = (char *)hel->val; char *labelStr = (char *)label->val; - char *idUrl = replaceInUrl(url, nrl->externalId, cart, database, - nrl->externalId, winStart, winEnd, tdb->track, TRUE, NULL); + char *idUrl = replaceInUrl(url, nrl->externalId, cart, srcDb, + nrl->externalId, winStart, winEnd, bareTrack, TRUE, NULL); printf("%s: ", labelStr); printf("%s
    \n", idUrl, nrl->externalId); } } } } if (differentWord(nrl->locusLinkId, "")) { printf("Entrez Gene: "); printf("", nrl->locusLinkId); printf("%s
    \n", nrl->locusLinkId); } if (differentWord(nrl->name,"")) { - printGeneCards(nrl->name); - if (startsWith("hg", database)) + printGeneCards(srcDb, nrl->name); + if (startsWith("hg", srcDb)) { printf("AceView: "); printf("", nrl->name); printf("%s
    \n", nrl->name); } } htmlHorizontalLine(); if (differentWord("", nrl->description)) { printf("Summary of %s
    \n%s
    \n", nrl->name, nrl->description); htmlHorizontalLine(); } static boolean hasSequence = TRUE; +// Alignments live in the source assembly's coordinates and the table's +// htcCdnaAli links wouldn't line up with the destination window; skip +// the block entirely for quickLifted tracks. +if (liftDb == NULL) + { struct psl *pslList = getAlignments(conn, "ncbiRefSeqPsl", itemName); // if the itemName isn't found, it might be found as the nrl->mrnaAcc if (! pslList) pslList = getAlignments(conn, "ncbiRefSeqPsl", nrl->mrnaAcc); if (pslList) { char query[256]; /* verify itemName has RNA sequence to work with */ sqlSafef(query, sizeof(query), "select id from seqNcbiRefSeq where acc='%s' limit 1", itemName); char * result= sqlQuickString(conn, query); if (isEmpty(result)) { printf ("

    No sequence available for %s, can't display alignment.

    \n", itemName); hasSequence = FALSE; } else { printf("

    mRNA/Genomic Alignments (%s)

    ", itemName); int start = cartInt(cart, "o"); printAlignments(pslList, start, "htcCdnaAli", "ncbiRefSeqPsl", itemName); } } else { printf ("

    Missing alignment for %s


    \n", itemName); } - htmlHorizontalLine(); + } -if (! ( sameString(tdb->track, "ncbiRefSeqPsl") || sameString(tdb->track, "ncbiRefSeqOther" ) ) ) +if (! ( sameString(bareTrack, "ncbiRefSeqPsl") || sameString(bareTrack, "ncbiRefSeqOther" ) ) ) showGenePos(itemName, tdb); printf("

    Links to sequence:

    \n"); printf("
      \n"); if (differentWord("", nrl->protAcc)) { puts("
    • \n"); + // For quickLifted tracks, translate from destination-genome CDS so + // any destination-specific substitutions are reflected. On the + // source assembly, keep the NCBI-authored protein from refPep/gbSeq. + if (liftDb != NULL) + hgcAnchorSomewhere("htcTranslatedPredMRna", itemName, "ncbiRefSeqPepTable", seqName); + else hgcAnchorSomewhere("htcTranslatedProtein", nrl->protAcc, "ncbiRefSeqPepTable", seqName); printf("Predicted Protein \n"); puts("
    • \n"); } if (hasSequence) { puts("
    • \n"); + // Same split: destination-derived mRNA when quickLifted, NCBI-authored + // seqNcbiRefSeq otherwise. + if (liftDb != NULL) + hgcAnchorSomewhere("htcGeneMrna", itemName, "ncbiRefSeqPsl", seqName); + else hgcAnchorSomewhere("ncbiRefSeqSequence", itemName, "ncbiRefSeqPsl", seqName); printf("%s may be different from the genomic sequence.\n", "Predicted mRNA"); puts("
    • \n"); } puts("
    • \n"); hgcAnchorSomewhere("getDna", itemName, tdb->track, seqName); printf("Genomic Sequence from assembly\n"); puts("
    • \n"); printf("
    \n"); printTrackHtml(tdb); hFreeConn(&conn); } void doRefGene(struct trackDb *tdb, char *rnaName) /* Process click on a known RefSeq gene. */ { -struct sqlConnection *conn = hAllocConn(database); +// When the track is quickLifted, refLink/refSeqAli/xenoRefSeqAli live in +// the source assembly. Connect there and compare against the bare track +// name (tdb->track and tdb->table have a hub_NNN_ prefix otherwise). +char *liftDb = trackDbSetting(tdb, "quickLiftDb"); +char *srcDb = (liftDb != NULL) ? liftDb : database; +char *bareTrack = trackHubSkipHubName(tdb->track); +struct sqlConnection *conn = hAllocConn(srcDb); int start = cartInt(cart, "o"); int left = cartInt(cart, "l"); int right = cartInt(cart, "r"); char *chrom = cartString(cart, "c"); -boolean isXeno = sameString(tdb->table, "xenoRefGene"); +boolean isXeno = sameString(bareTrack, "xenoRefGene"); if (isXeno) cartWebStart(cart, database, "Non-%s RefSeq Gene", organism); else cartWebStart(cart, database, "RefSeq Gene"); struct refLink *rl = printRefSeqInfo( conn, tdb, rnaName, NULL); /* print alignments that track was based on */ -char *aliTbl = (sameString(tdb->table, "refGene") ? "refSeqAli" : "xenoRefSeqAli"); -if (hTableExists(database, aliTbl)) +// Alignments live in the source assembly's coordinates and the table's +// htcCdnaAli links wouldn't line up with the destination window; skip +// the block entirely for quickLifted tracks. +char *aliTbl = (sameString(bareTrack, "refGene") ? "refSeqAli" : "xenoRefSeqAli"); +if (liftDb == NULL) + { + if (hTableExists(srcDb, aliTbl)) { struct psl *pslList = getAlignments(conn, aliTbl, rl->mrnaAcc); printf("

    mRNA/Genomic Alignments

    "); printAlignments(pslList, start, "htcCdnaAli", aliTbl, rl->mrnaAcc); } else warn("Sequence alignment links not shown below, the table %s.refSeqAli is not installed " - "on this server", database); - + "on this server", srcDb); htmlHorizontalLine(); + } struct palInfo *palInfo = NULL; if (genbankIsRefSeqCodingMRnaAcc(rnaName)) { AllocVar(palInfo); palInfo->chrom = chrom; palInfo->left = left; palInfo->right = right; palInfo->rnaName = rnaName; } -geneShowPosAndLinksPal(rl->mrnaAcc, rl->protAcc, tdb, refPepTable, "htcTranslatedProtein", - "htcRefMrna", "htcGeneInGenome", "mRNA Sequence",palInfo); +// For quickLifted tracks, route the Predicted Protein/mRNA links to the +// genome-derived handlers so the sequences reflect the destination +// assembly at the lifted exon coordinates, rather than the NCBI-authored +// sequence that lives with the source refPep/refMrna extFiles. +// htcTranslatedPredMRna keys on the transcript name rather than the +// protein accession, so swap the pepName for that case. +char *pepClick = "htcTranslatedProtein"; +char *pepName = rl->protAcc; +char *mrnaClick = "htcRefMrna"; +if (liftDb != NULL) + { + pepClick = "htcTranslatedPredMRna"; + pepName = rl->mrnaAcc; + mrnaClick = "htcGeneMrna"; + } +geneShowPosAndLinksPal(rl->mrnaAcc, pepName, tdb, refPepTable, pepClick, + mrnaClick, "htcGeneInGenome", "mRNA Sequence",palInfo); printTrackHtml(tdb); hFreeConn(&conn); } char *kgIdToSpId(struct sqlConnection *conn, char* kgId) /* get the swissprot id for a known genes id; resulting string should be * freed */ { char query[512]; sqlSafef(query, sizeof(query), "select spID from kgXref where kgID='%s'", kgId); return sqlNeedQuickString(conn, query); } void doHInvGenes(struct trackDb *tdb, char *item) @@ -27200,30 +27285,34 @@ { cartWebStart(cart, database, "%s", track); warn("Sorry, clicking there doesn't do anything yet (%s).", track); } } cartHtmlEnd(); } boolean findNameBasedHandler(struct trackDb *tdb, char *track, char *item) // call hander routine based on name. Return TRUE if we called a handler { char* handler = trackDbSetting(tdb, "trackHandler"); char *table = (tdb ? tdb->table : track); +// Quick-lifted tracks come in with hub_NNN_ prefixed names; strip the prefix +// so the unprefixed comparisons below match and the native handler fires. +if (tdb && trackDbSetting(tdb, "quickLifted")) + table = trackHubSkipHubName(table); if (sameWord(table, "getDna")) { htmlDoNotTranslate(); doGetDna1(); } else if (sameWord(table, "htcGetDna2")) { htmlDoNotTranslate(); doGetDna2(); } else if (sameWord(table, "htcGetDna3")) { htmlDoNotTranslate(); doGetDna3(); }