diff --git a/src/webcomponents/commons/aggregation-stats.js b/src/webcomponents/commons/aggregation-stats.js index 5975bc6f55..5d450221ea 100644 --- a/src/webcomponents/commons/aggregation-stats.js +++ b/src/webcomponents/commons/aggregation-stats.js @@ -14,14 +14,13 @@ * limitations under the License. */ -import {LitElement, html} from "lit"; +import {html, LitElement, nothing} from "lit"; import {RestResponse} from "../../core/clients/rest-response.js"; import UtilsNew from "../../core/utils-new.js"; -import PolymerUtils from "../PolymerUtils.js"; +import NotificationUtils from "./utils/notification-utils.js"; import "./opencga-facet-result-view.js"; import "./facet-filter.js"; import "../loading-spinner.js"; -import NotificationUtils from "./utils/notification-utils.js"; class AggregationStats extends LitElement { @@ -41,28 +40,18 @@ class AggregationStats extends LitElement { resource: { type: String }, - opencgaSession: { + facet: { type: Object }, query: { type: Object }, - cellbaseClient: { - type: Object - }, - populationFrequencies: { - type: Object - }, active: { type: Boolean }, - // Static data. Non currently used (query is being used instead) - data: { + opencgaSession: { type: Object }, - loading: { - type: Boolean - }, config: { type: Object } @@ -72,90 +61,31 @@ class AggregationStats extends LitElement { #init() { this._prefix = UtilsNew.randomString(8); - this._showInitMessage = true; - - this.facets = new Set(); - this.facetActive = true; + this.facet = {}; + this.preparedFacetQueryFormatted = {}; + this.facetResults = []; + this.loading = false; this._config = this.getDefaultConfig(); - this.selectedFacet = {}; - this.facetResults = []; } - updated(changedProperties) { - if (changedProperties.has("opencgaSession") && this.active) { + update(changedProperties) { + if (changedProperties.has("facet") && this.active) { this.queryObserver(); } - if (changedProperties.has("query")) { + if (changedProperties.has("query") && this.active) { this.queryObserver(); } - if (changedProperties.has("config")) { - this.configObserver(); - } if (changedProperties.has("active") && this.active) { - console.warn("fire http req"); // TODO check if query has changed before - // this.fetchDefaultData(); + this.queryObserver() } - } - - queryObserver() { - this.facetQueryBuilder(); - - debugger - // executedQuery in opencga-variant-browser has changed so, if requested, we have to repeat the facet query - this.facetResults = []; - this.requestUpdate(); - // await this.updateComplete; - if (this.query) { - this.loading = true; - this.errorState = false; - this.requestUpdate(); - // await this.updateComplete; - this.endpoint(this.resource).aggregationStats(this.query, {}) - .then(restResponse => { - this.errorState = false; - - // Remove all categories with an empty 'value' (no id) - const results = restResponse.responses[0].results; - for (const result of results) { - result.buckets = result.buckets.filter(bucket => !!bucket.value); - } - this.facetResults = results || []; - }) - .catch(response => { - if (response instanceof RestResponse || response instanceof Error) { - NotificationUtils.dispatch(this, NotificationUtils.NOTIFY_RESPONSE, response); - } else { - this.errorState = [{name: "Generic Error", message: JSON.JSON.stringify(response)}]; - NotificationUtils.dispatch(this, NotificationUtils.NOTIFY_ERROR, { - title: this.errorState[0].name, - message: this.errorState[0].message, - }); - } - }) - .finally(() => { - this.loading = false; - this.requestUpdate(); - }); + if (changedProperties.has("opencgaSession") && this.active) { + this.queryObserver(); } - } - - facetQueryBuilder() { - // facetQuery is the query object sent to the client in - debugger - if (Object.keys(this.selectedFacet).length) { - this.executedFacetQueryFormatted = {...this.preparedFacetQueryFormatted}; - - this.facetQuery = { - ...this.preparedQuery, - study: this.opencgaSession.study.fqn, - // FIXME rename fields to field - fields: Object.values(this.preparedFacetQueryFormatted).map(v => v.formatted).join(";") - }; - this.changeView("facet-tab"); - } else { - this.facetQuery = null; + if (changedProperties.has("config")) { + this.configObserver(); } + super.update(changedProperties); } configObserver() { @@ -166,60 +96,98 @@ class AggregationStats extends LitElement { switch (resource) { case "VARIANT": return this.opencgaSession.opencgaClient.variants(); + case "JOB": + return this.opencgaSession.opencgaClient.jobs(); case "FILE": return this.opencgaSession.opencgaClient.files(); case "SAMPLE": return this.opencgaSession.opencgaClient.samples(); case "INDIVIDUAL": return this.opencgaSession.opencgaClient.individuals(); - case "COHORT": - return this.opencgaSession.opencgaClient.cohorts(); case "FAMILY": return this.opencgaSession.opencgaClient.families(); + case "COHORT": + return this.opencgaSession.opencgaClient.cohorts(); case "CLINICAL_ANALYSIS": return this.opencgaSession.opencgaClient.clinical(); - case "JOB": - return this.opencgaSession.opencgaClient.jobs(); default: throw new Error("Resource not recognized"); } } - clearPlots() { - if (UtilsNew.isNotUndefined(this.results) && this.results.length > 0) { - for (const result of this.results) { - PolymerUtils.removeElement(this._prefix + result.name + "Plot"); - } - } - this.results = []; + onAggregationFieldChange(e) { + this.preparedFacetQueryFormatted = e.detail.value; } - clear() { - this.clearPlots(); - this.chromosome = ""; + /** + * This method creates the facetQuery object to be sent to the client in + */ + facetQueryBuilder() { + if (Object.keys(this.preparedFacetQueryFormatted).length) { + this.executedFacetQueryFormatted = {...this.preparedFacetQueryFormatted}; + // Build the facet query object + this.facetQuery = { + ...this.query, + study: this.opencgaSession.study.fqn, + field: Object.values(this.preparedFacetQueryFormatted) + .map(v => v.formatted) + .join(";") + }; + } else { + this.facetQuery = null; + } + } - this.facets = new Set(); - this.facetFilters = []; + queryObserver() { + // 1. Prepare the facet query object + this.facetQueryBuilder(); - PolymerUtils.hide(this._prefix + "Warning"); + // 2. Execute the facet query + this.facetResults = []; + if (this.facetQuery) { + this.loading = true; + this.errorState = null; + this.requestUpdate(); - this.facetFields = []; - this.facetRanges = []; - this.facetFieldsName = []; - this.facetRangeFields = []; - this._showInitMessage = true; + this.endpoint(this.resource) + .aggregationStats(this.facetQuery, {}) + .then(restResponse => { + this.errorState = null; - this.requestUpdate(); + const results = restResponse.responses[0].results; + // Remove all categories with an empty 'value' (no id) + for (const result of results) { + result.buckets = result.buckets + .filter(bucket => !!bucket.value); + } + this.facetResults = results || []; + }) + .catch(response => { + // this.facetResults = []; + if (response instanceof RestResponse || response instanceof Error) { + NotificationUtils.dispatch(this, NotificationUtils.NOTIFY_RESPONSE, response); + } else { + this.errorState = [{name: "Aggregation Error", message: JSON.stringify(response)}]; + NotificationUtils.dispatch(this, NotificationUtils.NOTIFY_ERROR, { + title: this.errorState[0].name, + message: this.errorState[0].message, + }); + } + }) + .finally(() => { + this.loading = false; + this.requestUpdate(); + }); + } } - getDefaultConfig() { - return { - }; + aggregationClear() { + this.facet = {}; + this.facetResults = []; + this.preparedFacetQueryFormatted = {}; } render() { - this._config; - debugger return html` -
+
- + @facetQueryChange="${this.onAggregationFieldChange}" + @aggregationClear="${this.aggregationClear}">
-
- ${this.loading ? html` -
- -
- ` : null } - ${this.errorState?.length ? html` - - ` : null} - - ${this.facetResults.length ? this.facetResults.map(item => item.aggregationName && item.aggregationValues ? html` -
-

${item.name}

-
- ${item.aggregationName} - ${item.aggregationValues} -
-
- ` : html` -
-

${item.name}

- - -
- `) : null} - - ${false && !this.facetResults.length && !this.loading && !this.errorState ? - !this.query ? html` - - ` : html` - - ` : null} -
+ ${this.errorState?.length > 0 ? html` + + ` : nothing} + + + ${this.loading ? html` +
+ +
+ ` : html` + + + `}
`; } + getDefaultConfig() { + return {}; + } + } customElements.define("aggregation-stats", AggregationStats); diff --git a/src/webcomponents/commons/facet-filter.js b/src/webcomponents/commons/facet-filter.js index ecc85587a6..1b9ef8a0c9 100644 --- a/src/webcomponents/commons/facet-filter.js +++ b/src/webcomponents/commons/facet-filter.js @@ -14,16 +14,18 @@ * limitations under the License. */ -import {LitElement, html} from "lit"; +import {html, LitElement} from "lit"; import UtilsNew from "../../core/utils-new.js"; import "../commons/filters/consequence-type-select-filter.js"; import "../commons/forms/select-field-filter.js"; +import LitUtils from "./utils/lit-utils"; export default class FacetFilter extends LitElement { constructor() { super(); - this._init(); + + this.#init(); } createRenderRoot() { @@ -42,11 +44,13 @@ export default class FacetFilter extends LitElement { }; } - _init() { + #init() { this._prefix = UtilsNew.randomString(8); - this.fns = {avg: "Average", min: "Minimum", max: "Maxiumum", unique: "Uniques values", hll: "Distributed cardinality estimate", percentile: "Percentile estimate", sumsq: "Sum of squares of fields or function"}; + this.fns = {avg: "Average", min: "Minimum", max: "Maximum", unique: "Uniques values", hll: "Distributed cardinality estimate", percentile: "Percentile estimate", sumsq: "Sum of squares of fields or function"}; this.selectFns = Object.entries({range: "Range", ...this.fns}).map(([k, v]) => ({id: k, name: v})); + this.selectedFacet = {}; + // copy of selectedFacet in JSON string, to avoid unnecessary refresh this._JsonSelectedFacet = null; this.preparedQuery = {}; @@ -60,7 +64,6 @@ export default class FacetFilter extends LitElement { } selectedFacetObserver() { - // Helper for formatting the list of facets to show in opencga-active-filters const _valueFormatter = (k, v) => { let str = ""; @@ -151,7 +154,7 @@ export default class FacetFilter extends LitElement { async onFacetFieldChange(e) { /** * fires a filterChange event with all the selected values. Here we need just the new selected (deselected) item, so we compute the difference between the 2 sets. - */ + */ const currentSelectionNames = e.detail.value ? e.detail.value.split(",") : []; // compute the symmetric difference between this.selectedFacet and currentSelectionNames @@ -392,26 +395,26 @@ export default class FacetFilter extends LitElement {
- +
+ id="${this._prefix}${facet.id}_range_stop" .disabled="${facet.fn}" + data-id="${facet.id}" data-type="range_stop" .value="${nstop || ""}" + @input="${this.onFacetRangeChange}" />
+ id="${this._prefix}${facet.id}_range_step" .disabled="${facet.fn}" + data-id="${facet.id}" data-type="range_step" .value="${nstep || ""}" + @input="${this.onFacetRangeChange}" />
- +
- `; + `; default: console.log("no type recognized", facet); return html`
Type not recognized: ${JSON.stringify(facet)}
`; @@ -507,21 +510,21 @@ export default class FacetFilter extends LitElement {
+ id="${this._prefix}${parent}_Nested_range_start" .disabled="${facet.fn}" + data-id="${facet.id}" data-type="range_start" .value="${nstart || ""}" + @input="${this.onNestedFacetRangeChange}" />
+ id="${this._prefix}${parent}_Nested_range_stop" .disabled="${facet.fn}" + data-id="${facet.id}" data-type="range_stop" .value="${nstop || ""}" + @input="${this.onNestedFacetRangeChange}" />
+ id="${this._prefix}${parent}_Nested_range_step" .disabled="${facet.fn}" + data-id="${facet.id}" data-type="range_step" .value="${nstep || ""}" + @input="${this.onNestedFacetRangeChange}" />
${Object.keys(this.selectedFacet).length > 0 ? Object.entries(this.selectedFacet).map(([, facet], i) => html ` @@ -613,7 +626,7 @@ export default class FacetFilter extends LitElement { - ` : null} + ` : null}
${this.renderField(facet)} diff --git a/src/webcomponents/commons/opencb-facet-results.js b/src/webcomponents/commons/opencb-facet-results.js index 29922d5fee..c79c4568d6 100644 --- a/src/webcomponents/commons/opencb-facet-results.js +++ b/src/webcomponents/commons/opencb-facet-results.js @@ -14,13 +14,9 @@ * limitations under the License. */ -import {LitElement, html} from "lit"; -import {RestResponse} from "../../core/clients/rest-response.js"; +import {html, LitElement, nothing} from "lit"; import UtilsNew from "../../core/utils-new.js"; -import PolymerUtils from "../PolymerUtils.js"; import "./opencga-facet-result-view.js"; -import "../loading-spinner.js"; -import NotificationUtils from "./utils/notification-utils.js"; class OpencbFacetResults extends LitElement { @@ -37,216 +33,72 @@ class OpencbFacetResults extends LitElement { static get properties() { return { - opencgaSession: { - type: Object - }, - query: { - type: Object - }, - cellbaseClient: { - type: Object - }, - populationFrequencies: { - type: Object + data: { + type: Array }, config: { type: Object - }, - active: { - type: Boolean - }, - // Static data. Non currently used (query is being used instead) - data: { - type: Object - }, - loading: { - type: Boolean - }, - resource: { - type: String } }; } _init() { - this._prefix = "facet-results" + UtilsNew.randomString(6); - - this._showInitMessage = true; - - this.facets = new Set(); - this.facetActive = true; + this._prefix = UtilsNew.randomString(8); + this.data = []; this._config = this.getDefaultConfig(); - - this.facetResults = []; - } - - updated(changedProperties) { - if (changedProperties.has("opencgaSession") && this.active) { - this.queryObserver(); - } - if (changedProperties.has("query")) { - this.queryObserver(); - } - if (changedProperties.has("config")) { - this.configObserver(); - } - if (changedProperties.has("active") && this.active) { - console.warn("fire http req"); // TODO check if query has changed before - // this.fetchDefaultData(); - } } - async queryObserver() { - // executedQuery in opencga-variant-browser has changed so, if requested, we have to repeat the facet query - this.facetResults = []; - this.requestUpdate(); - await this.updateComplete; - if (this.query) { - this.loading = true; - this.errorState = false; - this.requestUpdate(); - await this.updateComplete; - this.endpoint(this.resource).aggregationStats(this.query, {}) - .then(restResponse => { - this.errorState = false; - - // Remove all categories with an empty 'value' (no id) - const results = restResponse.responses[0].results; - for (const result of results) { - result.buckets = result.buckets.filter(bucket => !!bucket.value); - } - this.facetResults = results || []; - }) - .catch(response => { - if (response instanceof RestResponse || response instanceof Error) { - NotificationUtils.dispatch(this, NotificationUtils.NOTIFY_RESPONSE, response); - } else { - this.errorState = [{name: "Generic Error", message: JSON.JSON.stringify(response)}]; - NotificationUtils.dispatch(this, NotificationUtils.NOTIFY_ERROR, { - title: this.errorState[0].name, - message: this.errorState[0].message, - }); - } - }) - .finally(() => { - this.loading = false; - this.requestUpdate(); - }); - } + update(changedProperties) { + // if (changedProperties.has("data")) { + // this.queryObserver(); + // } + super.update(changedProperties); } configObserver() { this._config = {...this.getDefaultConfig(), ...this.config}; } - endpoint(resource) { - switch (resource) { - case "VARIANT": - return this.opencgaSession.opencgaClient.variants(); - case "FILE": - return this.opencgaSession.opencgaClient.files(); - case "SAMPLE": - return this.opencgaSession.opencgaClient.samples(); - case "INDIVIDUAL": - return this.opencgaSession.opencgaClient.individuals(); - case "COHORT": - return this.opencgaSession.opencgaClient.cohorts(); - case "FAMILY": - return this.opencgaSession.opencgaClient.families(); - case "CLINICAL_ANALYSIS": - return this.opencgaSession.opencgaClient.clinical(); - case "JOB": - return this.opencgaSession.opencgaClient.jobs(); - default: - throw new Error("Resource not recognized"); - } - } - - clearPlots() { - if (UtilsNew.isNotUndefined(this.results) && this.results.length > 0) { - for (const result of this.results) { - PolymerUtils.removeElement(this._prefix + result.name + "Plot"); - } - } - this.results = []; - } - - clear() { - this.clearPlots(); - this.chromosome = ""; - - this.facets = new Set(); - this.facetFilters = []; - - PolymerUtils.hide(this._prefix + "Warning"); - - this.facetFields = []; - this.facetRanges = []; - this.facetFieldsName = []; - this.facetRangeFields = []; - this._showInitMessage = true; - - this.requestUpdate(); - } - - getDefaultConfig() { - return { - }; - } - render() { - return html` - -
- ${this.loading ? html` -
- -
- ` : null } - ${this.errorState?.length ? html` - - ` : null} - - ${this.facetResults.length ? this.facetResults.map(item => item.aggregationName && item.aggregationValues ? html` -
-

${item.name}

-
- ${item.aggregationName} - ${item.aggregationValues} + if (!this.data || this.data.length === 0) { + return html` + - ` : html` -
-

${item.name}

- - -
- `) : null} + ` + } - ${!this.facetResults.length && !this.loading && !this.errorState ? - !this.query ? html` - -
+
- -