From f68692c78f7b3a2483a835544e94baa5fabf507f Mon Sep 17 00:00:00 2001 From: imedina Date: Thu, 14 Mar 2024 02:26:04 +0000 Subject: [PATCH 01/10] Rename #broser tool to #variant-browser --- src/sites/iva/conf/config.js | 2 +- src/sites/iva/iva-app.js | 477 ++++++++++++++++++----------------- 2 files changed, 244 insertions(+), 235 deletions(-) diff --git a/src/sites/iva/conf/config.js b/src/sites/iva/conf/config.js index 0db8253f47..c83a8545e1 100644 --- a/src/sites/iva/conf/config.js +++ b/src/sites/iva/conf/config.js @@ -316,7 +316,7 @@ const SUITE = { }, menu: [ { - id: "browser", + id: "variant-browser", name: "Variant Browser", icon: "img/tools/icons/variant_browser.svg", visibility: "public", diff --git a/src/sites/iva/iva-app.js b/src/sites/iva/iva-app.js index ba9ff0d7f2..020fa8e873 100644 --- a/src/sites/iva/iva-app.js +++ b/src/sites/iva/iva-app.js @@ -187,7 +187,7 @@ class IvaApp extends LitElement { "gene", "transcript", "protein", - "browser", + "variant-browser", "job", "cat-browser", "cat-analysis", @@ -855,12 +855,19 @@ class IvaApp extends LitElement { break; } } + + // Change active study + ctx.opencgaSession = {...ctx.opencgaSession}; + ctx.requestUpdate(); } } switch (hashTool) { - case "#browser": - this.browserSearchQuery = Object.assign({}, this.browserSearchQuery); + case "#variant-browser": + // this.browserSearchQuery = Object.assign({}, this.browserSearchQuery); + if (feature) { + // feature === variant ID + } break; case "#protein": break; @@ -906,16 +913,19 @@ class IvaApp extends LitElement { ctx.tool = hashTool; } + // Parse query params const searchArr = window.location.hash.split("?"); if (searchArr.length > 1) { - const search = searchArr[1]; - arr = search.split("&"); const query = {}; - for (let i = 0; i < arr.length; i++) { - const split = arr[i].split("="); - query[split[0]] = split[1]; + const params = searchArr[1].split("&"); + for (const param of params) { + const keyValue = param.split("="); + query[keyValue[0]] = keyValue[1]; } + // debugger this.query = query; + // this.queries.variants = query; + // this.queries = {...this.queries}; } const componentName = this.tool.replace("#", ""); @@ -1014,7 +1024,7 @@ class IvaApp extends LitElement { // TODO: Opencga study will be shown later. For now variant browser is shown when the study changes // this.tool = "studyInformation"; - this.tool = "#browser"; + this.tool = "#variant-browser"; this.renderHashFragments(); // this.renderBreadcrumb(); } @@ -1027,21 +1037,21 @@ class IvaApp extends LitElement { } } - quickSearch(e) { - this.tool = "#browser"; - window.location.hash = "browser/" + this.opencgaSession.project.id + "/" + this.opencgaSession.study.id; - // this.browserQuery = {xref: e.detail.value}; - - this.browserSearchQuery = e.detail; - } + // quickSearch(e) { + // this.tool = "#variant-browser"; + // window.location.hash = "variant-browser/" + this.opencgaSession.project.id + "/" + this.opencgaSession.study.id; + // // this.browserQuery = {xref: e.detail.value}; + // + // this.browserSearchQuery = e.detail; + // } - quickFacetSearch(e) { - console.log("IVA-APP quickfacetsearch"); - this.tool = "#facet"; - window.location.hash = "facet/" + this.opencgaSession.project.id + "/" + this.opencgaSession.study.id; - // this.browserQuery = {xref: e.detail.value}; - this.browserSearchQuery = e.detail; - } + // quickFacetSearch(e) { + // console.log("IVA-APP quickfacetsearch"); + // this.tool = "#facet"; + // window.location.hash = "facet/" + this.opencgaSession.project.id + "/" + this.opencgaSession.study.id; + // // this.browserQuery = {xref: e.detail.value}; + // this.browserSearchQuery = e.detail; + // } onJobSelected(e) { this.jobSelected = e.detail.jobId; @@ -1054,10 +1064,10 @@ class IvaApp extends LitElement { } // TODO this should keep in sync the query object between variant-browser and variant-facet - onQueryChange(e) { - console.log("onQueryChange", e); - this.browserSearchQuery = {...e.detail.query}; - } + // onQueryChange(e) { + // console.log("onQueryChange", e); + // this.browserSearchQuery = {...e.detail.query}; + // } onQueryFilterSearch(e, source) { @@ -1283,11 +1293,11 @@ class IvaApp extends LitElement { ${ this.isCreatingSession ? html ` -
-
+ + ` : nothing } @@ -1351,76 +1361,75 @@ class IvaApp extends LitElement { ` : null} - ${this.config.enabledComponents.browser ? html` -
- - -
- ` : null} + ${this.config.enabledComponents["variant-browser"] ? html` +
+ + +
+ ` : null} ${this.config.enabledComponents["clinicalAnalysisPortal"] ? html` -
- - -
- ` : null} +
+ + +
+ ` : null} ${this.config.enabledComponents["rga"] ? html` -
- - -
- ` : null} +
+ + +
+ ` : null} ${this.config.enabledComponents["rd-interpreter"] ? html` -
- - -
- ` : null} +
+ + +
+ ` : null} ${this.config.enabledComponents["cancer-interpreter"] ? html` -
- - -
- ` : null} +
+ + +
+ ` : null} ${this.config.enabledComponents.beacon ? html`
@@ -1446,16 +1455,16 @@ class IvaApp extends LitElement { ` : null} ${this.config.enabledComponents.sample ? html` -
- - -
- ` : null} +
+ + +
+ ` : null} ${this.config.enabledComponents.panel ? html` @@ -1471,29 +1480,29 @@ class IvaApp extends LitElement { ` : null} ${this.config.enabledComponents.file ? html` -
- - -
- ` : null} +
+ + +
+ ` : null} ${this.config.enabledComponents["disease-panel"] ? html` -
- - -
- ` : null} +
+ + +
+ ` : null} ${this.config.enabledComponents["diseasePanelUpdate"] ? html`
@@ -1675,69 +1684,69 @@ class IvaApp extends LitElement { ` : null} ${this.config.enabledComponents.individual ? html` -
- - -
- ` : null} +
+ + +
+ ` : null} ${this.config.enabledComponents.family ? html` -
- - -
- ` : null} +
+ + +
+ ` : null} ${this.config.enabledComponents.cohort ? html` -
- - -
- ` : null} +
+ + +
+ ` : null} ${this.config.enabledComponents.clinicalAnalysis ? html` -
- - -
- ` : null} +
+ + +
+ ` : null} ${this.config.enabledComponents.job ? html` -
- - -
- ` : null} +
+ + +
+ ` : null} ${this.config.enabledComponents["cat-browser"] ? html`
- +
` : null} @@ -1943,13 +1952,13 @@ class IvaApp extends LitElement { ` : null} ${this.config.enabledComponents.account ? html` -
- - -
- ` : null} +
+ + +
+ ` : null} ${this.config.enabledComponents["file-manager"] ? html`
@@ -1965,16 +1974,16 @@ class IvaApp extends LitElement { ${this.config.enabledComponents["interpreter"] ? html` -
- - -
- ` : null} +
+ + +
+ ` : null} ${this.config.enabledComponents["alignment-index"] ? html` @@ -2019,56 +2028,56 @@ class IvaApp extends LitElement { ` : null} ${this.config.enabledComponents["catalog-admin"] ? html` -
- - -
- ` : null} +
+ + +
+ ` : null} ${this.config.enabledComponents["opencga-admin"] ? html` - -
- - -
- ` : null} + +
+ + +
+ ` : null} ${this.config.enabledComponents["study-admin"] ? html` -
- - -
- ` : null} +
+ + +
+ ` : null} - - + + ${this.config.enabledComponents["study-admin-iva"] ? html` -
- - -
- ` : null} +
+ + +
+ ` : null} ${this.config.enabledComponents["study-variant-admin"] ? html` -
- - -
- ` : null} +
+ + +
+ ` : null} ${this.config.enabledComponents["rest-api"] ? html` From ce8b95e445f04ea7f8a5dec03afd574453ee629b Mon Sep 17 00:00:00 2001 From: imedina Date: Mon, 18 Mar 2024 02:42:34 +0000 Subject: [PATCH 02/10] Many improvements and clean-ups in hashFragmentListener --- src/sites/iva/iva-app.js | 282 ++++++++++++++++++++++++++------------- 1 file changed, 187 insertions(+), 95 deletions(-) diff --git a/src/sites/iva/iva-app.js b/src/sites/iva/iva-app.js index 020fa8e873..5a09f70e37 100644 --- a/src/sites/iva/iva-app.js +++ b/src/sites/iva/iva-app.js @@ -38,7 +38,7 @@ import "../../webcomponents/variant/variant-beacon.js"; import "../../webcomponents/opencga/opencga-gene-view.js"; import "../../webcomponents/opencga/opencga-transcript-view.js"; import "../../webcomponents/opencga/opencga-protein-view.js"; -import "../../webcomponents/user/opencga-projects.js"; +// import "../../webcomponents/user/opencga-projects.js"; import "../../webcomponents/sample/sample-browser.js"; import "../../webcomponents/sample/sample-view.js"; import "../../webcomponents/sample/sample-variant-stats-browser.js"; @@ -263,10 +263,11 @@ class IvaApp extends LitElement { this.app = this.getActiveAppConfig(); // We need to listen to hash fragment changes to update the display and breadcrumb - const _this = this; - window.onhashchange = function (e) { + // const _this = this; + window.onhashchange = e => { // e.preventDefault(); - _this.hashFragmentListener(_this); + console.log("URL Hash changed: ", e); + this.hashFragmentListener(); }; // Remember the tool that was previously set @@ -407,16 +408,17 @@ class IvaApp extends LitElement { }); } - updated(changedProperties) { + update(changedProperties) { if (changedProperties.has("opencgaSession")) { this.opencgaSessionObserver(); } + super.update(changedProperties); } opencgaSessionObserver() { this.renderHashFragments(); this.queries = {}; - this.requestUpdate(); + // this.requestUpdate(); } #initStudiesSettings() { @@ -626,8 +628,6 @@ class IvaApp extends LitElement { onLogin(credentials) { // This creates a new authenticated opencga-session object - - // console.log("iva-app: roger I'm in", credentials); this.opencgaClient._config.token = credentials.detail.token; this._createOpenCGASession(); @@ -769,21 +769,19 @@ class IvaApp extends LitElement { $(target).closest("ul").closest("li").addClass("active"); } - if (UtilsNew.isNotUndefined(e)) { + if (e) { e.preventDefault(); // prevents the hash change to "#" and allows to manipulate the hash fragment as needed } - if (UtilsNew.isNotUndefined(target) && UtilsNew.isNotUndefined(target.attributes.href)) { - // $(e.target.attributes.href.value).show(); // get the href and use it find which div to show + if (target?.attributes?.href) { this.tool = target.attributes.href.value; - if (UtilsNew.isNotUndefinedOrNull(this._samplesPerTool)) { + if (this._samplesPerTool) { if (this._samplesPerTool.hasOwnProperty(this.tool.replace("#", ""))) { this.samples = this._samplesPerTool[this.tool.replace("#", "")]; } else { this.samples = []; } } - // this.renderBreadcrumb() } else { this.tool = "#home"; } @@ -791,21 +789,27 @@ class IvaApp extends LitElement { this.renderHashFragments(); } - renderHashFragments() { - console.log("renderHashFragments - DEBUG", this.tool); - let hashFrag = this.tool; - if (this.opencgaSession?.project?.alias) { + renderHashFragments(tool) { + console.log(`Update hash fragment URL with tool: '${tool ? `#${tool}` : this.tool}'`); + + // Keep global 'tool' param updated. + if (tool && this.tool !== `#${tool}`) { + this.tool = `#${tool}`; + } - hashFrag += "/" + this.opencgaSession.project.id; - if (UtilsNew.isNotUndefined(this.opencgaSession.study) && UtilsNew.isNotEmpty(this.opencgaSession.study.alias)) { - hashFrag += "/" + this.opencgaSession.study.id; + // Build hash fragment URL as: #tool/projectId/studyId + let newHashFragmentUrl = tool ? `#${tool}` : this.tool; + if (this.opencgaSession?.project) { + newHashFragmentUrl += "/" + this.opencgaSession.project.id; + if (this.opencgaSession.study) { + newHashFragmentUrl += "/" + this.opencgaSession.study.id; } } - if (window.location.hash === hashFrag || hashFrag === "#interpreter") { - this.hashFragmentListener(this); + if (window.location.hash === newHashFragmentUrl) { // || newHashFragmentUrl === "#interpreter" + this.hashFragmentListener(); } else { - window.location.hash = hashFrag; + window.location.hash = newHashFragmentUrl; } } @@ -817,7 +821,7 @@ class IvaApp extends LitElement { this.renderHashFragments(); } - hashFragmentListener(ctx) { + hashFragmentListenerOld(ctx) { console.log("hashFragmentListener - DEBUG", this.tool); // Hide all elements console.log("Hide all enabled elements"); @@ -944,8 +948,111 @@ class IvaApp extends LitElement { }, 1); } - onStudySelect(e, study, project) { - e.preventDefault(); // prevents the hash change to "#" and allows to manipulate the hash fragment as needed + hashFragmentListener() { + // 1. Hide all elements + console.log("hashFragmentListener - Hide all enabled elements"); + for (const element in this.config.enabledComponents) { + if (this.config.enabledComponents[element]) { + this.config.enabledComponents[element] = false; + } + } + + // 2. Parse hash fragment URL + const [hashTool, hashProject, hashStudy, featureId] = window.location.hash.split("/"); + + // 3. Processing the actions + // NOTE: remove this check: hashTool === "#interpreter" || + if (hashTool !== this.tool || hashProject !== this.opencgaSession?.project?.id || hashStudy !== this.opencgaSession?.study?.id) { + // 3.1. Update global 'tool' param + this.tool = hashTool; + + // 3.2. Update project/study + if (hashProject || hashStudy) { + this.changeActiveStudy(`${this.opencgaSession.user.id}@${hashProject}:${hashStudy}`); + } + + // 3.3. Parsing 'featureId' + switch (hashTool) { + case "#variant-browser": + // this.browserSearchQuery = Object.assign({}, this.browserSearchQuery); + if (featureId) { + this.browserSearchQuery = {id: featureId}; + // this.requestUpdate(); + } + + console.log("hashFragmentListener - 2.1"); + break; + case "#gene": + this.gene = featureId || null; + break; + case "#transcript": + if (featureId.startsWith("ENST")) { + this.transcript = featureId; + } else { + this.gene = featureId; + } + break; + case "#protein": + this.protein = featureId || null; + break; + case "#interpreter": + this.clinicalAnalysisId = featureId; + if (!this.clinicalAnalysisId) { + // Redirect to Case Portal when trying to access the interpreter without a valid Clinical Analysis ID + window.location.hash = `#clinicalAnalysisPortal/${this.opencgaSession.project.id}/${this.opencgaSession.study.id}`; + } + break; + case "#sampleVariantStatsBrowser": + case "#sampleCancerVariantStatsBrowser": + case "#sampleUpdate": + this.sampleId = featureId; + break; + case "#fileUpdate": + this.fileId = featureId; + break; + case "#individualUpdate": + this.individualId = featureId; + break; + case "#familyUpdate": + this.familyId = featureId; + break; + case "#study-admin": + // this.studyAdminFqn = arr[1]; + const arr = window.location.hash.split("/"); + this.changeActiveStudy(arr[1]); + break; + case "#diseasePanelUpdate": + this.diseasePanelId = featureId; + break; + } + + // 3.4 Update location.hash + window.location.hash = `${hashTool}/${this.opencgaSession.project.id}/${this.opencgaSession.study.id}`; + } + + // 4. Enable component + const componentName = this.tool.replace("#", ""); + if (this.config.enabledComponents.hasOwnProperty(componentName)) { + this.config.enabledComponents[componentName] = true; + } else { + // If the component does not exist, mark as custom page + this.config.enabledComponents["customPage"] = true; + } + + // 5. Force a page refresh + this.config = {...this.config}; + + // TODO quickfix to avoid hash browser scroll + $("body,html").animate({ + scrollTop: 0 + }, 1); + } + + onStudySelect(e, study) { + // Prevents the hash change to "#" and allows to manipulate the hash fragment as needed + e.preventDefault(); + + // Change study this.changeActiveStudy(study.fqn); } @@ -976,58 +1083,53 @@ class IvaApp extends LitElement { window.location.hash = "#clinicalAnalysisPortal"; } - // Refresh the session and update cellbase - this.opencgaSession = {...this.opencgaSession}; - this.settings = UtilsNew.objectClone(this.opencgaSession.study.attributes[SETTINGS_NAME].settings); + // Update CellBase and refresh the session. this.updateCellBaseClient(); + this.settings = UtilsNew.objectClone(this.opencgaSession.study.attributes[SETTINGS_NAME].settings); + this.opencgaSession = {...this.opencgaSession}; } else { // TODO Convert this into a user notification console.error("Study not found!"); } } + // This method updates 'cellbaseClient' object but DO NOT refresh opencgaSession. updateCellBaseClient() { - this.cellbaseClient = null; // Reset cellbase client + // 1. Reset CellBase client + this.cellbaseClient = null; - if (this.opencgaSession?.project && this.opencgaSession?.project?.cellbase?.url) { + // 2. Build new CellBase client using 'project' info. + if (this.opencgaSession?.project?.cellbase?.url) { this.cellbaseClient = new CellBaseClient({ host: this.opencgaSession.project.cellbase.url.replace(/\/$/, ""), version: this.opencgaSession.project.cellbase.version, species: this.opencgaSession.project.organism.scientificName, }); - } else { - // Josemi 20220216 NOTE: we keep this old way to be backward compatible with OpenCGA 2.1 - // But this should be removed in future releases - this.config.cellbase = null; - this.cellbaseClient = new CellBaseClient({ - host: this.config.cellbase?.host, - version: this.config.cellbase?.version, - species: "hsapiens", - }); - } - // This simplifies passing cellbaseCLient to all components - this.opencgaSession.cellbaseClient = this.cellbaseClient; - } - updateProject(e) { - this.project = this.projects.find(project => project.name === e.detail.project.name); - this.tool = "#project"; - this.renderHashFragments(); - // this.renderBreadcrumb(); + // 2.1 This simplifies passing cellbaseClient to all components + this.opencgaSession.cellbaseClient = this.cellbaseClient; + } } - updateStudy(e) { - if (UtilsNew.isNotUndefined(e.detail.project) && UtilsNew.isNotEmpty(e.detail.project.name)) { - this.project = e.detail.project; - } - this.study = this.project.studies.find(study => study.name === e.detail.study.name || study.alias === e.detail.study.alias); + // updateProject(e) { + // this.project = this.projects.find(project => project.name === e.detail.project.name); + // // this.tool = "#project"; + // this.renderHashFragments("project"); + // // this.renderBreadcrumb(); + // } - // TODO: Opencga study will be shown later. For now variant browser is shown when the study changes - // this.tool = "studyInformation"; - this.tool = "#variant-browser"; - this.renderHashFragments(); - // this.renderBreadcrumb(); - } + // updateStudy(e) { + // if (e.detail.project?.name) { + // this.project = e.detail.project; + // } + // this.study = this.project.studies.find(study => study.name === e.detail.study.name); + // + // // TODO: Opencga study will be shown later. For now variant browser is shown when the study changes + // // this.tool = "studyInformation"; + // // this.tool = "#variant-browser"; + // this.renderHashFragments("variant-browser"); + // // this.renderBreadcrumb(); + // } onSampleChange(e) { if (UtilsNew.isNotUndefinedOrNull(this.samples) && UtilsNew.isNotUndefinedOrNull(e.detail)) { @@ -1085,15 +1187,15 @@ class IvaApp extends LitElement { } /* Set the width of the side navigation to 250px */ - openNav() { - this.querySelector("#side-nav").style.width = "250px"; - console.log("open"); - } + // openNav() { + // this.querySelector("#side-nav").style.width = "250px"; + // console.log("open"); + // } /* Set the width of the side navigation to 0 */ - closeNav() { - this.querySelector("#side-nav").style.width = "0"; - } + // closeNav() { + // this.querySelector("#side-nav").style.width = "0"; + // } toggleSideBar(e) { e.preventDefault(); @@ -1224,7 +1326,7 @@ class IvaApp extends LitElement { } // No page found --> Render a not found error page (TODO) - return html`Not found :(`; + return html`Not found :-(`; } render() { @@ -1367,7 +1469,7 @@ class IvaApp extends LitElement { .opencgaSession="${this.opencgaSession}" .cellbaseClient="${this.cellbaseClient}" .reactomeClient="${this.reactomeClient}" - .query="${this.queries?.variant}" + .query="${this.browserSearchQuery}" .settings="${this.settings.VARIANT_BROWSER}" .consequenceTypes="${this.config.consequenceTypes}" .populationFrequencies="${this.config.populationFrequencies}" @@ -1444,16 +1546,6 @@ class IvaApp extends LitElement {
` : null} - ${this.config.enabledComponents.projects ? html` -
- - -
- ` : null} - ${this.config.enabledComponents.sample ? html`
- ${this.config.enabledComponents.gene ? html` -
- - -
- ` : null} - ${this.config.enabledComponents["sample-view"] ? html`
` : null} + ${this.config.enabledComponents.gene ? html` +
+ + +
+ ` : null} + ${this.config.enabledComponents.transcript ? html`
Date: Tue, 19 Mar 2024 02:35:09 +0000 Subject: [PATCH 03/10] First funcitonal implementation --- src/sites/iva/iva-app.js | 305 +++++-------------- src/webcomponents/variant/variant-browser.js | 7 +- 2 files changed, 88 insertions(+), 224 deletions(-) diff --git a/src/sites/iva/iva-app.js b/src/sites/iva/iva-app.js index 5a09f70e37..dd37845c73 100644 --- a/src/sites/iva/iva-app.js +++ b/src/sites/iva/iva-app.js @@ -262,8 +262,7 @@ class IvaApp extends LitElement { // Initially we load the SUIte config this.app = this.getActiveAppConfig(); - // We need to listen to hash fragment changes to update the display and breadcrumb - // const _this = this; + // We need to listen to hash fragment changes to update the URL window.onhashchange = e => { // e.preventDefault(); console.log("URL Hash changed: ", e); @@ -282,11 +281,6 @@ class IvaApp extends LitElement { // window.location.hash = this.tool; // } - // Other initialisations - // This manages the sample selected in each tool for updating the breadcrumb - this.samples = []; - this._samplesPerTool = {}; - // Notifications this.notificationManager = new NotificationManager({}); @@ -305,12 +299,12 @@ class IvaApp extends LitElement { // Show confirmation this.addEventListener(NotificationUtils.NOTIFY_CONFIRMATION, e => this.notificationManager.showConfirmation(e.detail)); - // TODO remove browserSearchQuery - this.browserSearchQuery = {}; // keeps track of the executedQueries transitioning from browser tool to facet tool - this.queries = []; + this.queries = {}; + // keeps track of status and version of the hosts (opencga and cellbase) this.host = {}; + globalThis.addEventListener("signingIn", e => { this.isCreatingSession = e.detail.value; this.requestUpdate(); @@ -333,7 +327,6 @@ class IvaApp extends LitElement { // Import server configuration from conf/server.json file (if exists) // See issue https://github.com/opencb/jsorolla/issues/425 UtilsNew.importJSONFile("conf/server.json").then(serverConf => { - // Initialize opencga configuration const opencgaHost = serverConf?.host || this.config.opencga.host; const opencgaVersion = serverConf?.version || this.config.opencga.version; @@ -376,7 +369,6 @@ class IvaApp extends LitElement { } // Initialise clients and create the session - // this.opencgaClientConfig.serverVersion = this.config.opencga.serverVersion; const sid = Cookies.get(opencgaCookiePrefix + "_sid"); const userId = Cookies.get(opencgaCookiePrefix + "_userId"); @@ -396,8 +388,6 @@ class IvaApp extends LitElement { }, }); - this.reactomeClient = new ReactomeClient(); - if (sid) { this.checkSessionActive(); this.intervalCheckSession = setInterval(this.checkSessionActive.bind(this), this.config.session.checkTime); @@ -417,7 +407,7 @@ class IvaApp extends LitElement { opencgaSessionObserver() { this.renderHashFragments(); - this.queries = {}; + // this.queries = {}; // this.requestUpdate(); } @@ -761,7 +751,9 @@ class IvaApp extends LitElement { } changeTool(e) { + // prevents the hash change to "#" and allows to manipulate the hash fragment as needed e.preventDefault(); + const target = e.currentTarget; $(".navbar-inverse ul > li", this).removeClass("active"); $(target).parent("li").addClass("active"); @@ -769,19 +761,8 @@ class IvaApp extends LitElement { $(target).closest("ul").closest("li").addClass("active"); } - if (e) { - e.preventDefault(); // prevents the hash change to "#" and allows to manipulate the hash fragment as needed - } - if (target?.attributes?.href) { this.tool = target.attributes.href.value; - if (this._samplesPerTool) { - if (this._samplesPerTool.hasOwnProperty(this.tool.replace("#", ""))) { - this.samples = this._samplesPerTool[this.tool.replace("#", "")]; - } else { - this.samples = []; - } - } } else { this.tool = "#home"; } @@ -821,136 +802,11 @@ class IvaApp extends LitElement { this.renderHashFragments(); } - hashFragmentListenerOld(ctx) { - console.log("hashFragmentListener - DEBUG", this.tool); - // Hide all elements - console.log("Hide all enabled elements"); - for (const element in this.config.enabledComponents) { - if (UtilsNew.isNotUndefined(this.config.enabledComponents[element])) { - this.config.enabledComponents[element] = false; - } - } - console.log("All enabled elements hidden"); - - let arr = window.location.hash.split("/"); - - // TODO evaluate refactor - const [hashTool, hashProject, hashStudy, feature] = arr; - - // Stopping the recursive call - if (hashTool === "#interpreter" || hashTool !== this.tool || hashProject !== this.opencgaSession?.project?.id || hashStudy !== this.opencgaSession?.study?.id) { - if (arr.length > 1) { - // Field 'project' is being observed, just in case Polymer triggers - // an unnecessary event we can check they are really different - if (ctx.opencgaSession?.project?.id !== hashProject) { - // eslint-disable-next-line no-param-reassign - ctx.opencgaSession.project = ctx.opencgaSession.projects?.find(project => project.id === hashProject); - } - if (ctx.opencgaSession?.study && arr.length > 2 && ctx.opencgaSession.study !== hashStudy) { - for (let i = 0; i < ctx.opencgaSession.projects.length; i++) { - if (ctx.opencgaSession.projects[i].name === ctx.opencgaSession.project.name || - ctx.opencgaSession.projects[i].id === ctx.opencgaSession.project.id) { - for (let j = 0; j < ctx.opencgaSession.projects[i].studies.length; j++) { - if (ctx.opencgaSession.projects[i].studies[j].name === hashStudy || ctx.opencgaSession.projects[i].studies[j].id === hashStudy) { - ctx.opencgaSession.study = ctx.opencgaSession.projects[i].studies[j]; - break; - } - } - break; - } - } - - // Change active study - ctx.opencgaSession = {...ctx.opencgaSession}; - ctx.requestUpdate(); - } - } - - switch (hashTool) { - case "#variant-browser": - // this.browserSearchQuery = Object.assign({}, this.browserSearchQuery); - if (feature) { - // feature === variant ID - } - break; - case "#protein": - break; - case "#interpreter": - this.clinicalAnalysisId = feature; - if (!this.clinicalAnalysisId) { - // Redirect to Case Portal when trying to access the interpreter without a valid Clinical Analysis ID - window.location.hash = `#clinicalAnalysisPortal/${this.opencgaSession.project.id}/${this.opencgaSession.study.id}`; - } - break; - case "#sampleVariantStatsBrowser": - case "#sampleCancerVariantStatsBrowser": - case "#sampleUpdate": - this.sampleId = feature; - break; - case "#fileUpdate": - this.fileId = feature; - break; - case "#individualUpdate": - this.individualId = feature; - break; - case "#familyUpdate": - this.familyId = feature; - break; - case "#study-admin": - // this.studyAdminFqn = arr[1]; - this.changeActiveStudy(arr[1]); - break; - case "#diseasePanelUpdate": - this.diseasePanelId = feature; - break; - } - - if (UtilsNew.isNotEmpty(feature)) { - if (hashTool === "#protein") { - ctx.protein = feature; - } else if (feature.startsWith("ENST")) { - ctx.transcript = feature; - } else { - ctx.gene = feature; - } - } - ctx.tool = hashTool; - } - - // Parse query params - const searchArr = window.location.hash.split("?"); - if (searchArr.length > 1) { - const query = {}; - const params = searchArr[1].split("&"); - for (const param of params) { - const keyValue = param.split("="); - query[keyValue[0]] = keyValue[1]; - } - // debugger - this.query = query; - // this.queries.variants = query; - // this.queries = {...this.queries}; - } - - const componentName = this.tool.replace("#", ""); - if (UtilsNew.isNotUndefined(this.config.enabledComponents[componentName])) { - this.config.enabledComponents[componentName] = true; - } else { - // If the component does not exist, mark as custom page - this.config.enabledComponents["customPage"] = true; - } - console.log("Force update in hasFragmentListener"); - this.config = {...this.config}; - - // TODO quickfix to avoid hash browser scroll - $("body,html").animate({ - scrollTop: 0 - }, 1); - } hashFragmentListener() { - // 1. Hide all elements console.log("hashFragmentListener - Hide all enabled elements"); + + // 1. Hide all elements for (const element in this.config.enabledComponents) { if (this.config.enabledComponents[element]) { this.config.enabledComponents[element] = false; @@ -958,45 +814,59 @@ class IvaApp extends LitElement { } // 2. Parse hash fragment URL - const [hashTool, hashProject, hashStudy, featureId] = window.location.hash.split("/"); + const [hashTool, hashProject, hashStudy, hashQuery] = window.location.hash.split("/"); // 3. Processing the actions // NOTE: remove this check: hashTool === "#interpreter" || - if (hashTool !== this.tool || hashProject !== this.opencgaSession?.project?.id || hashStudy !== this.opencgaSession?.study?.id) { - // 3.1. Update global 'tool' param + if (hashTool !== this.tool) { this.tool = hashTool; + } - // 3.2. Update project/study - if (hashProject || hashStudy) { - this.changeActiveStudy(`${this.opencgaSession.user.id}@${hashProject}:${hashStudy}`); + // 4. Parse project and study + if (hashProject !== this.opencgaSession?.project?.id || hashStudy !== this.opencgaSession?.study?.id) { + this.changeActiveStudy(`${this.opencgaSession.user.id}@${hashProject}:${hashStudy}`); + } + + // 5. Update location.hash + window.location.hash = `${hashTool}/${this.opencgaSession.project.id}/${this.opencgaSession.study.id}`; + + // 6. Parse query fragment in 'featureId' + if (hashTool && hashQuery) { + const componentId = hashTool.replace("#", ""); + + let query = {}; + // Check if key=value list exist + if (hashQuery.includes("=")) { + const filters = hashQuery.split("&"); + for (const filter of filters) { + const [key, value] = filter.split("="); + query[key] = value; + } + } else { + // Default query filter is 'id' + query = {id: hashQuery}; } - // 3.3. Parsing 'featureId' switch (hashTool) { case "#variant-browser": - // this.browserSearchQuery = Object.assign({}, this.browserSearchQuery); - if (featureId) { - this.browserSearchQuery = {id: featureId}; - // this.requestUpdate(); - } - - console.log("hashFragmentListener - 2.1"); + case "#sample": + this.queries[componentId] = query; break; case "#gene": - this.gene = featureId || null; + this.gene = hashQuery || null; break; case "#transcript": - if (featureId.startsWith("ENST")) { - this.transcript = featureId; + if (hashQuery.startsWith("ENST")) { + this.transcript = hashQuery; } else { - this.gene = featureId; + this.gene = hashQuery; } break; case "#protein": - this.protein = featureId || null; + this.protein = hashQuery || null; break; case "#interpreter": - this.clinicalAnalysisId = featureId; + this.clinicalAnalysisId = hashQuery; if (!this.clinicalAnalysisId) { // Redirect to Case Portal when trying to access the interpreter without a valid Clinical Analysis ID window.location.hash = `#clinicalAnalysisPortal/${this.opencgaSession.project.id}/${this.opencgaSession.study.id}`; @@ -1005,16 +875,16 @@ class IvaApp extends LitElement { case "#sampleVariantStatsBrowser": case "#sampleCancerVariantStatsBrowser": case "#sampleUpdate": - this.sampleId = featureId; + this.sampleId = hashQuery; break; case "#fileUpdate": - this.fileId = featureId; + this.fileId = hashQuery; break; case "#individualUpdate": - this.individualId = featureId; + this.individualId = hashQuery; break; case "#familyUpdate": - this.familyId = featureId; + this.familyId = hashQuery; break; case "#study-admin": // this.studyAdminFqn = arr[1]; @@ -1022,15 +892,14 @@ class IvaApp extends LitElement { this.changeActiveStudy(arr[1]); break; case "#diseasePanelUpdate": - this.diseasePanelId = featureId; + this.diseasePanelId = hashQuery; break; } - - // 3.4 Update location.hash - window.location.hash = `${hashTool}/${this.opencgaSession.project.id}/${this.opencgaSession.study.id}`; + // this.requestUpdate(); } - // 4. Enable component + // 7. Enable component + console.log("hashFragmentListener - Show enabled element " + this.tool); const componentName = this.tool.replace("#", ""); if (this.config.enabledComponents.hasOwnProperty(componentName)) { this.config.enabledComponents[componentName] = true; @@ -1039,8 +908,7 @@ class IvaApp extends LitElement { this.config.enabledComponents["customPage"] = true; } - // 5. Force a page refresh - this.config = {...this.config}; + this.requestUpdate(); // TODO quickfix to avoid hash browser scroll $("body,html").animate({ @@ -1083,6 +951,8 @@ class IvaApp extends LitElement { window.location.hash = "#clinicalAnalysisPortal"; } + this.queries = {}; + // Update CellBase and refresh the session. this.updateCellBaseClient(); this.settings = UtilsNew.objectClone(this.opencgaSession.study.attributes[SETTINGS_NAME].settings); @@ -1106,7 +976,7 @@ class IvaApp extends LitElement { species: this.opencgaSession.project.organism.scientificName, }); - // 2.1 This simplifies passing cellbaseClient to all components + // 2.1 This simplifies passing 'cellbaseClient' to all components this.opencgaSession.cellbaseClient = this.cellbaseClient; } } @@ -1132,11 +1002,11 @@ class IvaApp extends LitElement { // } onSampleChange(e) { - if (UtilsNew.isNotUndefinedOrNull(this.samples) && UtilsNew.isNotUndefinedOrNull(e.detail)) { - this.samples = e.detail.samples; - this._samplesPerTool[this.tool.replace("#", "")] = this.samples; - // this.renderBreadcrumb(); - } + // if (UtilsNew.isNotUndefinedOrNull(this.samples) && UtilsNew.isNotUndefinedOrNull(e.detail)) { + // this.samples = e.detail.samples; + // this._samplesPerTool[this.tool.replace("#", "")] = this.samples; + // this.renderBreadcrumb(); + // } } // quickSearch(e) { @@ -1166,10 +1036,12 @@ class IvaApp extends LitElement { } // TODO this should keep in sync the query object between variant-browser and variant-facet - // onQueryChange(e) { - // console.log("onQueryChange", e); - // this.browserSearchQuery = {...e.detail.query}; - // } + onQueryChange(e) { + debugger + console.warn("onQueryChange", e); + this.browserSearchQuery = {...e.detail.query}; + // this.browserSearchQuery = {}; + } onQueryFilterSearch(e, source) { @@ -1177,26 +1049,14 @@ class IvaApp extends LitElement { // TODO fix active-filters const q = e.detail.query ? {...e.detail.query} : {...e.detail}; this.queries[source] = {...q}; - this.queries = {...this.queries}; - // console.log("this.queries",this.queries); - this.requestUpdate(); + // this.queries = {...this.queries}; + // this.requestUpdate(); } onSelectClinicalAnalysis(e) { this.clinicalAnalysis = e.detail.clinicalAnalysis; } - /* Set the width of the side navigation to 250px */ - // openNav() { - // this.querySelector("#side-nav").style.width = "250px"; - // console.log("open"); - // } - - /* Set the width of the side navigation to 0 */ - // closeNav() { - // this.querySelector("#side-nav").style.width = "0"; - // } - toggleSideBar(e) { e.preventDefault(); // const sidenav = this.querySelector("#side-nav"); @@ -1244,17 +1104,17 @@ class IvaApp extends LitElement { return !!this?.opencgaSession?.token; } - createAboutLink(link, button) { - const url = link.url ? `${link.url}` : `#${link.id}`; - const iconHtml = link.icon ? html`` : null; - if (link.url) { - return html` - ${iconHtml} ${link.name}`; - } else { - return html` - ${iconHtml} ${link.name}`; - } - } + // createAboutLink(link, button) { + // const url = link.url ? `${link.url}` : `#${link.id}`; + // const iconHtml = link.icon ? html`` : null; + // if (link.url) { + // return html` + // ${iconHtml} ${link.name}`; + // } else { + // return html` + // ${iconHtml} ${link.name}`; + // } + // } onSessionUpdateRequest() { this._createOpenCGASession(); @@ -1469,15 +1329,16 @@ class IvaApp extends LitElement { .opencgaSession="${this.opencgaSession}" .cellbaseClient="${this.cellbaseClient}" .reactomeClient="${this.reactomeClient}" - .query="${this.browserSearchQuery}" + .query="${this.queries["variant-browser"]}" .settings="${this.settings.VARIANT_BROWSER}" .consequenceTypes="${this.config.consequenceTypes}" .populationFrequencies="${this.config.populationFrequencies}" .proteinSubstitutionScores="${this.config.proteinSubstitutionScores}" @onGene="${this.geneSelected}" @onSamplechange="${this.onSampleChange}" - @querySearch="${e => this.onQueryFilterSearch(e, "variant")}" - @activeFilterChange="${e => this.onQueryFilterSearch(e, "variant")}"> + @querySearch="${e => this.onQueryFilterSearch(e, "variant-browser")}" + onqueryChange="${e => this.onQueryChange(e, "variant")}" + @activeFilterChange="${e => this.onQueryFilterSearch(e, "variant-browser")}">
` : null} diff --git a/src/webcomponents/variant/variant-browser.js b/src/webcomponents/variant/variant-browser.js index e6fa4914ad..7b22becfef 100644 --- a/src/webcomponents/variant/variant-browser.js +++ b/src/webcomponents/variant/variant-browser.js @@ -171,10 +171,11 @@ export default class VariantBrowser extends LitElement { } queryObserver() { - if (this?.opencgaSession?.study?.fqn) { + if (this.opencgaSession?.study?.fqn) { + debugger // NOTE UtilsNew.objectCompare avoid repeating remote requests. if (!UtilsNew.objectCompare(this.query, this._query)) { - this._query = this.query; + this._query = {...this.query}; if (this.query) { this.preparedQuery = {study: this.opencgaSession.study.fqn, ...this.query}; this.executedQuery = {study: this.opencgaSession.study.fqn, ...this.query}; @@ -216,6 +217,7 @@ export default class VariantBrowser extends LitElement { async onRun() { // NOTE notifySearch() triggers this chain: notifySearch -> onQueryFilterSearch() on iva-app.js -> this.queries updated -> queryObserver() in variant-browser // queryObserver() here stops the repetition of the remote request by checking if it has changed + debugger this.query = {...this.preparedQuery}; // updates this.queries in iva-app this.notifySearch(this.preparedQuery); @@ -251,6 +253,7 @@ export default class VariantBrowser extends LitElement { } onActiveFilterChange(e) { + debugger VariantUtils.validateQuery(e.detail); this.query = {study: this.opencgaSession.study.fqn, ...e.detail}; this.notifySearch(this.query); From 07a3d6ce725061712d369cd0b7d962c075fbfaeb Mon Sep 17 00:00:00 2001 From: Josemi Date: Thu, 28 Mar 2024 18:50:02 +0100 Subject: [PATCH 04/10] wc: fix listening for changed properties in variant-browser TASK-5864 --- src/webcomponents/variant/variant-browser.js | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/src/webcomponents/variant/variant-browser.js b/src/webcomponents/variant/variant-browser.js index 7b22becfef..3922325c08 100644 --- a/src/webcomponents/variant/variant-browser.js +++ b/src/webcomponents/variant/variant-browser.js @@ -16,9 +16,7 @@ import {LitElement, html} from "lit"; import UtilsNew from "../../core/utils-new.js"; -import OpencgaCatalogUtils from "../../core/clients/opencga/opencga-catalog-utils.js"; import VariantUtils from "./variant-utils.js"; -import NotificationUtils from "../commons/utils/notification-utils.js"; import LitUtils from "../commons/utils/lit-utils.js"; import "../commons/tool-header.js"; import "./variant-browser-filter.js"; @@ -112,7 +110,7 @@ export default class VariantBrowser extends LitElement { this.opencgaSessionObserver(); this.settingsObserver(); } - if (changedProperties.has("query")) { + if (changedProperties.has("query") || changedProperties.has("opencgaSession")) { this.queryObserver(); } if (changedProperties.has("selectedFacet")) { @@ -145,14 +143,11 @@ export default class VariantBrowser extends LitElement { ...this._config.filter?.result?.grid, ...this.opencgaSession?.user?.configs?.IVA?.settings?.[this.COMPONENT_ID]?.grid, }); - - this.requestUpdate(); } opencgaSessionObserver() { if (this?.opencgaSession?.study?.fqn) { - this.checkProjects = true; - this.query = {study: this.opencgaSession.study.fqn}; + this._query = {}; // study: this.opencgaSession.study.fqn}; // TODO FIXME /** temp fix this.onRun(): when you switch study this.facetQuery contains the old study when you perform a new Aggregation query. @@ -161,18 +156,11 @@ export default class VariantBrowser extends LitElement { this.preparedQuery = {study: this.opencgaSession.study.fqn}; this.facetQuery = null; this.preparedFacetQueryFormatted = null; - // this.requestUpdate(); - // this.onRun(); - - // this.requestUpdate().then(() => $(".bootstrap-select", this).selectpicker()); - } else { - this.checkProjects = false; } } queryObserver() { if (this.opencgaSession?.study?.fqn) { - debugger // NOTE UtilsNew.objectCompare avoid repeating remote requests. if (!UtilsNew.objectCompare(this.query, this._query)) { this._query = {...this.query}; @@ -217,7 +205,6 @@ export default class VariantBrowser extends LitElement { async onRun() { // NOTE notifySearch() triggers this chain: notifySearch -> onQueryFilterSearch() on iva-app.js -> this.queries updated -> queryObserver() in variant-browser // queryObserver() here stops the repetition of the remote request by checking if it has changed - debugger this.query = {...this.preparedQuery}; // updates this.queries in iva-app this.notifySearch(this.preparedQuery); @@ -253,7 +240,6 @@ export default class VariantBrowser extends LitElement { } onActiveFilterChange(e) { - debugger VariantUtils.validateQuery(e.detail); this.query = {study: this.opencgaSession.study.fqn, ...e.detail}; this.notifySearch(this.query); From 80578bf96efda6f0699ad5fc75fbcc3149fde600 Mon Sep 17 00:00:00 2001 From: imedina Date: Sat, 30 Mar 2024 02:03:31 +0000 Subject: [PATCH 05/10] Code cleanups --- src/webcomponents/variant/variant-browser.js | 36 +++++--------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/src/webcomponents/variant/variant-browser.js b/src/webcomponents/variant/variant-browser.js index 3922325c08..d620a9fb43 100644 --- a/src/webcomponents/variant/variant-browser.js +++ b/src/webcomponents/variant/variant-browser.js @@ -85,8 +85,8 @@ export default class VariantBrowser extends LitElement { this.COMPONENT_ID = "variant-browser"; this._prefix = UtilsNew.randomString(8); - this.results = []; - this._showInitMessage = true; + // this.results = []; + // this._showInitMessage = true; this.searchActive = true; this.facetActive = true; @@ -147,13 +147,13 @@ export default class VariantBrowser extends LitElement { opencgaSessionObserver() { if (this?.opencgaSession?.study?.fqn) { - this._query = {}; // study: this.opencgaSession.study.fqn}; + this._query = {study: this.opencgaSession.study.fqn}; + this.preparedQuery = {study: this.opencgaSession.study.fqn}; // TODO FIXME /** temp fix this.onRun(): when you switch study this.facetQuery contains the old study when you perform a new Aggregation query. * As a consequence, we need to update preparedQuery as this.onRun() uses it (without it the old study is in query in table result as well) */ - this.preparedQuery = {study: this.opencgaSession.study.fqn}; this.facetQuery = null; this.preparedFacetQueryFormatted = null; } @@ -163,17 +163,12 @@ export default class VariantBrowser extends LitElement { if (this.opencgaSession?.study?.fqn) { // NOTE UtilsNew.objectCompare avoid repeating remote requests. if (!UtilsNew.objectCompare(this.query, this._query)) { - this._query = {...this.query}; - if (this.query) { - this.preparedQuery = {study: this.opencgaSession.study.fqn, ...this.query}; - this.executedQuery = {study: this.opencgaSession.study.fqn, ...this.query}; - } else { - this.preparedQuery = {study: this.opencgaSession.study.fqn}; - this.executedQuery = {study: this.opencgaSession.study.fqn}; - } - // onServerFilterChange() in opencga-active-filters fires an activeFilterChange event when the Filter dropdown is used + this._query = {study: this.opencgaSession.study.fqn, ...this.query}; + this.preparedQuery = {...this.query}; + this.executedQuery = {...this.query}; + LitUtils.dispatchCustomEvent(this, "queryChange", undefined, this.preparedQuery); - this.detail = {}; + // this.detail = {}; this.searchActive = false; // Disable search button } } @@ -247,7 +242,6 @@ export default class VariantBrowser extends LitElement { } onActiveFilterClear() { - // console.log("onActiveFilterClear"); this.query = {study: this.opencgaSession.study.fqn}; this.preparedQuery = {...this.query}; this.notifySearch(this.query); @@ -255,7 +249,6 @@ export default class VariantBrowser extends LitElement { } onFacetQueryChange(e) { - // console.log("onFacetQueryChange"); this.preparedFacetQueryFormatted = e.detail.value; this.requestUpdate(); } @@ -263,7 +256,6 @@ export default class VariantBrowser extends LitElement { onActiveFacetChange(e) { this.selectedFacet = {...e.detail}; this.preparedFacetQueryFormatted = {...e.detail}; - // this.onRun(); this.facetQueryBuilder(); this.requestUpdate(); } @@ -279,11 +271,6 @@ export default class VariantBrowser extends LitElement { this.requestUpdate(); } - onClickRow(e) { - this.detail = {...this.detail, [e.detail.resource]: e.detail.data}; - this.requestUpdate(); - } - onSampleChange(e) { this.samples = e.detail.samples; LitUtils.dispatchCustomEvent(this, "sampleChange", undefined, { @@ -728,11 +715,6 @@ export default class VariantBrowser extends LitElement { `, } - // TODO Think about Neeworks - // { - // id: "network", - // title: "Reactome Pathways" - // }, ] } }, From c2c6efe38bc6e309758657bf0f463d7eaca5dcbb Mon Sep 17 00:00:00 2001 From: imedina Date: Fri, 12 Apr 2024 01:22:30 +0100 Subject: [PATCH 06/10] Several fixes in the Variant link routing --- src/core/clients/opencga/opencga-client.js | 33 ++------ src/sites/iva/iva-app.js | 96 ++++++++++++++++------ 2 files changed, 77 insertions(+), 52 deletions(-) diff --git a/src/core/clients/opencga/opencga-client.js b/src/core/clients/opencga/opencga-client.js index 6bf521b1cb..7e53f92442 100644 --- a/src/core/clients/opencga/opencga-client.js +++ b/src/core/clients/opencga/opencga-client.js @@ -357,7 +357,8 @@ export class OpenCGAClient { // check that a session exists // TODO should we check the session has not expired? if (_this._config.token) { - _this.users().info(_this._config.userId) + _this.users() + .info(_this._config.userId) .then(async response => { console.log("Creating session"); const session = { @@ -370,7 +371,6 @@ export class OpenCGAClient { session.server = { host: _this._config.host, version: _this._config.version, - // serverVersion: _this._config.serverVersion, }; session.opencgaClient = _this; const userConfig = await this.updateUserConfig("IVA", { @@ -392,7 +392,6 @@ export class OpenCGAClient { .search({limit: 100}) .then(async function (response) { try { - // session.projects = response.responses[0].results; for (const project of response.responses[0].results) { const projectIndex = session.projects.findIndex(proj => proj.fqn === project.fqn); if (projectIndex < 0) { @@ -425,29 +424,12 @@ export class OpenCGAClient { // FIXME line above should check cohort.internal instead // .filter(cohort => cohort.internal.index?.status === "READY"); - // Check if lastStudy form User Configuration matches - if (session.user?.configs?.IVA?.lastStudy === study.fqn) { - session.project = project; - session.study = study; - } - // Keep track of the studies to fetch Disease Panels studies.push(study.fqn); } } } - // If the user doesn't have his own default study then we select the first project and study as default - if (!session.project && !session.study) { - for (const project of session.projects) { - if (project.studies?.length > 0) { - session.project = project; - session.study = project.studies[0]; - break; - } - } - } - // Fetch the CellBase sources for each Project const cellbaseSourcesPromises = []; const indexesMap = []; @@ -482,11 +464,12 @@ export class OpenCGAClient { console.log("Fetching disease panels"); const panelPromises = []; for (const study of studies) { - const promise = _this.panels().search({ - study: study, - limit: 2000, - include: "id,name,stats,source,genes.id,genes.name,genes.modeOfInheritance,genes.confidence,regions.id" - }); + const promise = _this.panels() + .search({ + study: study, + limit: 1000, + include: "id,name,stats,source,genes.id,genes.name,genes.modeOfInheritance,genes.confidence,regions.id" + }); panelPromises.push(promise); } const panelResponses = await Promise.all(panelPromises); diff --git a/src/sites/iva/iva-app.js b/src/sites/iva/iva-app.js index ae9ab97993..fc849d34af 100644 --- a/src/sites/iva/iva-app.js +++ b/src/sites/iva/iva-app.js @@ -481,17 +481,14 @@ class IvaApp extends LitElement { return; } this.isCreatingSession = true; - console.log("Init creating opencgasession"); + console.log("Init creating opencgaSession ..."); this.requestUpdate(); await this.updateComplete; this.opencgaClient.createSession() .then(response => { - const _response = response; - console.log("_createOpenCGASession", response); - + // 1. Check if project array has been defined in the config.js // This is only valid when public IVA/OpenCGA installations - // Check if project array has been defined in the config.js - if (UtilsNew.isNotEmptyArray(this.config.opencga.projects)) { + if (this.config.opencga.projects?.length > 0) { // We store the project and study ids the user needs to visualise (defined in the config.js) const configProjects = {}; for (let i = 0; i < this.config.opencga.projects.length; i++) { @@ -524,16 +521,58 @@ class IvaApp extends LitElement { } // TODO we must query projects/info URL to get the whole object - _response.projects = activeProjects || []; + response.projects = activeProjects || []; if (UtilsNew.isNotEmptyArray(response.projects[0].studies)) { - _response.project = response.projects[0]; - _response.study = response.projects[0].studies[0]; + response.project = response.projects[0]; + response.study = response.projects[0].studies[0]; + } + } + + // 2. Set the default active project and study. + // 2.1 Check if the user has set a project/study in the URL + const [hashTool, hashProject, hashStudy, hashQuery] = window.location.hash.split("/"); + if (hashProject && hashStudy) { + const project = response.projects.find(p => p.id === hashProject); + const study = project.studies.find(s => s.id === hashStudy); + if (project && study) { + console.log("Setting active project and study from hash"); + response.project = project; + response.study = study; + } else { + console.error(`Project '${hashProject}' and Study '${hashStudy}' not found`); + } + } else { + // 2.2 Check if lastStudy form User Configuration matches + for (const project of response.projects) { + if (project.studies?.length > 0) { + for (const study of project.studies) { + if (response.user?.configs?.IVA?.lastStudy === study.fqn) { + response.project = project; + response.study = study; + break; + } + } + } + } + + // 2.3 if not 'lastStudy' found then set the first project and study. + // If the user doesn't have his own default study then we select the first project and study as default + if (!response.project && !response.study) { + for (const project of response.projects) { + if (project.studies?.length > 0) { + response.project = project; + response.study = project.studies[0]; + break; + } + } } } - // this forces the observer to be executed. - if (UtilsNew.isNotEmptyArray(response.projects) && response.projects.some(p => UtilsNew.isNotEmptyArray(p.studies))) { + + // 3. Update data structures and refresh. + // This forces the observer to be executed. + if (response.projects?.length > 0 && response.projects.some(p => UtilsNew.isNotEmptyArray(p.studies))) { this.opencgaSession = { - ..._response, + ...response, ivaDefaultSettings: { version: this.version, settings: UtilsNew.objectClone(this.DEFAULT_TOOL_SETTINGS), @@ -548,11 +587,10 @@ class IvaApp extends LitElement { this.config = {...this.config}; } else { this.opencgaSession = { - ..._response, + ...response, }; this.config = {...this.config}; } - }) .catch(e => { console.error(e); @@ -779,19 +817,23 @@ class IvaApp extends LitElement { } // Build hash fragment URL as: #tool/projectId/studyId - let newHashFragmentUrl = tool ? `#${tool}` : this.tool; - if (this.opencgaSession?.project) { - newHashFragmentUrl += "/" + this.opencgaSession.project.id; - if (this.opencgaSession.study) { - newHashFragmentUrl += "/" + this.opencgaSession.study.id; - } - } + // let newHashFragmentUrl = tool ? `#${tool}` : this.tool; + // if (this.opencgaSession?.project) { + // newHashFragmentUrl += "/" + this.opencgaSession.project.id; + // if (this.opencgaSession.study) { + // newHashFragmentUrl += "/" + this.opencgaSession.study.id; + // } + // } - if (window.location.hash === newHashFragmentUrl) { // || newHashFragmentUrl === "#interpreter" - this.hashFragmentListener(); - } else { - window.location.hash = newHashFragmentUrl; - } + // if (window.location.hash === newHashFragmentUrl) { // || newHashFragmentUrl === "#interpreter" + // this.hashFragmentListener(); + // } else { + // window.location.hash = newHashFragmentUrl; + // } + // if (window.location.hash !== newHashFragmentUrl) { + // window.location.hash = newHashFragmentUrl; + // } + this.hashFragmentListener(); } route(e) { @@ -828,7 +870,7 @@ class IvaApp extends LitElement { } // 5. Update location.hash - window.location.hash = `${hashTool}/${this.opencgaSession.project.id}/${this.opencgaSession.study.id}`; + window.location.hash = `${hashTool || "home"}/${this.opencgaSession.project.id}/${this.opencgaSession.study.id}`; // 6. Parse query fragment in 'featureId' if (hashTool && hashQuery) { From 4f2c3472a15aac3d07f8b1102c6e8783fde45e4d Mon Sep 17 00:00:00 2001 From: imedina Date: Fri, 12 Apr 2024 10:57:45 +0100 Subject: [PATCH 07/10] Fix change study --- src/sites/iva/conf/config.js | 4 +- src/sites/iva/iva-app.js | 78 ++++++++++++++++++++---------------- 2 files changed, 45 insertions(+), 37 deletions(-) diff --git a/src/sites/iva/conf/config.js b/src/sites/iva/conf/config.js index 4d5c5e92e3..f6671d9c32 100644 --- a/src/sites/iva/conf/config.js +++ b/src/sites/iva/conf/config.js @@ -32,10 +32,10 @@ const hosts = [ ]; const opencga = { - host: hosts[2].url, + host: hosts[1].url, version: "v2", cookie: { - prefix: "iva-" + hosts[2].id, + prefix: "iva-" + hosts[1].id, secure: true, }, sso: { diff --git a/src/sites/iva/iva-app.js b/src/sites/iva/iva-app.js index fc849d34af..d32d710a3c 100644 --- a/src/sites/iva/iva-app.js +++ b/src/sites/iva/iva-app.js @@ -406,7 +406,8 @@ class IvaApp extends LitElement { } opencgaSessionObserver() { - this.renderHashFragments(); + // this.renderHashFragments(); + this.hashFragmentListener(); // this.queries = {}; // this.requestUpdate(); } @@ -805,43 +806,41 @@ class IvaApp extends LitElement { this.tool = "#home"; } - this.renderHashFragments(); - } - - renderHashFragments(tool) { - console.log(`Update hash fragment URL with tool: '${tool ? `#${tool}` : this.tool}'`); - - // Keep global 'tool' param updated. - if (tool && this.tool !== `#${tool}`) { - this.tool = `#${tool}`; - } - - // Build hash fragment URL as: #tool/projectId/studyId - // let newHashFragmentUrl = tool ? `#${tool}` : this.tool; - // if (this.opencgaSession?.project) { - // newHashFragmentUrl += "/" + this.opencgaSession.project.id; - // if (this.opencgaSession.study) { - // newHashFragmentUrl += "/" + this.opencgaSession.study.id; - // } - // } - - // if (window.location.hash === newHashFragmentUrl) { // || newHashFragmentUrl === "#interpreter" - // this.hashFragmentListener(); - // } else { - // window.location.hash = newHashFragmentUrl; - // } - // if (window.location.hash !== newHashFragmentUrl) { - // window.location.hash = newHashFragmentUrl; - // } + // this.renderHashFragments(); this.hashFragmentListener(); } + // renderHashFragments(tool) { + // console.log(`Update hash fragment URL with tool: '${tool ? `#${tool}` : this.tool}'`); + // + // // Keep global 'tool' param updated. + // if (tool && this.tool !== `#${tool}`) { + // this.tool = `#${tool}`; + // } + // + // // Build hash fragment URL as: #tool/projectId/studyId + // let newHashFragmentUrl = tool ? `#${tool}` : this.tool; + // if (this.opencgaSession?.project) { + // newHashFragmentUrl += "/" + this.opencgaSession.project.id; + // if (this.opencgaSession.study) { + // newHashFragmentUrl += "/" + this.opencgaSession.study.id; + // } + // } + // + // if (window.location.hash === newHashFragmentUrl) { // || newHashFragmentUrl === "#interpreter" + // this.hashFragmentListener(); + // } else { + // window.location.hash = newHashFragmentUrl; + // } + // } + route(e) { this.tool = e.detail.hash; if (e.detail?.resource) { this.queries = {...this.queries, [e.detail.resource]: e.detail?.query}; } - this.renderHashFragments(); + // this.renderHashFragments(); + this.hashFragmentListener(); } @@ -985,14 +984,23 @@ class IvaApp extends LitElement { } if (studyFound) { - // Update the lastStudy in config iff has changed - this.opencgaClient.updateUserConfig("IVA", {...this.opencgaSession.user.configs["IVA"], lastStudy: studyFqn}); + // 1. Update the lastStudy in config iff has changed + this.opencgaClient.updateUserConfig("IVA", { + ...this.opencgaSession.user.configs["IVA"], + lastStudy: studyFqn + }); - // This is a terrible hack to exit interpreter when we change the current study - if (this.tool === "#interpreter") { - window.location.hash = "#clinicalAnalysisPortal"; + // 2. Set the new Hash URL + let newHashFragmentUrl = this.tool !== "#interpreter" ? this.tool : "#clinicalAnalysisPortal"; + if (this.opencgaSession?.project) { + newHashFragmentUrl += "/" + this.opencgaSession.project.id; + if (this.opencgaSession.study) { + newHashFragmentUrl += "/" + this.opencgaSession.study.id; + } } + window.location.hash = newHashFragmentUrl; + // 3. Reset queries from old studies this.queries = {}; // Update CellBase and refresh the session. From 0f0a14023c7e7a4b373ab14ef526599cf674d867 Mon Sep 17 00:00:00 2001 From: Josemi Date: Fri, 12 Apr 2024 14:39:24 +0200 Subject: [PATCH 08/10] iva: fix bug when changing app or tool from navbar or sidebar #TASK-5864 --- src/sites/iva/iva-app.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/sites/iva/iva-app.js b/src/sites/iva/iva-app.js index d32d710a3c..646ef59155 100644 --- a/src/sites/iva/iva-app.js +++ b/src/sites/iva/iva-app.js @@ -791,7 +791,7 @@ class IvaApp extends LitElement { changeTool(e) { // prevents the hash change to "#" and allows to manipulate the hash fragment as needed - e.preventDefault(); + // e.preventDefault(); const target = e.currentTarget; $(".navbar-inverse ul > li", this).removeClass("active"); @@ -800,14 +800,14 @@ class IvaApp extends LitElement { $(target).closest("ul").closest("li").addClass("active"); } - if (target?.attributes?.href) { - this.tool = target.attributes.href.value; - } else { - this.tool = "#home"; - } + // if (target?.attributes?.href) { + // this.tool = target.attributes.href.value; + // } else { + // this.tool = "#home"; + // } - // this.renderHashFragments(); - this.hashFragmentListener(); + // // this.renderHashFragments(); + // this.hashFragmentListener(); } // renderHashFragments(tool) { @@ -856,6 +856,7 @@ class IvaApp extends LitElement { // 2. Parse hash fragment URL const [hashTool, hashProject, hashStudy, hashQuery] = window.location.hash.split("/"); + console.log(hashTool); // 3. Processing the actions // NOTE: remove this check: hashTool === "#interpreter" || @@ -1108,7 +1109,7 @@ class IvaApp extends LitElement { } toggleSideBar(e) { - e.preventDefault(); + // e.preventDefault(); // const sidenav = this.querySelector("#side-nav"); $("#side-nav").toggleClass("active"); $("#overlay").toggleClass("active"); From fe3c775baf0f36f592ee4c82a2088e76f3c6ff97 Mon Sep 17 00:00:00 2001 From: Josemi Date: Fri, 12 Apr 2024 14:42:48 +0200 Subject: [PATCH 09/10] iva: remove useless requestUpdate after changing app #TASK-5864 --- src/sites/iva/iva-app.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sites/iva/iva-app.js b/src/sites/iva/iva-app.js index 646ef59155..0a50c3ef86 100644 --- a/src/sites/iva/iva-app.js +++ b/src/sites/iva/iva-app.js @@ -1088,7 +1088,6 @@ class IvaApp extends LitElement { // TODO this should keep in sync the query object between variant-browser and variant-facet onQueryChange(e) { - debugger console.warn("onQueryChange", e); this.browserSearchQuery = {...e.detail.query}; // this.browserSearchQuery = {}; @@ -1108,7 +1107,7 @@ class IvaApp extends LitElement { this.clinicalAnalysis = e.detail.clinicalAnalysis; } - toggleSideBar(e) { + toggleSideBar() { // e.preventDefault(); // const sidenav = this.querySelector("#side-nav"); $("#side-nav").toggleClass("active"); @@ -1125,11 +1124,10 @@ class IvaApp extends LitElement { // We only want to toggle when clicked in the sidenav if (toggle) { - this.toggleSideBar(e); + this.toggleSideBar(); } this.changeTool(e); - this.requestUpdate(); } getActiveAppConfig() { From 5d855213fc4af3680696f3676c85b8c5e07b0fc3 Mon Sep 17 00:00:00 2001 From: Josemi Date: Fri, 12 Apr 2024 16:25:51 +0200 Subject: [PATCH 10/10] iva: added minor checks in hashFragmentListener #TASK-5864 --- src/sites/iva/iva-app.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/sites/iva/iva-app.js b/src/sites/iva/iva-app.js index 0a50c3ef86..5630a328d2 100644 --- a/src/sites/iva/iva-app.js +++ b/src/sites/iva/iva-app.js @@ -856,7 +856,6 @@ class IvaApp extends LitElement { // 2. Parse hash fragment URL const [hashTool, hashProject, hashStudy, hashQuery] = window.location.hash.split("/"); - console.log(hashTool); // 3. Processing the actions // NOTE: remove this check: hashTool === "#interpreter" || @@ -866,11 +865,15 @@ class IvaApp extends LitElement { // 4. Parse project and study if (hashProject !== this.opencgaSession?.project?.id || hashStudy !== this.opencgaSession?.study?.id) { - this.changeActiveStudy(`${this.opencgaSession.user.id}@${hashProject}:${hashStudy}`); + if (hashProject && hashStudy) { + this.changeActiveStudy(`${this.opencgaSession.user.id}@${hashProject}:${hashStudy}`); + } } - // 5. Update location.hash - window.location.hash = `${hashTool || "home"}/${this.opencgaSession.project.id}/${this.opencgaSession.study.id}`; + // 5. Update location.hash (only if project.id and study.id are defined) + if (this.opencgaSession?.project?.id && this.opencgaSession?.study?.id) { + window.location.hash = `${hashTool || "home"}/${this.opencgaSession.project.id}/${this.opencgaSession.study.id}`; + } // 6. Parse query fragment in 'featureId' if (hashTool && hashQuery) { @@ -1010,7 +1013,7 @@ class IvaApp extends LitElement { this.opencgaSession = {...this.opencgaSession}; } else { // TODO Convert this into a user notification - console.error("Study not found!"); + console.error(`Study '${studyFqn}' not found!`); } }