40b4fc89ec72c22cae51bf9a8b9a7e8dd887eebf chmalee Thu Jun 4 15:34:16 2026 -0700 Show MANE-relative HGVS terms on myVariants SNV detail pages, refs #33808 Co-Authored-By: Claude Opus 4.8 (1M context) diff --git src/hg/hgc/myVariantsClick.c src/hg/hgc/myVariantsClick.c index 6c3d2cb4789..2cfed4e85f9 100644 --- src/hg/hgc/myVariantsClick.c +++ src/hg/hgc/myVariantsClick.c @@ -8,30 +8,136 @@ #include "linefile.h" #include "hgc.h" #include "myVariants.h" #include "myVariantsShare.h" #include "obscure.h" #include "cheapcgi.h" #include "hgMaf.h" #include "hui.h" #include "hCommon.h" #include "wikiLink.h" #include "jsHelper.h" #include "web.h" #include "hgConfig.h" #include "jsonWrite.h" #include "htmshell.h" +#include "hgHgvs.h" +#include "variantProjector.h" +#include "genePred.h" +#include "genbank.h" +#include "bigBed.h" +#include "chromAlias.h" + +static struct genePred *getGeneTrackOverlaps(struct trackDb *geneTdb, char *db, char *chrom, + int start, int end) +/* Return transcripts in geneTdb overlapping chrom:start-end, from a bigGenePred file + * or a genePred SQL table. */ +{ +struct genePred *gpList = NULL; +if (sameString(geneTdb->type, "bigGenePred")) + { + char *fileName = hReplaceGbdb(trackDbSetting(geneTdb, "bigDataUrl")); + if (isEmpty(fileName)) + return NULL; + struct bbiFile *bbi = bigBedFileOpenAlias(fileName, chromAliasFindAliases); + struct lm *lm = lmInit(0); + struct bigBedInterval *bb, *bbList = bigBedIntervalQuery(bbi, chrom, start, end, 0, lm); + for (bb = bbList; bb != NULL; bb = bb->next) + { + struct genePred *gp = (struct genePred *)genePredFromBigGenePred(chrom, bb); + if (gp != NULL) + slAddHead(&gpList, gp); + } + lmCleanup(&lm); + bigBedFileClose(&bbi); + freeMem(fileName); + } +else + { + struct sqlConnection *conn = hAllocConn(db); + int rowOffset = 0; + struct sqlResult *sr = hRangeQuery(conn, geneTdb->table, chrom, start, end, NULL, &rowOffset); + char **row; + while ((row = sqlNextRow(sr)) != NULL) + slAddHead(&gpList, genePredLoad(row + rowOffset)); + sqlFreeResult(&sr); + hFreeConn(&conn); + } +slReverse(&gpList); +return gpList; +} + +static void printMyVariantsHgvs(struct myVariants *item) +/* For SNV items, show the genomic HGVS g. term and a c./n. term for each overlapping + * transcript in the assembly's default (MANE-first) gene track. */ +{ +if (!sameOk(item->itemType, "snv")) + return; +char *db = item->db; +struct seqWindow *gSeqWin = chromSeqWindowNew(db, NULL, 0, 0); +struct bed3 variantBed; +ZeroVar(&variantBed); +variantBed.chrom = item->chrom; +variantBed.chromStart = item->chromStart; +variantBed.chromEnd = item->chromEnd; +char *chromAcc = hRefSeqAccForChrom(db, item->chrom); +/* vpGenomicToTranscript requires an IUPAC alt (or "*"/"") and aborts otherwise, and + * rewrites "" in place; work on a copy, normalize "", and skip transcript terms + * for any alt it can't project. */ +char *alt = cloneString(item->alt); +boolean altProjectable = (isAllNt(alt, strlen(alt)) || + sameString(alt, "*") || sameString(alt, "")); +if (sameString(alt, "")) + alt[0] = '\0'; +char *hgvsG = hgvsGFromVariant(gSeqWin, &variantBed, alt, chromAcc, FALSE); + +struct trackDb *geneTdb = altProjectable ? hgvsDefaultGeneTrack(db) : NULL; +if (geneTdb != NULL) + htmlPrintf("HGVS (relative to %s):
\n", geneTdb->shortLabel); +else + printf("HGVS:
\n"); +if (isNotEmpty(hgvsG)) + htmlPrintf("  %s
\n", hgvsG); + +if (geneTdb == NULL) + return; + +struct genePred *gp, *gpList = getGeneTrackOverlaps(geneTdb, db, item->chrom, + item->chromStart, item->chromEnd); +for (gp = gpList; gp != NULL; gp = gp->next) + { + char *seq = txSeqFromGp(db, gp); + struct dnaSeq *txSeq = newDnaSeq(seq, strlen(seq), gp->name); + struct genbankCds cds; + genePredToCds(gp, &cds); + struct psl *psl = genePredToPsl(gp, hChromSize(db, gp->chrom), txSeq->size); + vpExpandIndelGaps(psl, gSeqWin, txSeq); + struct vpTx *vpTx = vpGenomicToTranscript(gSeqWin, &variantBed, alt, psl, txSeq); + char *hgvsTx = NULL; + if (cds.end > cds.start) + hgvsTx = hgvsCFromVpTx(vpTx, gSeqWin, psl, &cds, txSeq, FALSE); + else + hgvsTx = hgvsNFromVpTx(vpTx, gSeqWin, psl, txSeq, FALSE); + if (isNotEmpty(hgvsTx)) + { + if (isNotEmpty(gp->name2)) + htmlPrintf("  %s (%s):%s
\n", gp->name, gp->name2, hgvsTx); + else + htmlPrintf("  %s:%s
\n", gp->name, hgvsTx); + } + } +} void doMyVariantsDetails(struct customTrack *ct, char *itemIdString) /* Show details of a myVariants item. */ { jsIncludeFile("hgc.js",NULL); char *idString = cloneString(itemIdString); char *trackName = ct->tdb->track; /* Detect shared track and resolve table/permissions via hgcentral so that * revoked or downgraded shares no longer return owner data. */ boolean isShared = isMyVariantsSharedTrack(trackName); char *dataOwner = NULL; /* user whose table holds the data */ char *scopeProject = NULL; /* live share's project, or NULL for own track */ char *scopeDb = NULL; /* live share's db, or NULL for own track */ int permission = MYVAR_PERM_READONLY; @@ -452,30 +558,32 @@ for (col = customCols; col != NULL; col = col->next, i++) { if (cfRow[i] && cfRow[i][0]) htmlPrintf("%s: %s
\n", col->name, cfRow[i]); } } sqlFreeResult(&cfSr); dyStringFree(&cfQuery); slFreeList(&customCols); } } } printf("id: %d
\n", item->id); + printMyVariantsHgvs(item); + /* Overlaps section: only emit if hg.conf names overlap tracks for this assembly. * Format: myVariantsOverlapTracks. = track1,track2,... */ char overlapKey[256]; safef(overlapKey, sizeof(overlapKey), "myVariantsOverlapTracks.%s", database); char *overlapList = cfgOption(overlapKey); if (isNotEmpty(overlapList)) { struct jsonWrite *jw = jsonWriteNew(); jsonWriteListStart(jw, NULL); struct slName *trackNames = slNameListFromComma(overlapList); struct slName *t; for (t = trackNames; t != NULL; t = t->next) jsonWriteString(jw, NULL, t->name); jsonWriteListEnd(jw); jsInline("var doItemOverlaps = true;\n");