ef4a5ad1ea45eda4fde693f56f2106c553e87d93 chmalee Wed May 20 15:24:30 2026 -0700 Add cnv type to myVariants, refs #33808 diff --git src/hg/hgc/myVariantsClick.c src/hg/hgc/myVariantsClick.c index 4086546d22e..6c3d2cb4789 100644 --- src/hg/hgc/myVariantsClick.c +++ src/hg/hgc/myVariantsClick.c @@ -107,30 +107,32 @@ /* Show shared banner */ if (isShared) { if (canEdit) printf("<div style='padding:6px; margin-bottom:8px; background:#e8f5e9; " "border:1px solid #a5d6a7; border-radius:4px'>" "<B>Shared from %s</B></div>\n", htmlEncode(dataOwner)); else printf("<div style='padding:6px; margin-bottom:8px; background:#e3f2fd; " "border:1px solid #90caf9; border-radius:4px'>" "<B>Shared from %s (read-only)</B></div>\n", htmlEncode(dataOwner)); } if (canEdit) { + boolean isTranscript = sameOk(item->itemType, "transcript"); + boolean isCnv = sameOk(item->itemType, "cnv"); webIncludeResourceFile("spectrum.min.css"); jsIncludeFile("spectrum.min.js", NULL); printf("<FORM ACTION=\"%s\" METHOD=\"POST\">\n\n", hgTracksName()); cartSaveSession(cart); /* Save away ID string in hidden var. */ char varName[128]; char idStr[128]; safef(varName, sizeof(varName), "%s_%s", trackName, "id"); safef(idStr, sizeof(idStr), "%d", item->id); cgiMakeHiddenVar(varName, idStr); /* Put up editable label. */ safef(varName, sizeof(varName), "%s_%s", trackName, "name"); printf("<B>Label:</B> "); @@ -152,30 +154,44 @@ /* Editable start and end. */ int chromSize = hChromSize(database, item->chrom); char chromSizeString[16]; safef(chromSizeString, sizeof(chromSizeString), "%d", chromSize); printf("<B>Start:</B> "); safef(varName, sizeof(varName), "%s_%s", trackName, "chromStart"); cgiMakeIntVarInRange(varName, item->chromStart, NULL, 80, "0", chromSizeString); printInfoIcon("0-based start position on the chromosome."); printf("<BR>\n"); printf("<B>End:</B> "); safef(varName, sizeof(varName), "%s_%s", trackName, "chromEnd"); cgiMakeIntVarInRange(varName, item->chromEnd, NULL, 80, "1", chromSizeString); printInfoIcon("0-based half-open end position on the chromosome."); printf("<BR>\n"); + if (isTranscript) + { + /* Editable CDS Start / CDS End (stored as thickStart/thickEnd). */ + printf("<B>CDS Start:</B> "); + safef(varName, sizeof(varName), "%s_%s", trackName, "thickStart"); + cgiMakeIntVarInRange(varName, item->thickStart, NULL, 80, "0", chromSizeString); + printInfoIcon("Start of the coding region."); + printf("<BR>\n"); + printf("<B>CDS End:</B> "); + safef(varName, sizeof(varName), "%s_%s", trackName, "thickEnd"); + cgiMakeIntVarInRange(varName, item->thickEnd, NULL, 80, "0", chromSizeString); + printInfoIcon("End of the coding region."); + printf("<BR>\n"); + /* Blocks (BED12). Hidden inputs are kept in sync by the widget; * updateBlocksFields in hgTracks reads them on submit. */ char vBC[128], vBS[128], vBT[128], vCS[128], vCE[128]; safef(vBC, sizeof(vBC), "%s_blockCount", trackName); safef(vBS, sizeof(vBS), "%s_blockSizes", trackName); safef(vBT, sizeof(vBT), "%s_chromStarts", trackName); safef(vCS, sizeof(vCS), "%s_chromStart", trackName); safef(vCE, sizeof(vCE), "%s_chromEnd", trackName); struct dyString *sizesCsv = dyStringNew(64); struct dyString *startsCsv = dyStringNew(64); int bi; for (bi = 0; bi < item->blockCount; bi++) { if (bi > 0) { @@ -203,61 +219,87 @@ " var sizes = sizesEl.value ? sizesEl.value.split(',').map(Number) : [];\n" " var starts = startsEl.value ? startsEl.value.split(',').map(Number) : [];\n" " myVariantsBlocks.mount('myVariantsBlocksUi', {\n" " initialSizes: sizes,\n" " initialStarts: starts,\n" " getStart: function(){ return parseInt(document.getElementById('%s').value, 10); },\n" " getEnd: function(){ return parseInt(document.getElementById('%s').value, 10); },\n" " hiddenCountInput: document.getElementById('%s'),\n" " hiddenSizesInput: sizesEl,\n" " hiddenStartsInput: startsEl\n" " });\n" "});\n", vBS, vBT, vCS, vCE, vBC); dyStringFree(&sizesCsv); dyStringFree(&startsCsv); + } /* Edit the color */ safef(varName, sizeof(varName), "%s_%s", trackName, "itemRgb"); char colorHex[8]; safef(colorHex, sizeof(colorHex), "#%06X", item->itemRgb); hPrintf("<label for=\"%s\"><b>Color:</b></label> ", varName); hPrintf("<input type=\"text\" name=\"%s\" id=\"%s\" value=\"%s\">\n", varName, varName, colorHex); jsInlineF( "$(function() {" "$(document.getElementById('%s')).spectrum({" "preferredFormat: 'hex'," "showInput: true," "showPalette: true," "hideAfterPaletteSelect: true" "});" "});\n", varName); printf("<br>"); - /* Edit ref/alt */ + /* Edit ref/alt. Transcript has no alleles; CNV stores a sequence in alt. */ + if (isCnv) + { + safef(varName, sizeof(varName), "%s_%s", trackName, "cnvType"); + printf("<B>CNV type:</B> "); + htmlPrintf("<select name='%s|attr|' id='%s|attr|'>", varName, varName); + int ci; + for (ci = 0; ci < myVariantsNumCnvTypes; ci++) + { + boolean sel = sameOk(item->cnvType, myVariantsCnvTypes[ci]); + htmlPrintf("<option value='%s|attr|'%s|none|>%s</option>", + myVariantsCnvTypes[ci], sel ? " selected" : "", + myVariantsCnvTypes[ci]); + } + printf("</select>"); + printInfoIcon("CNV vocabulary follows gnomAD"); + printf("<BR>\n"); + safef(varName, sizeof(varName), "%s_%s", trackName, "alt"); + printf("<B>Sequence:</B> "); + cgiMakeTextVar(varName, item->alt, 40); + printInfoIcon("Inserted or duplicated sequence at this position."); + printf("<BR>\n"); + } + else if (!isTranscript) + { safef(varName, sizeof(varName), "%s_%s", trackName, "ref"); printf("<B>Ref:</B> "); cgiMakeTextVar(varName, item->ref, 17); printInfoIcon("Reference allele sequence at this position."); printf("<BR>\n"); safef(varName, sizeof(varName), "%s_%s", trackName, "alt"); printf("<B>Alt:</B> "); cgiMakeTextVar(varName, item->alt, 17); printInfoIcon("Alternate (variant) allele sequence."); printf("<BR>\n"); + } /* Project: locked for shared tracks, editable for own track */ printf("<B>Project:</B> "); if (isShared) { htmlPrintf("%s<BR>\n", isNotEmpty(item->project) ? item->project : "(none)"); } else { safef(varName, sizeof(varName), "%s_%s", trackName, "project"); struct slName *projects = myVariantsGetProjects(dataOwner); if (projects) { char selectName[128]; safef(selectName, sizeof(selectName), "%s_projectSelect", trackName); @@ -361,34 +403,37 @@ int roBi; for (roBi = 0; roBi < item->blockCount; roBi++) { if (roBi > 0) printf(", "); int relStart = item->chromStarts[roBi]; int relEnd = relStart + item->blockSizes[roBi]; printf("%d-%d (%dbp)", relStart, relEnd, item->blockSizes[roBi]); } printf("<BR>\n"); } char colorHex[8]; safef(colorHex, sizeof(colorHex), "#%06X", item->itemRgb); printf("<B>Color:</B> <span style='background:%s; padding:2px 12px'> </span> %s<BR>\n", colorHex, colorHex); + boolean roIsCnv = sameOk(item->itemType, "cnv"); + if (roIsCnv && isNotEmpty(item->cnvType)) + htmlPrintf("<B>CNV type:</B> %s<BR>\n", item->cnvType); if (isNotEmpty(item->ref)) htmlPrintf("<B>Ref:</B> %s<BR>\n", item->ref); if (isNotEmpty(item->alt)) - htmlPrintf("<B>Alt:</B> %s<BR>\n", item->alt); + htmlPrintf("<B>%s:</B> %s<BR>\n", roIsCnv ? "Sequence" : "Alt", item->alt); if (isNotEmpty(item->project)) htmlPrintf("<B>Project:</B> %s<BR>\n", item->project); if (isNotEmpty(item->mouseover)) htmlPrintf("<B>Mouseover:</B> %s<BR>\n", item->mouseover); /* Custom fields read-only */ { struct slName *customCols = myVariantsGetCustomFields(dataOwner); if (customCols) { struct dyString *cfQuery = dyStringNew(256); struct slName *col; sqlDyStringPrintf(cfQuery, "SELECT "); boolean first = TRUE; for (col = customCols; col != NULL; col = col->next)