5c213974bb28e98d1b10ad9a86063101e684e5e3 braney Mon Oct 31 12:16:52 2022 -0700 add Jim's track duplication code diff --git src/hg/hgTrackUi/hgTrackUi.c src/hg/hgTrackUi/hgTrackUi.c index e6accf8..42fcdbd 100644 --- src/hg/hgTrackUi/hgTrackUi.c +++ src/hg/hgTrackUi/hgTrackUi.c @@ -18,30 +18,31 @@ #include "snpUi.h" #include "snp125Ui.h" #include "snp125.h" #include "sample.h" #include "wiggle.h" #include "hgMaf.h" #include "obscure.h" #include "chainCart.h" #include "chainDb.h" #include "gvUi.h" #include "grp.h" #include "oregannoUi.h" #include "chromGraph.h" #include "hgConfig.h" #include "customTrack.h" +#include "dupTrack.h" #include "dbRIP.h" #include "tfbsConsSites.h" #include "hapmapSnps.h" #include "nonCodingUi.h" #include "expRecord.h" #include "wikiTrack.h" #include "hubConnect.h" #include "trackHub.h" #include "pcrResult.h" #include "dgv.h" #include "transMapStuff.h" #include "vcfUi.h" #include "bbiFile.h" #include "ensFace.h" #include "microarray.h" @@ -3152,30 +3153,46 @@ } printf("<tr>"); printf("<td><a href='%s?%s=%s&c=%s&g=%s'>%s</a> </td>", tdbIsDownloadsOnly(sibTdb) ? hgFileUiName(): hTrackUiForTrack(sibTdb->track), cartSessionVarName(), cartSessionId(cart), chromosome, cgiEncode(sibTdb->track), sibTdb->shortLabel); printf("<td>%s</td></tr>\n", sibTdb->longLabel); } printf("</table>"); jsEndCollapsibleSection(); printf("</table>"); // required by jsCollapsible printf("<hr>"); printf("</p>"); } +boolean tdbIsDupable(struct trackDb *tdb) +/* Return TRUE if a track is duplicatable */ +{ +/* Can't handle container tracks yet at least */ +if (!tdbIsDataTrack(tdb)) + return FALSE; +/* A few other special case we can't handle */ +if (sameString(tdb->track, "ruler")) + return FALSE; +if (sameString(tdb->track, "cutters")) + return FALSE; +if (sameString(tdb->track, "oligoMatch")) + return FALSE; +return TRUE; +} + void trackUi(struct trackDb *tdb, struct trackDb *tdbList, struct customTrack *ct, boolean ajax) /* Put up track-specific user interface. */ { if (!ajax) { jsIncludeFile("jquery.js", NULL); webIncludeResourceFile("jquery-ui.css"); jsIncludeFile("jquery-ui.js", NULL); jsIncludeFile("utils.js",NULL); webIncludeResourceFile("spectrum.min.css"); jsIncludeFile("spectrum.min.js",NULL); jsIncludeFile("jquery.tablednd.js", NULL); jsonObjectAddGlobal("track", newJsonString(tdb->track)); jsonObjectAddGlobal("db", newJsonString(database)); } @@ -3377,32 +3394,46 @@ { printf(" "); cgiMakeOnClickButton("htui_cancel", "window.history.back();","Cancel"); } if (tdbIsComposite(tdb)) { printf("\n <a href='#' id='htui_reset'>Reset to defaults</a>\n"); jsOnEventByIdF("click", "htui_reset", "setVarAndPostForm('%s','1','mainForm'); return false;", setting); } if ( isCustomComposite(tdb)) { printf("\n <a href='%s' >Go to Track Collection Builder</a>\n", hgCollectionName()); } + /* Offer to dupe the non-containery tracks including composite and supertrack elements */ + if (tdbIsDupable(tdb)) + { + printf("\n <a href='%s?%s=%s&c=%s&g=%s&hgTrackUi_op=dupe' >Duplicate track</a>\n", + hgTrackUiName(), cartSessionVarName(), cartSessionId(cart), + chromosome, cgiEncode(tdb->track)); + if (isDupTrack(tdb->track)) + { + /* Offer to undupe */ + printf("\n <a href='%s?%s=%s&c=%s&g=%s&hgTrackUi_op=undupe' >Remove duplicate</a>\n", + hgTrackUiName(), cartSessionVarName(), cartSessionId(cart), + chromosome, cgiEncode(tdb->track)); + } } + } if (ct) { puts(" "); cgiMakeButton(CT_DO_REMOVE_VAR, "Remove custom track"); cgiMakeHiddenVar(CT_SELECTED_TABLE_VAR, tdb->track); puts(" "); if (differentString(tdb->type, "chromGraph")) { char buf[256]; if (ajax) // reference to a separate form doesn't work in modal dialog, // so change window.location directly. safef(buf, sizeof(buf), "window.location='%s?hgsid=%s&%s=%s';return false;", hgCustomName(), cartSessionId(cart), CT_SELECTED_TABLE_VAR, tdb->track); @@ -3546,56 +3577,129 @@ struct trackDb *trackDbForRuler() /* Create a trackDb entry for the base position ruler. It is not (yet?) a real track, so doesn't appear in trackDb */ { return trackDbForPseudoTrack(RULER_TRACK_NAME, RULER_TRACK_LABEL, RULER_TRACK_LONGLABEL, tvFull, FALSE); } struct trackDb *trackDbForOligoMatch() /* Create a trackDb entry for the oligo matcher pseudo-track. */ { return trackDbForPseudoTrack(OLIGO_MATCH_TRACK_NAME, OLIGO_MATCH_TRACK_LABEL, OLIGO_MATCH_TRACK_LONGLABEL, tvHide, TRUE); } +static char *handleDupOp(char *track, struct hash *trackHash) +/* Handle the duplication operation in the URL if any. Return dupe name if + * a dupe has happened. The trackHash is keyed by track name and has + * struct trackDb values. */ +{ +char *newTrack = NULL; + +/* Handle duplicate, and possible in the future other operations on tracks. */ +char *opVar = "hgTrackUi_op"; +char *operation = cartUsualString(cart, opVar, NULL); +if (operation != NULL) + { + if (sameString(operation, "dupe")) + { + struct trackDb *tdb = hashFindVal(trackHash, dupTrackSkipToSourceName(track)); + newTrack = dupTrackInCartAndTrash(track, cart, tdb); + } + else if (sameString(operation, "undupe")) + { + newTrack = dupTrackSkipToSourceName(track); + undupTrackInCartAndTrash(track, cart); + } + else + { + internalErr(); + } + cartRemove(cart, opVar); + } +return newTrack; +} + void doMiddle(struct cart *theCart) /* Write body of web page. */ { struct trackDb *tdbList = NULL; struct trackDb *tdb = NULL; char *track; struct customTrack *ct = NULL, *ctList = NULL; char *ignored; /* used to have hgBotDelayFrac(0.25) here, replaced with earlyBotCheck() * at the beginning of main() to output message here if in delay time * 2021-06-21 - Hiram */ if (issueBotWarning) { char *ip = getenv("REMOTE_ADDR"); botDelayMessage(ip, botDelayMillis); } cart = theCart; track = cartString(cart, "g"); getDbAndGenome(cart, &database, &ignored, NULL); initGenbankTableNames(database); chromosome = cartUsualString(cart, "c", hDefaultChrom(database)); - trackHash = trackHashMakeWithComposites(database,chromosome,&tdbList,FALSE); + +/* Handle dup of track related stuff */ +char *dupeName = handleDupOp(track, trackHash); +if (dupeName != NULL) + track = dupeName; +struct dupTrack *dupList = dupTrackListFromCart(cart); +char *dupWholeName = NULL; +boolean isDup = isDupTrack(track); +if (isDup) + { + dupWholeName = track; + track = dupTrackSkipToSourceName(track); + } + + +/* Add in duplicate tracks. */ +struct dupTrack *dup; +for (dup = dupList; dup != NULL; dup = dup->next) + { + struct trackDb *sourceTdb = hashFindVal(trackHash, dup->source); + if (sourceTdb != NULL) + { + struct trackDb *dupTdb = dupTdbFrom(sourceTdb, dup); + hashAdd(trackHash, dupTdb->track, dupTdb); + if (sourceTdb->parent != NULL) + { + struct trackDb *parent = sourceTdb->parent; + // Add to parent here depending on whether composite or something else? + if (tdbIsFolder(parent)) + { + refAdd(&parent->children, dupTdb); + } + else + { + slAddHead(&parent->subtracks, dupTdb); + dupTdb = NULL; /* We use it! */ + } + } + if (dupTdb != NULL) + slAddHead(&tdbList, dupTdb); + } + } + if (sameWord(track, WIKI_TRACK_TABLE)) tdb = trackDbForWikiTrack(); else if (sameWord(track, RULER_TRACK_NAME)) /* special handling -- it's not a full-fledged track */ tdb = trackDbForRuler(); else if (sameWord(track, OLIGO_MATCH_TRACK_NAME)) tdb = trackDbForOligoMatch(); else if (sameWord(track, CUTTERS_TRACK_NAME)) tdb = trackDbForPseudoTrack(CUTTERS_TRACK_NAME, CUTTERS_TRACK_LABEL, CUTTERS_TRACK_LONGLABEL, tvHide, TRUE); else if (isCustomTrack(track)) { ctList = customTracksParseCart(database, cart, NULL, NULL); for (ct = ctList; ct != NULL; ct = ct->next) { if (sameString(track, ct->tdb->track)) @@ -3608,30 +3712,39 @@ else if (isHubTrack(track)) { tdb = hubConnectAddHubForTrackAndFindTdb(database, track, &tdbList, trackHash); } else if (sameString(track, "hgPcrResult")) tdb = pcrResultFakeTdb(); else { tdb = tdbForTrack(database, track,&tdbList); } if (tdb == NULL) { errAbort("Can't find %s in track database %s chromosome %s", track, database, chromosome); } + +// Do little more dupe handling - make a tdb for dupe if any +if (isDup) + { + struct dupTrack *dup = dupTrackFindInList(dupList, dupWholeName); + tdb = dupTdbFrom(tdb, dup); + } + + if(cartOptionalString(cart, "ajax")) { // html is going to be used w/n a dialog in hgTracks.js so serve up stripped down html // still need CSP2 header for security printf("%s", getCspMetaHeader()); trackUi(tdb, tdbList, ct, TRUE); cartRemove(cart,"ajax"); jsInlineFinish(); } else { char title[1000]; if (tdb->parent) { safef(title, sizeof title,