diff --git a/src/webcomponents/commons/filters-toolbar.js b/src/webcomponents/commons/filters-toolbar.js index 4cd7aff862..a8bc2b3b01 100644 --- a/src/webcomponents/commons/filters-toolbar.js +++ b/src/webcomponents/commons/filters-toolbar.js @@ -22,8 +22,11 @@ export default class FiltersToolbar extends LitElement { opencgaSession: { type: Object }, - query: { - type: Object + preparedQuery: { + type: Object, + }, + executedQuery: { + type: Object, }, resource: { type: String, @@ -53,12 +56,11 @@ export default class FiltersToolbar extends LitElement { this._prefix = UtilsNew.randomString(8); this._config = this.getDefaultConfig(); - this.query = {}; this.searchActive = true; - - this.queryList = []; this.preparedQuery = {}; + this.executedQuery = {}; + this.queryList = []; this.quickFilters = []; this.applicationFilters = []; this.userFilters = []; @@ -75,10 +77,14 @@ export default class FiltersToolbar extends LitElement { this.opencgaSessionObserver(); } - if (changedProperties.has("query")) { + if (changedProperties.has("preparedQuery") || changedProperties.has("executedQuery")) { this.queryObserver(); } + if (changedProperties.has("executedQuery")) { + this.updateHistory(); + } + super.update(changedProperties); } @@ -104,13 +110,67 @@ export default class FiltersToolbar extends LitElement { } queryObserver() { - this.preparedQuery = UtilsNew.objectClone(this.query); - this.updateQueryList(); + this.queryList = []; - // update the history only if it is empty - if (this.historyFilters.length === 0) { - this.updateHistory(); - } + Object.keys(this.preparedQuery).forEach(key => { + if (!!this.preparedQuery[key] && (!this._config.activeFilters.hiddenFields || !this._config.activeFilters.hiddenFields.includes(key))) { + // We use the alias to rename the key + let title = key; + + if (this._config?.activeFilters?.alias && this._config.activeFilters.alias[key]) { + title = this._config.activeFilters.alias[key]; + } + + // We convert the Query entry object into an array of small objects (queryList) + let value = this.preparedQuery[key]; + if (typeof value === "boolean") { + value = value.toString(); + } + + let filterFields = []; + + // in case of annotation + if (key === "annotation") { + filterFields = value.split(";"); + } else if (key === "study") { + // We fist have need to remove defaultStudy from 'filterFields' and 'value' + filterFields = value.split(/[,;]/).filter(fqn => fqn !== this.opencgaSession?.study?.fqn); + // defaultStudy was the only one present so no need to render anything + if (!filterFields.length) { + return; + } + value = filterFields.join(/[,;]/); + } else { + // Check if the field has been defined as complex + const complexField = (this._config?.activeFilters?.complexFields || []) + .find(item => item.id === key); + + if (complexField) { + filterFields = complexField?.separator ? value.split(complexField.separator) : UtilsNew.splitByRegex(value, complexField.separatorRegex); + } else if (value.indexOf(";") !== -1 && value.indexOf(",") !== -1) { + // If we find a field with both ; and , we will separate by ; + filterFields = value.split(";"); + } else { + filterFields = value.split(new RegExp("[,;]")); + } + } + + // Check if the field is locked + const locked = !!this.lockedFieldsMap[key]; + const lockedTooltip = locked ? this.lockedFieldsMap[key].message : ""; + + // separator used in the text in case that we only have one filter field + const separator = (value.indexOf(">") !== -1 || value.indexOf("<") !== -1 || value.indexOf("=") !== -1) ? ": " : " = "; + + this.queryList.push({ + name: key, + text: filterFields.length === 1 ? title + separator + value : title, + items: filterFields, + locked: locked, + message: lockedTooltip, + }); + } + }); } configObserver() { @@ -161,7 +221,7 @@ export default class FiltersToolbar extends LitElement { updateHistory() { // 1. remove all identical filters const history = this.historyFilters.filter(historyItem => { - return JSON.stringify(historyItem.query) !== JSON.stringify(this.preparedQuery); + return JSON.stringify(historyItem.query) !== JSON.stringify(this.executedQuery); }); // 2. remove previous latest @@ -173,7 +233,7 @@ export default class FiltersToolbar extends LitElement { history.unshift({ id: UtilsNew.dateFormatter(UtilsNew.getDatetime(), "HH:mm:ss"), // date: UtilsNew.getDatetime(), - query: UtilsNew.objectClone(this.preparedQuery), + query: UtilsNew.objectClone(this.executedQuery), latest: true, }); @@ -182,67 +242,7 @@ export default class FiltersToolbar extends LitElement { } updateQueryList() { - this.queryList = []; - - Object.keys(this.preparedQuery).forEach(key => { - if (!!this.preparedQuery[key] && (!this._config.activeFilters.hiddenFields || !this._config.activeFilters.hiddenFields.includes(key))) { - // We use the alias to rename the key - let title = key; - - if (this._config?.activeFilters?.alias && this._config.activeFilters.alias[key]) { - title = this._config.activeFilters.alias[key]; - } - - // We convert the Query entry object into an array of small objects (queryList) - let value = this.preparedQuery[key]; - if (typeof value === "boolean") { - value = value.toString(); - } - - let filterFields = []; - - // in case of annotation - if (key === "annotation") { - filterFields = value.split(";"); - } else if (key === "study") { - // We fist have need to remove defaultStudy from 'filterFields' and 'value' - filterFields = value.split(/[,;]/).filter(fqn => fqn !== this.opencgaSession?.study?.fqn); - // defaultStudy was the only one present so no need to render anything - if (!filterFields.length) { - return; - } - value = filterFields.join(/[,;]/); - } else { - // Check if the field has been defined as complex - const complexField = (this._config?.activeFilters?.complexFields || []) - .find(item => item.id === key); - - if (complexField) { - filterFields = complexField?.separator ? value.split(complexField.separator) : UtilsNew.splitByRegex(value, complexField.separatorRegex); - } else if (value.indexOf(";") !== -1 && value.indexOf(",") !== -1) { - // If we find a field with both ; and , we will separate by ; - filterFields = value.split(";"); - } else { - filterFields = value.split(new RegExp("[,;]")); - } - } - - // Check if the field is locked - const locked = !!this.lockedFieldsMap[key]; - const lockedTooltip = locked ? this.lockedFieldsMap[key].message : ""; - - // separator used in the text in case that we only have one filter field - const separator = (value.indexOf(">") !== -1 || value.indexOf("<") !== -1 || value.indexOf("=") !== -1) ? ": " : " = "; - - this.queryList.push({ - name: key, - text: filterFields.length === 1 ? title + separator + value : title, - items: filterFields, - locked: locked, - message: lockedTooltip, - }); - } - }); + return null; } saveFilter() { @@ -358,39 +358,43 @@ export default class FiltersToolbar extends LitElement { // Example: REST accepts filter and qual while filter returns FILTER and QUALITY // - key: {filter: "FILTER", qual: "QUALITY"} // - value: {FILTER: "pass", QUALITY: "25"} + const query = UtilsNew.objectClone(this.preparedQuery); + if (typeof key === "object" && typeof value === "object") { Object.values(key).forEach(k => { if (value[k] && value[k] !== "") { - this.preparedQuery[k] = value[k]; + query[k] = value[k]; } else { - delete this.preparedQuery[k]; + delete query[k]; } }); } else { if (!!value) { - this.preparedQuery[key] = value; + query[key] = value; } else { - delete this.preparedQuery[key]; + delete query[key]; } } - this.notifyQuery(this.preparedQuery); + this.notifyQuery(query); } onFilterDelete(key, value) { + const query = UtilsNew.objectClone(this.preparedQuery); + // in case that we want to remove the whole filter, or the filter has a single value - if (!value || this.preparedQuery[key] === value) { - delete this.preparedQuery[key]; + if (!value || query[key] === value) { + delete query[key]; } else { let filterFields = []; const complexField = (this._config?.complexFields || []).find(item => item.id === key); if (complexField) { - filterFields = complexField?.separator ? this.preparedQuery[key].split(complexField.separator) : UtilsNew.splitByRegex(this.preparedQuery[key], complexField.separatorRegex); + filterFields = complexField?.separator ? query[key].split(complexField.separator) : UtilsNew.splitByRegex(query[key], complexField.separatorRegex); } else if (value.indexOf(";") !== -1 && value.indexOf(",") !== -1) { - filterFields = this.preparedQuery[key].split(";"); // If we find a field with both ; and , we will separate by ; + filterFields = query[key].split(";"); // If we find a field with both ; and , we will separate by ; } else { - filterFields = this.preparedQuery[key].split(new RegExp("[,;]")); + filterFields = query[key].split(new RegExp("[,;]")); } // remove value from filterFields @@ -398,35 +402,29 @@ export default class FiltersToolbar extends LitElement { // restore the query field if (complexField) { - this.preparedQuery[key] = complexField?.separator ? filterFields.join(complexField.separator) : filterFields.join(","); + query[key] = complexField?.separator ? filterFields.join(complexField.separator) : filterFields.join(","); } else if (value.indexOf(";") !== -1 && value.indexOf(",") !== -1) { - this.preparedQuery[key] = filterFields.join(";"); - } else if (this.preparedQuery[key].indexOf(",") !== -1) { - this.preparedQuery[key] = filterFields.join(","); + query[key] = filterFields.join(";"); + } else if (query[key].indexOf(",") !== -1) { + query[key] = filterFields.join(","); } else { - this.preparedQuery[key] = filterFields.join(";"); + query[key] = filterFields.join(";"); } } - this.notifySearch(this.preparedQuery); - this.updateHistory(); + this.notifySearch(query); } onApplyQuery(query) { - this.preparedQuery = UtilsNew.objectClone(query || {}); - this.notifySearch(this.preparedQuery); - this.updateHistory(); + this.notifySearch(query); } onSearch() { this.notifySearch(this.preparedQuery); - this.updateHistory(); } onClear() { - this.preparedQuery = {}; - this.notifySearch(this.preparedQuery); - this.updateHistory(); + LitUtils.dispatchCustomEvent(this, "queryClear", null, {}); } onSave() { @@ -435,7 +433,7 @@ export default class FiltersToolbar extends LitElement { onCopyLink() { // 1. Generate the url to the tool with the current query - const link = WebUtils.getIVALink(this.opencgaSession, this.toolId, this.query); + const link = WebUtils.getIVALink(this.opencgaSession, this.toolId, this.preparedQuery); // 2. Copy this link to the user clipboard UtilsNew.copyToClipboard(link); @@ -755,6 +753,9 @@ export default class FiltersToolbar extends LitElement { hiddenFields: [], lockedFields: [], }, + save: { + ignoreParams: [], + }, sections: [], examples: [], defaultFilter: {}, diff --git a/src/webcomponents/variant/interpretation/variant-interpreter-browser-cancer.js b/src/webcomponents/variant/interpretation/variant-interpreter-browser-cancer.js index 83c4d3537e..006630a30e 100644 --- a/src/webcomponents/variant/interpretation/variant-interpreter-browser-cancer.js +++ b/src/webcomponents/variant/interpretation/variant-interpreter-browser-cancer.js @@ -27,7 +27,7 @@ class VariantInterpreterBrowserCancer extends LitElement { super(); // Set status and init private properties - this._init(); + this.#init(); } createRenderRoot() { @@ -45,12 +45,12 @@ class VariantInterpreterBrowserCancer extends LitElement { opencgaSession: { type: Object }, - // query: { - // type: Object - // }, cellbaseClient: { type: Object }, + title: { + type: String, + }, settings: { type: Object }, @@ -60,7 +60,7 @@ class VariantInterpreterBrowserCancer extends LitElement { }; } - _init() { + #init() { this.COMPONENT_ID = "variant-interpreter-cancer-snv"; this._prefix = UtilsNew.randomString(8); @@ -75,18 +75,14 @@ class VariantInterpreterBrowserCancer extends LitElement { if (changedProperties.has("clinicalAnalysisId")) { this.clinicalAnalysisIdObserver(); } + if (changedProperties.has("clinicalAnalysis")) { this.clinicalAnalysisObserver(); } - // if (changedProperties.has("query")) { - // this.queryObserver(); - // } + super.update(changedProperties); } - /* - * Fetch the ClinicalAnalysis object from REST and trigger the observer call. - */ clinicalAnalysisIdObserver() { if (this.opencgaSession && this.clinicalAnalysisId) { this.opencgaSession.opencgaClient.clinical().info(this.clinicalAnalysisId, {study: this.opencgaSession.study.fqn}) @@ -296,28 +292,15 @@ class VariantInterpreterBrowserCancer extends LitElement { } return { - title: "Cancer Case Interpreter", - icon: "fas fa-search", - active: false, - showOtherTools: false, - showTitle: false, + title: this.title || "SNV Variant Browser", filter: { - title: "Filter", - searchButton: true, - searchButtonText: "Search", activeFilters: { - alias: { - // Example: - "ct": "Consequence Types", - "sample": "Sample Genotype" - }, - complexFields: [ - {id: "sample", separator: ";"}, - {id: "fileData", separator: ","}, - ], hiddenFields: [], lockedFields: lockedFields, }, + save: { + ignoreParams: ["study", "sample", "file", "fileData"], + }, sections: [ // sections and subsections, structure and order is respected { title: "Sample And File", diff --git a/src/webcomponents/variant/interpretation/variant-interpreter-browser-cnv.js b/src/webcomponents/variant/interpretation/variant-interpreter-browser-cnv.js index f822ea1f99..ff1cc7e6ba 100644 --- a/src/webcomponents/variant/interpretation/variant-interpreter-browser-cnv.js +++ b/src/webcomponents/variant/interpretation/variant-interpreter-browser-cnv.js @@ -27,7 +27,7 @@ class VariantInterpreterBrowserCNV extends LitElement { super(); // Set status and init private properties - this._init(); + this.#init(); } createRenderRoot() { @@ -48,6 +48,9 @@ class VariantInterpreterBrowserCNV extends LitElement { cellbaseClient: { type: Object }, + title: { + type: String, + }, settings: { type: Object }, @@ -57,7 +60,7 @@ class VariantInterpreterBrowserCNV extends LitElement { }; } - _init() { + #init() { this.COMPONENT_ID = "variant-interpreter-cancer-cnv"; this._prefix = UtilsNew.randomString(8); @@ -264,27 +267,15 @@ class VariantInterpreterBrowserCNV extends LitElement { } return { - title: "Cancer CNV Case Interpreter", - icon: "fas fa-search", - active: false, - showOtherTools: false, - showTitle: false, + title: this.title || "Cancer CNV Variant Browser", filter: { - title: "Filter", - searchButton: true, - searchButtonText: "Search", activeFilters: { - alias: { - "ct": "Consequence Types", - "sample": "Sample Genotype" - }, - complexFields: [ - {id: "sample", separator: ";"}, - {id: "fileData", separator: ","}, - ], hiddenFields: [], lockedFields: lockedFields, }, + save: { + ignoreParams: ["study", "sample", "file", "fileData"], + }, sections: [ // sections and subsections, structure and order is respected { title: "Sample And File", diff --git a/src/webcomponents/variant/interpretation/variant-interpreter-browser-rd.js b/src/webcomponents/variant/interpretation/variant-interpreter-browser-rd.js index e08dd3ff1e..56fcdd1428 100644 --- a/src/webcomponents/variant/interpretation/variant-interpreter-browser-rd.js +++ b/src/webcomponents/variant/interpretation/variant-interpreter-browser-rd.js @@ -28,7 +28,7 @@ class VariantInterpreterBrowserRd extends LitElement { super(); // Set status and init private properties - this._init(); + this.#init(); } createRenderRoot() { @@ -46,12 +46,12 @@ class VariantInterpreterBrowserRd extends LitElement { opencgaSession: { type: Object }, - // query: { - // type: Object - // }, cellbaseClient: { type: Object }, + title: { + type: String, + }, settings: { type: Object }, @@ -61,7 +61,7 @@ class VariantInterpreterBrowserRd extends LitElement { }; } - _init() { + #init() { this.COMPONENT_ID = "variant-interpreter-rd"; this._prefix = UtilsNew.randomString(8); @@ -76,12 +76,11 @@ class VariantInterpreterBrowserRd extends LitElement { if (changedProperties.has("clinicalAnalysisId")) { this.clinicalAnalysisIdObserver(); } + if (changedProperties.has("clinicalAnalysis")) { this.clinicalAnalysisObserver(); } - // if (changedProperties.has("query")) { - // this.queryObserver(); - // } + super.update(changedProperties); } @@ -314,26 +313,15 @@ class VariantInterpreterBrowserRd extends LitElement { } return { - title: "RD Case Interpreter", - showSaveInterpretation: true, - showOtherTools: true, - showTitle: false, + title: this.title || "RD Variant Browser", filter: { - title: "Filter", - searchButton: true, - searchButtonText: "Search", activeFilters: { - alias: { - "ct": "Consequence Types", - "sample": "Sample Genotype" - }, - complexFields: [ - {id: "sample", separator: ";"}, - {id: "fileData", separator: ","}, - ], hiddenFields: [], lockedFields: lockedFields }, + save: { + ignoreParams: ["study", "sample", "file", "fileData"], + }, sections: [ { title: "Sample", diff --git a/src/webcomponents/variant/interpretation/variant-interpreter-browser-rearrangement.js b/src/webcomponents/variant/interpretation/variant-interpreter-browser-rearrangement.js index 29e6f1686b..d59a93806a 100644 --- a/src/webcomponents/variant/interpretation/variant-interpreter-browser-rearrangement.js +++ b/src/webcomponents/variant/interpretation/variant-interpreter-browser-rearrangement.js @@ -47,6 +47,9 @@ class VariantInterpreterBrowserRearrangement extends LitElement { cellbaseClient: { type: Object }, + title: { + type: String, + }, active: { type: Boolean, }, @@ -80,9 +83,6 @@ class VariantInterpreterBrowserRearrangement extends LitElement { super.update(changedProperties); } - /* - * Fetch the ClinicalAnalysis object from REST and trigger the observer call. - */ clinicalAnalysisIdObserver() { if (this.opencgaSession && this.clinicalAnalysisId) { this.opencgaSession.opencgaClient.clinical().info(this.clinicalAnalysisId, {study: this.opencgaSession.study.fqn}) @@ -271,23 +271,9 @@ class VariantInterpreterBrowserRearrangement extends LitElement { getDefaultConfig() { return { - title: "Cancer Case Interpreter", - icon: "fas fa-search", - active: false, - showOtherTools: false, - showTitle: false, + title: this.title || "Rearrangement Variant Browser", filter: { - title: "Filter", - searchButton: true, - searchButtonText: "Search", activeFilters: { - alias: { - "ct": "Consequence Types" - }, - complexFields: [ - {id: "sample", separator: ";"}, - {id: "fileData", separator: ","}, - ], hiddenFields: [], lockedFields: [ {id: "sample"}, @@ -296,6 +282,9 @@ class VariantInterpreterBrowserRearrangement extends LitElement { // {id: "fileData"}, ], }, + save: { + ignoreParams: ["study", "sample", "file", "fileData"], + }, callers: [], sections: [ { diff --git a/src/webcomponents/variant/interpretation/variant-interpreter-browser-template.js b/src/webcomponents/variant/interpretation/variant-interpreter-browser-template.js index 38957a338d..620fe5aec1 100644 --- a/src/webcomponents/variant/interpretation/variant-interpreter-browser-template.js +++ b/src/webcomponents/variant/interpretation/variant-interpreter-browser-template.js @@ -15,17 +15,18 @@ */ import {html, LitElement, nothing} from "lit"; -import VariantUtils from "../variant-utils.js"; import ClinicalAnalysisManager from "../../clinical/clinical-analysis-manager.js"; import LitUtils from "../../commons/utils/lit-utils.js"; import OpencgaCatalogUtils from "../../../core/clients/opencga/opencga-catalog-utils.js"; import UtilsNew from "../../../core/utils-new.js"; +import WebUtils from "../../commons/utils/web-utils.js"; import Region from "../../../core/bioinfo/region.js"; import "./variant-interpreter-browser-toolbar.js"; import "./variant-interpreter-grid.js"; import "./variant-interpreter-detail.js"; import "../variant-browser-filter.js"; import "../../commons/tool-header.js"; +import "../../commons/grid-notifications.js"; import "../../commons/opencga-active-filters.js"; import "../../visualization/genome-browser.js"; import "../../visualization/split-genome-browser.js"; @@ -36,7 +37,7 @@ class VariantInterpreterBrowserTemplate extends LitElement { super(); // Set status and init private properties - this._init(); + this.#init(); } createRenderRoot() { @@ -72,12 +73,13 @@ class VariantInterpreterBrowserTemplate extends LitElement { }; } - _init() { + #init() { this._prefix = UtilsNew.randomString(8); this.searchActive = true; this.variant = null; this.query = {}; + this.notifications = []; // Saves the current active view this.activeView = "table"; @@ -213,7 +215,8 @@ class VariantInterpreterBrowserTemplate extends LitElement { }); } - onQueryComplete() { + onQueryComplete(event) { + this.notifications = WebUtils.getResponseEvents(event.detail.response); this.searchActive = true; this.requestUpdate(); } @@ -307,8 +310,8 @@ class VariantInterpreterBrowserTemplate extends LitElement { }); } - onActiveFilterClear() { - const lockedFields = [...this._config?.filter?.activeFilters?.lockedFields.map(key => key.id)]; + onVariantFilterClear() { + const lockedFields = this._config?.filter?.activeFilters?.lockedFields.map(key => key.id); let _query = { study: this.opencgaSession.study.fqn }; @@ -341,58 +344,35 @@ class VariantInterpreterBrowserTemplate extends LitElement { this.requestUpdate(); } - renderStyles() { - return html ` - `; - } - - renderViewButton(id, title, icon) { + renderHeaderRightContent() { + const viewButtons = [ + {name: "Table View", id: "table", icon: "fa fa-table", visible: true}, + {name: "Genome Browser", id: "genome-browser", icon: "fas fa-dna", visible: !this.settings?.hideGenomeBrowser}, + ]; return html` - +