diff --git a/lib/public/components/Filters/LhcFillsFilter/StableBeamFilterModel.js b/lib/public/components/Filters/LhcFillsFilter/StableBeamFilterModel.js deleted file mode 100644 index 1bc3f8aed2..0000000000 --- a/lib/public/components/Filters/LhcFillsFilter/StableBeamFilterModel.js +++ /dev/null @@ -1,76 +0,0 @@ -/** - * @license - * Copyright CERN and copyright holders of ALICE Trg. This software is - * distributed under the terms of the GNU General Public License v3 (GPL - * Version 3), copied verbatim in the file "COPYING". - * - * See http://alice-Trg.web.cern.ch/license for full licensing information. - * - * In applying this license CERN does not waive the privileges and immunities - * granted to it by virtue of its status as an Intergovernmental Organization - * or submit itself to any jurisdiction. - */ - -import { SelectionModel } from '../../common/selection/SelectionModel.js'; - -/** - * Stable beam filter model - * Holds true or false value - */ -export class StableBeamFilterModel extends SelectionModel { - /** - * Constructor - */ - constructor() { - super({ availableOptions: [{ value: true }, { value: false }], - defaultSelection: [{ value: false }], - multiple: false, - allowEmpty: false }); - } - - /** - * Returns true if the current filter is stable beams only - * - * @return {boolean} true if filter is stable beams only - */ - isStableBeamsOnly() { - return this.current; - } - - /** - * Sets the current filter to stable beams only - * - * @param {boolean} value value to set this stable beams only filter with - * @return {void} - */ - setStableBeamsOnly(value) { - this.select({ value }); - } - - /** - * Get normalized selected option - */ - get normalized() { - return this.current; - } - - /** - * Overrides SelectionModel.isEmpty to respect the fact that stable beam filter cannot be empty. - * @returns {boolean} true if the current value of the filter is false. - */ - get isEmpty() { - return this.current === false; - } - - /** - * Reset the filter to default values - * - * @return {void} - */ - resetDefaults() { - if (!this.isEmpty) { - this.reset(); - this.notify(); - } - } -} diff --git a/lib/public/components/Filters/LhcFillsFilter/stableBeamFilter.js b/lib/public/components/Filters/LhcFillsFilter/stableBeamFilter.js deleted file mode 100644 index b4429c002c..0000000000 --- a/lib/public/components/Filters/LhcFillsFilter/stableBeamFilter.js +++ /dev/null @@ -1,49 +0,0 @@ -/** - * @license - * Copyright CERN and copyright holders of ALICE Trg. This software is - * distributed under the terms of the GNU General Public License v3 (GPL - * Version 3), copied verbatim in the file "COPYING". - * - * See http://alice-Trg.web.cern.ch/license for full licensing information. - * - * In applying this license CERN does not waive the privileges and immunities - * granted to it by virtue of its status as an Intergovernmental Organization - * or submit itself to any jurisdiction. - */ - -import { h } from '/js/src/index.js'; -import { switchInput } from '../../common/form/switchInput.js'; -import { radioButton } from '../../common/form/inputs/radioButton.js'; - -/** - * Display a toggle switch or radio buttons to filter stable beams only - * - * @param {StableBeamFilterModel} stableBeamFilterModel the stableBeamFilterModel - * @param {boolean} radioButtonMode define whether or not to return radio buttons or a switch. - * @returns {Component} the toggle switch - */ -export const toggleStableBeamOnlyFilter = (stableBeamFilterModel, radioButtonMode = false) => { - const name = 'stableBeamsOnlyRadio'; - const labelOff = 'OFF'; - const labelOn = 'ON'; - if (radioButtonMode) { - return h('.form-group-header.flex-row.w-100', [ - radioButton({ - label: labelOff, - isChecked: !stableBeamFilterModel.isStableBeamsOnly(), - action: () => stableBeamFilterModel.setStableBeamsOnly(false), - name: name, - }), - radioButton({ - label: labelOn, - isChecked: stableBeamFilterModel.isStableBeamsOnly(), - action: () => stableBeamFilterModel.setStableBeamsOnly(true), - name: name, - }), - ]); - } else { - return switchInput(stableBeamFilterModel.isStableBeamsOnly(), (newState) => { - stableBeamFilterModel.setStableBeamsOnly(newState); - }, { labelAfter: 'STABLE BEAM ONLY' }); - } -}; diff --git a/lib/public/components/Filters/RunsFilter/GaqFilterModel.js b/lib/public/components/Filters/RunsFilter/GaqFilterModel.js new file mode 100644 index 0000000000..8d42fd8b90 --- /dev/null +++ b/lib/public/components/Filters/RunsFilter/GaqFilterModel.js @@ -0,0 +1,79 @@ +/** + * @license + * Copyright CERN and copyright holders of ALICE Trg. This software is + * distributed under the terms of the GNU General Public License v3 (GPL + * Version 3), copied verbatim in the file "COPYING". + * + * See http://alice-Trg.web.cern.ch/license for full licensing information. + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +import { FilterModel } from '../common/FilterModel.js'; +import { NumericalComparisonFilterModel } from '../common/filters/NumericalComparisonFilterModel.js'; + +/** + * Time-range filter model + */ +export class GaqFilterModel extends FilterModel { + /** + * Constructor + * @param {ToggleFilterModel} mcReproducibleAsNotBad model that determines if a 'not bad' status was reproduceable for a Monte Carlo. + * This param is required as many other filters models need to make use of the same ToggleFilterModel instance + */ + constructor(mcReproducibleAsNotBad) { + super(); + + this._notBadFraction = new NumericalComparisonFilterModel({ scale: 0.01, integer: false }); + this._addSubmodel(this._notBadFraction); + this._mcReproducibleAsNotBad = mcReproducibleAsNotBad; + this._addSubmodel(this._mcReproducibleAsNotBad); + } + + /** + * @inheritDoc + */ + reset() { + this._notBadFraction.reset(); + } + + /** + * @inheritDoc + */ + get isEmpty() { + return this._notBadFraction.isEmpty; + } + + /** + * @inheritDoc + */ + get normalized() { + const normalized = { notBadFraction: this._notBadFraction.normalized }; + + if (!this.isEmpty) { + normalized.mcReproducibleAsNotBad = this._mcReproducibleAsNotBad.isToggled(); + } + + return normalized; + } + + /** + * Return the underlying notBadFraction model + * + * @return {NumericalComparisonFilterModel} the filter model + */ + get notBadFraction() { + return this._notBadFraction; + } + + /** + * Return the underlying mcReproducibleAsNotBad model + * + * @return {ToggleFilterModel} the filter model + */ + get mcReproducibleAsNotBad() { + return this._mcReproducibleAsNotBad; + } +} diff --git a/lib/public/components/Filters/RunsFilter/MultiCompositionFilterModel.js b/lib/public/components/Filters/RunsFilter/MultiCompositionFilterModel.js new file mode 100644 index 0000000000..96e47e871d --- /dev/null +++ b/lib/public/components/Filters/RunsFilter/MultiCompositionFilterModel.js @@ -0,0 +1,92 @@ +/** + * @license + * Copyright CERN and copyright holders of ALICE Trg. This software is + * distributed under the terms of the GNU General Public License v3 (GPL + * Version 3), copied verbatim in the file "COPYING". + * + * See http://alice-Trg.web.cern.ch/license for full licensing information. + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +import { FilterModel } from '../common/FilterModel.js'; + +/** + * Time-range filter model + */ +export class MultiCompositionFilterModel extends FilterModel { + /** + * Constructor + * @param {Object} filters the filters that will make up the composite filter + */ + constructor(filters = {}) { + super(); + + /** + * @type {Object} + */ + this._filters = {}; + + Object.entries(filters).forEach(([key, filter]) => this.putFilter(key, filter)); + } + + /** + * Return a subfilter by key + * + * @param {string} key the key of the subfilter + * @return {FilterModel} the subfilter + */ + putFilter(key, filterModel) { + if (key in this._filters) { + return; + } + + this._filters[key] = filterModel; + this._addSubmodel(filterModel); + } + + /** + * Add new subfilter + * + * @param {string} key key of the subfilter + * @param {FilterModel} filter the the subfilter + */ + getFilter(key) { + if (!(key in this._filters)) { + throw new Error(`No filter found with key ${key}`); + } + + return this._filters[key]; + } + + /** + * @inheritDoc + */ + reset() { + Object.values(this._filters).forEach((filter) => filter.reset()); + } + + /** + * @inheritDoc + */ + get isEmpty() { + return Object.values(this._filters).every((filter) => filter.isEmpty); + } + + /** + * @inheritDoc + */ + get normalized() { + const normalized = {}; + + for (const [id, detector] of Object.entries(this._filters)) { + if (!detector.isEmpty) { + normalized[id] = detector.normalized; + } + } + + return normalized; + } +} diff --git a/lib/public/components/Filters/RunsFilter/dcs.js b/lib/public/components/Filters/RunsFilter/dcs.js deleted file mode 100644 index 590eb81b78..0000000000 --- a/lib/public/components/Filters/RunsFilter/dcs.js +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @license - * Copyright CERN and copyright holders of ALICE Trg. This software is - * distributed under the terms of the GNU General Public License v3 (GPL - * Version 3), copied verbatim in the file "COPYING". - * - * See http://alice-Trg.web.cern.ch/license for full licensing information. - * - * In applying this license CERN does not waive the privileges and immunities - * granted to it by virtue of its status as an Intergovernmental Organization - * or submit itself to any jurisdiction. - */ - -import { radioButton } from '../../common/form/inputs/radioButton.js'; -import { h } from '/js/src/index.js'; - -/** - * Filter panel for DCS toggle; ON/OFF/ANY - * @param {RunsOverviewModel} runModel the run model object - * @return {vnode} Three radio buttons inline - */ -const dcsOperationRadioButtons = (runModel) => { - const state = runModel.getDcsFilterOperation(); - const name = 'dcsFilterRadio'; - const labelAny = 'ANY'; - const labelOff = 'OFF'; - const labelOn = 'ON'; - return h('.form-group-header.flex-row.w-100', [ - radioButton({ - label: labelAny, - isChecked: state === '', - action: () => runModel.removeDcs(), - name, - }), - radioButton({ - label: labelOff, - isChecked: state === false, - action: () => runModel.setDcsFilterOperation(false), - name, - }), - radioButton({ - label: labelOn, - isChecked: state === true, - action: () => runModel.setDcsFilterOperation(true), - name, - }), - ]); -}; - -export default dcsOperationRadioButtons; diff --git a/lib/public/components/Filters/RunsFilter/ddflp.js b/lib/public/components/Filters/RunsFilter/ddflp.js deleted file mode 100644 index 74bf28f4ba..0000000000 --- a/lib/public/components/Filters/RunsFilter/ddflp.js +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @license - * Copyright CERN and copyright holders of ALICE Trg. This software is - * distributed under the terms of the GNU General Public License v3 (GPL - * Version 3), copied verbatim in the file "COPYING". - * - * See http://alice-Trg.web.cern.ch/license for full licensing information. - * - * In applying this license CERN does not waive the privileges and immunities - * granted to it by virtue of its status as an Intergovernmental Organization - * or submit itself to any jurisdiction. - */ - -import { radioButton } from '../../common/form/inputs/radioButton.js'; -import { h } from '/js/src/index.js'; - -/** - * Filter panel for Data Distribution toggle; ON/OFF/ANY - * @param {RunsOverviewModel} runModel the run model object - * @return {vnode} Three radio buttons inline - */ -const ddflpOperationRadioButtons = (runModel) => { - const state = runModel.getDdflpFilterOperation(); - const name = 'ddFlpFilterRadio'; - const labelAny = 'ANY'; - const labelOff = 'OFF'; - const labelOn = 'ON'; - return h('.form-group-header.flex-row.w-100', [ - radioButton({ - label: labelAny, - isChecked: state === '', - action: () => runModel.removeDdflp(), - name, - }), - radioButton({ - label: labelOff, - isChecked: state === false, - action: () => runModel.setDdflpFilterOperation(false), - name, - }), - radioButton({ - label: labelOn, - isChecked: state === true, - action: () => runModel.setDdflpFilterOperation(true), - name, - }), - ]); -}; - -export default ddflpOperationRadioButtons; diff --git a/lib/public/components/Filters/RunsFilter/epn.js b/lib/public/components/Filters/RunsFilter/epn.js deleted file mode 100644 index 5e639d8afb..0000000000 --- a/lib/public/components/Filters/RunsFilter/epn.js +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @license - * Copyright CERN and copyright holders of ALICE Trg. This software is - * distributed under the terms of the GNU General Public License v3 (GPL - * Version 3), copied verbatim in the file "COPYING". - * - * See http://alice-Trg.web.cern.ch/license for full licensing information. - * - * In applying this license CERN does not waive the privileges and immunities - * granted to it by virtue of its status as an Intergovernmental Organization - * or submit itself to any jurisdiction. - */ - -import { radioButton } from '../../common/form/inputs/radioButton.js'; -import { h } from '/js/src/index.js'; - -/** - * Filter panel for EPN toggle; ON/OFF/ANY - * @param {RunsOverviewModel} runModel the run model object - * @return {vnode} Three radio buttons inline - */ -const epnOperationRadioButtons = (runModel) => { - const state = runModel.getEpnFilterOperation(); - const name = 'epnFilterRadio'; - const labelAny = 'ANY'; - const labelOff = 'OFF'; - const labelOn = 'ON'; - return h('.form-group-header.flex-row.w-100', [ - radioButton({ - label: labelAny, - isChecked: state === '', - action: () => runModel.removeEpn(), - name, - }), - radioButton({ - label: labelOff, - isChecked: state === false, - action: () => runModel.setEpnFilterOperation(false), - name, - }), - radioButton({ - label: labelOn, - isChecked: state === true, - action: () => runModel.setEpnFilterOperation(true), - name, - }), - ]); -}; - -export default epnOperationRadioButtons; diff --git a/lib/public/components/Filters/RunsFilter/triggerValueFilter.js b/lib/public/components/Filters/RunsFilter/triggerValueFilter.js deleted file mode 100644 index 5addab02fe..0000000000 --- a/lib/public/components/Filters/RunsFilter/triggerValueFilter.js +++ /dev/null @@ -1,21 +0,0 @@ -import { checkboxFilter } from '../common/filters/checkboxFilter.js'; -import { TRIGGER_VALUES } from '../../../domain/enums/TriggerValue.js'; - -/** - * Returns a panel to be used by user to filter runs by trigger value - * @param {RunsOverviewModel} runModel The global model object - * @return {vnode} Multiple checkboxes for a user to select the values to be filtered. - */ -export const triggerValueFilter = (runModel) => checkboxFilter( - 'triggerValue', - TRIGGER_VALUES, - (value) => runModel.triggerValuesFilters.has(value), - (e, value) => { - if (e.target.checked) { - runModel.triggerValuesFilters.add(value); - } else { - runModel.triggerValuesFilters.delete(value); - } - runModel.triggerValuesFilters = Array.from(runModel.triggerValuesFilters); - }, -); diff --git a/lib/public/components/Filters/common/RadioButtonFilterModel.js b/lib/public/components/Filters/common/RadioButtonFilterModel.js new file mode 100644 index 0000000000..5e93205bfc --- /dev/null +++ b/lib/public/components/Filters/common/RadioButtonFilterModel.js @@ -0,0 +1,34 @@ +/** + * @license + * Copyright CERN and copyright holders of ALICE O2. This software is + * distributed under the terms of the GNU General Public License v3 (GPL + * Version 3), copied verbatim in the file "COPYING". + * + * See http://alice-o2.web.cern.ch/license for full licensing information. + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +import { SelectionModel } from '../../common/selection/SelectionModel.js'; + +/** + * Model for managing a radiobutton view and state + */ +export class RadioButtonFilterModel extends SelectionModel { + /** + * Constructor + * + * @param {SelectionOption[]} [availableOptions] the list of possible operators + * @param {function} [setDefault] function that selects the default from the list of available options. Selects first entry by default + */ + constructor(availableOptions, setDefault = (options) => [options[0]]) { + super({ + availableOptions, + defaultSelection: setDefault(availableOptions), + multiple: false, + allowEmpty: false, + }); + } +} diff --git a/lib/public/components/Filters/common/filters/ToggleFilterModel.js b/lib/public/components/Filters/common/filters/ToggleFilterModel.js new file mode 100644 index 0000000000..46c16c7f80 --- /dev/null +++ b/lib/public/components/Filters/common/filters/ToggleFilterModel.js @@ -0,0 +1,57 @@ +/** + * @license + * Copyright CERN and copyright holders of ALICE O2. This software is + * distributed under the terms of the GNU General Public License v3 (GPL + * Version 3), copied verbatim in the file "COPYING". + * + * See http://alice-o2.web.cern.ch/license for full licensing information. + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ +import { SelectionModel } from '../../../common/selection/SelectionModel.js'; + +/** + * Filtering model to handle raw text value + */ +export class ToggleFilterModel extends SelectionModel { + /** + * Constructor + * @param {boolean} toggledByDefault If the filter should be toggled by default + */ + constructor(toggledByDefault = false) { + super({ availableOptions: [{ value: true }, { value: false }], + defaultSelection: [{ value: toggledByDefault }], + multiple: false, + allowEmpty: false }); + + this._toggledByDefault = toggledByDefault; + } + + /** + * Returns true if the current value is set to true + * + * @return {boolean} true if filter is stable beams only + */ + isToggled() { + return this.current; + } + + /** + * Toggles the filter state + * + * @return {void} + */ + toggle() { + this.select({ value: !this.current }); + } + + /** + * Overrides SelectionModel.isEmpty to respect the fact that toggle filters cannot be empty. + * @returns {boolean} true if the current toggle state is equal to the default toggle state. + */ + get isEmpty() { + return this.current === this._toggledByDefault; + } +} diff --git a/lib/public/components/Filters/common/filters/checkboxFilter.js b/lib/public/components/Filters/common/filters/checkboxFilter.js index dcfcb4a95b..2cf550c091 100644 --- a/lib/public/components/Filters/common/filters/checkboxFilter.js +++ b/lib/public/components/Filters/common/filters/checkboxFilter.js @@ -14,32 +14,6 @@ import { h } from '/js/src/index.js'; -/** - * A general component for generating checkboxes. - * - * @param {string} name The general name of the element. - * @param {Array} values the list of options to display - * @param {function} isChecked true if the checkbox is checked, else false - * @param {function} onChange the handler called once the checkbox state changes (change event is passed as first parameter, value as second) - * @param {Object} [additionalProperties] Additional options that can be given to the class. - * @returns {vnode} An object that has one or multiple checkboxes. - * @deprecated use checkboxes - */ -export const checkboxFilter = (name, values, isChecked, onChange, additionalProperties) => - h('.flex-row.flex-wrap', values.map((value) => h('.form-check.flex-grow', [ - h('input.form-check-input', { - id: `${name}Checkbox${value}`, - class: name, - type: 'checkbox', - checked: isChecked(value), - onchange: (e) => onChange(e, value), - ...additionalProperties || {}, - }), - h('label.form-check-label', { - for: `${name}Checkbox${value}`, - }, value.toUpperCase()), - ]))); - /** * Display a filter composed of checkbox listing pre-defined options * @param {SelectionModel} selectionModel filter model diff --git a/lib/public/components/Filters/QcFlagTypesFilter/bad.js b/lib/public/components/Filters/common/filters/radioButtonFilter.js similarity index 72% rename from lib/public/components/Filters/QcFlagTypesFilter/bad.js rename to lib/public/components/Filters/common/filters/radioButtonFilter.js index 7378d18d0d..1b1d91d7ed 100644 --- a/lib/public/components/Filters/QcFlagTypesFilter/bad.js +++ b/lib/public/components/Filters/common/filters/radioButtonFilter.js @@ -11,16 +11,18 @@ * or submit itself to any jurisdiction. */ -import { radioButton } from '../../common/form/inputs/radioButton.js'; +import { radioButton } from '../../../common/form/inputs/radioButton.js'; import { h } from '/js/src/index.js'; /** - * Radiobutton filter for the qcFlag 'bad' filter - * @param {SelectionModel} selectionModel the a selectionmodel + * Radiobutton filter component + * + * @param {RadioSelectionModel} selectionModel the a selectionmodel + * @param {string} filterName the name of the filter * @return {vnode} A number of radiobuttons corresponding with the selection options */ -const badFilterRadioButtons = (selectionModel) => { - const name = 'badFilterRadio'; +const radioButtonFilter = (selectionModel, filterName) => { + const name = `${filterName}FilterRadio`; return h( '.form-group-header.flex-row.w-100', selectionModel.options.map((option) => { @@ -33,4 +35,4 @@ const badFilterRadioButtons = (selectionModel) => { ); }; -export default badFilterRadioButtons; +export default radioButtonFilter; diff --git a/lib/public/components/Filters/common/filters/toggleFilter.js b/lib/public/components/Filters/common/filters/toggleFilter.js new file mode 100644 index 0000000000..064ce2f0f9 --- /dev/null +++ b/lib/public/components/Filters/common/filters/toggleFilter.js @@ -0,0 +1,45 @@ +/** + * @license + * Copyright CERN and copyright holders of ALICE Trg. This software is + * distributed under the terms of the GNU General Public License v3 (GPL + * Version 3), copied verbatim in the file "COPYING". + * + * See http://alice-Trg.web.cern.ch/license for full licensing information. + * + * In applying this license CERN does not waive the privileges and immunities + * granted to it by virtue of its status as an Intergovernmental Organization + * or submit itself to any jurisdiction. + */ + +import { h } from '/js/src/index.js'; +import { switchInput } from '../../../common/form/switchInput.js'; +import { radioButton } from '../../../common/form/inputs/radioButton.js'; + +/** + * Display a toggle switch or radio buttons for toggle filters + * + * @param {ToggleFilterModel} toggleFilterModel a ToggleFilterModel + * @param {name} toggleFilterModel the name used to identify and label the filter + * @param {boolean} radioButtonMode define whether or not to return radio buttons or a switch. + * @returns {Component} the toggle switch + */ +export const toggleFilter = (toggleFilterModel, name, id, radioButtonMode = false) => { + if (radioButtonMode) { + return h('.form-group-header.flex-row.w-100', [ + radioButton({ + label: 'OFF', + isChecked: !toggleFilterModel.isToggled(), + action: () => toggleFilterModel.toggle(), + name, + }), + radioButton({ + label: 'ON', + isChecked: toggleFilterModel.isToggled(), + action: () => toggleFilterModel.toggle(), + name, + }), + ]); + } + + return switchInput(toggleFilterModel.isToggled(), () => toggleFilterModel.toggle(), { labelAfter: name, id }); +}; diff --git a/lib/public/components/common/form/switchInput.js b/lib/public/components/common/form/switchInput.js index ad7f7f8135..f06cb5154a 100644 --- a/lib/public/components/common/form/switchInput.js +++ b/lib/public/components/common/form/switchInput.js @@ -32,7 +32,7 @@ import { h } from '/js/src/index.js'; * @return {Component} the switch component */ export const switchInput = (value, onChange, options) => { - const { key, labelAfter, labelBefore, color } = options || {}; + const { key, labelAfter, labelBefore, color, id } = options || {}; const attributes = { ...key ? { key } : {} }; return h( @@ -40,7 +40,7 @@ export const switchInput = (value, onChange, options) => { attributes, [ labelBefore, - h('.switch', [ + h('.switch', { id }, [ h('input', { onchange: (e) => onChange(e.target.checked), type: 'checkbox', diff --git a/lib/public/views/LhcFills/ActiveColumns/lhcFillsActiveColumns.js b/lib/public/views/LhcFills/ActiveColumns/lhcFillsActiveColumns.js index b2657c8cfd..6d2968b1c8 100644 --- a/lib/public/views/LhcFills/ActiveColumns/lhcFillsActiveColumns.js +++ b/lib/public/views/LhcFills/ActiveColumns/lhcFillsActiveColumns.js @@ -23,7 +23,7 @@ import { buttonLinkWithDropdown } from '../../../components/common/selection/inf import { infologgerLinksComponents } from '../../../components/common/externalLinks/infologgerLinksComponents.js'; import { formatBeamType } from '../../../utilities/formatting/formatBeamType.js'; import { frontLink } from '../../../components/common/navigation/frontLink.js'; -import { toggleStableBeamOnlyFilter } from '../../../components/Filters/LhcFillsFilter/stableBeamFilter.js'; +import { toggleFilter } from '../../../components/Filters/common/filters/toggleFilter.js'; import { fillNumberFilter } from '../../../components/Filters/LhcFillsFilter/fillNumberFilter.js'; import { durationFilter } from '../../../components/Filters/LhcFillsFilter/durationFilter.js'; import { beamTypeFilter } from '../../../components/Filters/LhcFillsFilter/beamTypeFilter.js'; @@ -111,7 +111,7 @@ export const lhcFillsActiveColumns = { name: 'Stable Beams Only', visible: false, format: (boolean) => boolean ? 'On' : 'Off', - filter: (lhcFillModel) => toggleStableBeamOnlyFilter(lhcFillModel.filteringModel.get('hasStableBeams'), true), + filter: (lhcFillModel) => toggleFilter(lhcFillModel.filteringModel.get('hasStableBeams'), 'stableBeamsOnlyRadio', true), }, stableBeamsDuration: { name: 'SB Duration', diff --git a/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js b/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js index c57ae69c25..8e292f9fbf 100644 --- a/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js +++ b/lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js @@ -13,13 +13,13 @@ import { buildUrl } from '/js/src/index.js'; import { FilteringModel } from '../../../components/Filters/common/FilteringModel.js'; -import { StableBeamFilterModel } from '../../../components/Filters/LhcFillsFilter/StableBeamFilterModel.js'; import { RawTextFilterModel } from '../../../components/Filters/common/filters/RawTextFilterModel.js'; import { OverviewPageModel } from '../../../models/OverviewModel.js'; import { addStatisticsToLhcFill } from '../../../services/lhcFill/addStatisticsToLhcFill.js'; import { BeamTypeFilterModel } from '../../../components/Filters/LhcFillsFilter/BeamTypeFilterModel.js'; import { TextComparisonFilterModel } from '../../../components/Filters/common/filters/TextComparisonFilterModel.js'; import { TimeRangeFilterModel } from '../../../components/Filters/RunsFilter/TimeRangeFilter.js'; +import { ToggleFilterModel } from '../../../components/Filters/common/filters/ToggleFilterModel.js'; /** * Model for the LHC fills overview page @@ -39,7 +39,7 @@ export class LhcFillsOverviewModel extends OverviewPageModel { fillNumbers: new RawTextFilterModel(), beamDuration: new TextComparisonFilterModel(), runDuration: new TextComparisonFilterModel(), - hasStableBeams: new StableBeamFilterModel(), + hasStableBeams: new ToggleFilterModel(stableBeamsOnly), stableBeamsStart: new TimeRangeFilterModel(), stableBeamsEnd: new TimeRangeFilterModel(), beamTypes: new BeamTypeFilterModel(), @@ -50,10 +50,6 @@ export class LhcFillsOverviewModel extends OverviewPageModel { this._filteringModel.visualChange$.bubbleTo(this); this.reset(false); - - if (stableBeamsOnly) { - this._filteringModel.get('hasStableBeams').setStableBeamsOnly(true); - } } /** diff --git a/lib/public/views/LhcFills/Overview/index.js b/lib/public/views/LhcFills/Overview/index.js index e81409f06c..a29abf5145 100644 --- a/lib/public/views/LhcFills/Overview/index.js +++ b/lib/public/views/LhcFills/Overview/index.js @@ -18,7 +18,7 @@ import { lhcFillsActiveColumns } from '../ActiveColumns/lhcFillsActiveColumns.js import { estimateDisplayableRowsCount } from '../../../utilities/estimateDisplayableRowsCount.js'; import { paginationComponent } from '../../../components/Pagination/paginationComponent.js'; import { filtersPanelPopover } from '../../../components/Filters/common/filtersPanelPopover.js'; -import { toggleStableBeamOnlyFilter } from '../../../components/Filters/LhcFillsFilter/stableBeamFilter.js'; +import { toggleFilter } from '../../../components/Filters/common/filters/toggleFilter.js'; const TABLEROW_HEIGHT = 53.3; // Estimate of the navbar and pagination elements height total; Needs to be updated in case of changes; @@ -50,7 +50,7 @@ const showLhcFillsTable = (lhcFillsOverviewModel) => { return [ h('.flex-row.header-container.g2.pv2', [ filtersPanelPopover(lhcFillsOverviewModel, lhcFillsActiveColumns), - toggleStableBeamOnlyFilter(lhcFillsOverviewModel.filteringModel.get('hasStableBeams')), + toggleFilter(lhcFillsOverviewModel.filteringModel.get('hasStableBeams'), 'STABLE BEAM ONLY'), ]), h('.w-100.flex-column', [ table(lhcFillsOverviewModel.items, lhcFillsActiveColumns, null, { tableClasses: '.table-sm' }), diff --git a/lib/public/views/QcFlagTypes/ActiveColumns/qcFlagTypesActiveColumns.js b/lib/public/views/QcFlagTypes/ActiveColumns/qcFlagTypesActiveColumns.js index 9bed5b35a6..af1c8fe385 100644 --- a/lib/public/views/QcFlagTypes/ActiveColumns/qcFlagTypesActiveColumns.js +++ b/lib/public/views/QcFlagTypes/ActiveColumns/qcFlagTypesActiveColumns.js @@ -15,7 +15,7 @@ import { h } from '/js/src/index.js'; import { formatTimestamp } from '../../../utilities/formatting/formatTimestamp.js'; import { textFilter } from '../../../components/Filters/common/filters/textFilter.js'; import { qcFlagTypeColoredBadge } from '../../../components/qcFlags/qcFlagTypeColoredBadge.js'; -import badFilterRadioButtons from '../../../components/Filters/QcFlagTypesFilter/bad.js'; +import radioButtonFilter from '../../../components/Filters/common/filters/radioButtonFilter.js'; /** * List of active columns for a QC Flag Types table @@ -54,7 +54,7 @@ export const qcFlagTypesActiveColumns = { name: 'Bad', visible: true, sortable: true, - filter: ({ filteringModel }) => badFilterRadioButtons(filteringModel.get('bad')), + filter: ({ filteringModel }) => radioButtonFilter(filteringModel.get('bad'), 'bad'), classes: 'f6 w-5', format: (bad) => bad ? h('.danger', 'Yes') : h('.success', 'No'), }, diff --git a/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js b/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js index cc4ced6716..72abce90f5 100644 --- a/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js +++ b/lib/public/views/QcFlagTypes/Overview/QcFlagTypesOverviewModel.js @@ -13,9 +13,9 @@ import { TextTokensFilterModel } from '../../../components/Filters/common/filters/TextTokensFilterModel.js'; import { OverviewPageModel } from '../../../models/OverviewModel.js'; -import { SelectionModel } from '../../../components/common/selection/SelectionModel.js'; import { buildUrl } from '/js/src/index.js'; import { FilteringModel } from '../../../components/Filters/common/FilteringModel.js'; +import { RadioButtonFilterModel } from '../../../components/Filters/common/RadioButtonFilterModel.js'; /** * QcFlagTypesOverviewModel @@ -30,12 +30,7 @@ export class QcFlagTypesOverviewModel extends OverviewPageModel { this._filteringModel = new FilteringModel({ names: new TextTokensFilterModel(), methods: new TextTokensFilterModel(), - bad: new SelectionModel({ - availableOptions: [{ label: 'Any' }, { label: 'Bad', value: true }, { label: 'Not Bad', value: false }], - defaultSelection: [{ label: 'Any' }], - allowEmpty: false, - multiple: false, - }), + bad: new RadioButtonFilterModel([{ label: 'Any' }, { label: 'Bad', value: true }, { label: 'Not Bad', value: false }]), }); this._filteringModel.observe(() => { diff --git a/lib/public/views/Runs/ActiveColumns/runDetectorsAsyncQcActiveColumns.js b/lib/public/views/Runs/ActiveColumns/runDetectorsAsyncQcActiveColumns.js index f4497010c4..b2069d0886 100644 --- a/lib/public/views/Runs/ActiveColumns/runDetectorsAsyncQcActiveColumns.js +++ b/lib/public/views/Runs/ActiveColumns/runDetectorsAsyncQcActiveColumns.js @@ -161,7 +161,7 @@ export const createRunDetectorsAsyncQcActiveColumns = ( visible: false, profiles: profile, filter: (filteringModel) => { - const filterModel = filteringModel.get(`detectorsQc[_${dplDetectorId}][notBadFraction]`); + const filterModel = filteringModel.get('detectorsQc').getFilter(`_${dplDetectorId}`).getFilter('notBadFraction'); return filterModel ? numericalComparisonFilter(filterModel, { step: 0.1, selectorPrefix: `detectorsQc-for-${dplDetectorId}-notBadFraction` }) : null; diff --git a/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js b/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js index eefe0f006f..e0d5110557 100644 --- a/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js +++ b/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js @@ -14,9 +14,6 @@ import { CopyToClipboardComponent, h } from '/js/src/index.js'; import { runNumbersFilter } from '../../../components/Filters/RunsFilter/runNumbersFilter.js'; import { displayRunEorReasonsOverview } from '../format/displayRunEorReasonOverview.js'; -import ddflpFilter from '../../../components/Filters/RunsFilter/ddflp.js'; -import dcsFilter from '../../../components/Filters/RunsFilter/dcs.js'; -import epnFilter from '../../../components/Filters/RunsFilter/epn.js'; import { formatTimestamp } from '../../../utilities/formatting/formatTimestamp.js'; import { displayRunDuration } from '../format/displayRunDuration.js'; import { frontLink } from '../../../components/common/navigation/frontLink.js'; @@ -47,7 +44,7 @@ import { timeRangeFilter } from '../../../components/Filters/common/filters/time import { rawTextFilter } from '../../../components/Filters/common/filters/rawTextFilter.js'; import { numericalComparisonFilter } from '../../../components/Filters/common/filters/numericalComparisonFilter.js'; import { checkboxes } from '../../../components/Filters/common/filters/checkboxFilter.js'; -import { triggerValueFilter } from '../../../components/Filters/RunsFilter/triggerValueFilter.js'; +import radioButtonFilter from '../../../components/Filters/common/filters/radioButtonFilter.js'; /** * List of active columns for a generic runs table @@ -525,7 +522,7 @@ export const runsActiveColumns = { classes: 'w-2 f6 w-wrapped', format: (boolean) => boolean ? 'On' : 'Off', exportFormat: (boolean) => boolean ? 'On' : 'Off', - filter: ddflpFilter, + filter: ({ filteringModel }) => radioButtonFilter(filteringModel.get('ddflp'), 'ddFlp'), }, dcs: { name: 'DCS', @@ -534,14 +531,21 @@ export const runsActiveColumns = { classes: 'w-2 f6 w-wrapped', format: (boolean) => boolean ? 'On' : 'Off', exportFormat: (boolean) => boolean ? 'On' : 'Off', - filter: dcsFilter, + filter: ({ filteringModel }) => radioButtonFilter(filteringModel.get('dcs'), 'dcs'), }, triggerValue: { name: 'TRG', visible: true, profiles: [profiles.none, 'lhcFill', 'environment'], classes: 'w-5 f6 w-wrapped', - filter: triggerValueFilter, + + /** + * TriggerValue filter component + * + * @param {RunsOverviewModel} runsOverviewModel the runs overview model + * @return {Component} the trigger value filter component + */ + filter: ({ filteringModel }) => checkboxes(filteringModel.get('triggerValues'), { selector: 'triggerValue' }), format: (trgValue) => trgValue ? trgValue : '-', }, epn: { @@ -551,7 +555,7 @@ export const runsActiveColumns = { classes: 'w-2 f6 w-wrapped', format: (boolean) => boolean ? 'On' : 'Off', exportFormat: (boolean) => boolean ? 'On' : 'Off', - filter: epnFilter, + filter: ({ filteringModel }) => radioButtonFilter(filteringModel.get('epn'), 'epn'), }, epnTopology: { name: 'EPN Topology', diff --git a/lib/public/views/Runs/Overview/RunsOverviewModel.js b/lib/public/views/Runs/Overview/RunsOverviewModel.js index 0249c66085..b5c400e5d2 100644 --- a/lib/public/views/Runs/Overview/RunsOverviewModel.js +++ b/lib/public/views/Runs/Overview/RunsOverviewModel.js @@ -36,6 +36,9 @@ import { DataExportModel } from '../../../models/DataExportModel.js'; import { runsActiveColumns as dataExportConfiguration } from '../ActiveColumns/runsActiveColumns.js'; import { BeamModeFilterModel } from '../../../components/Filters/RunsFilter/BeamModeFilterModel.js'; import { beamModesProvider } from '../../../services/beamModes/beamModesProvider.js'; +import { RadioButtonFilterModel } from '../../../components/Filters/common/RadioButtonFilterModel.js'; +import { SelectionModel } from '../../../components/common/selection/SelectionModel.js'; +import { TRIGGER_VALUES } from '../../../domain/enums/TriggerValue.js'; /** * Model representing handlers for runs page @@ -90,6 +93,10 @@ export class RunsOverviewModel extends OverviewPageModel { inelasticInteractionRateAtStart: new NumericalComparisonFilterModel(), inelasticInteractionRateAtMid: new NumericalComparisonFilterModel(), inelasticInteractionRateAtEnd: new NumericalComparisonFilterModel(), + ddflp: new RadioButtonFilterModel([{ label: 'ANY' }, { label: 'ON', value: true }, { label: 'OFF', value: false }]), + dcs: new RadioButtonFilterModel([{ label: 'ANY' }, { label: 'ON', value: true }, { label: 'OFF', value: false }]), + epn: new RadioButtonFilterModel([{ label: 'ANY' }, { label: 'ON', value: true }, { label: 'OFF', value: false }]), + triggerValues: new SelectionModel({ availableOptions: TRIGGER_VALUES.map((value) => ({ label: value, value })) }), }); this._filteringModel.observe(() => this._applyFilters(true)); @@ -123,7 +130,7 @@ export class RunsOverviewModel extends OverviewPageModel { * @inheritdoc */ getRootEndpoint() { - return buildUrl('/api/runs', { ...this._getFilterQueryParams(), ...{ filter: this.filteringModel.normalized } }); + return buildUrl('/api/runs', { filter: this.filteringModel.normalized }); } /** @@ -145,14 +152,6 @@ export class RunsOverviewModel extends OverviewPageModel { resetFiltering(fetch = true) { this._filteringModel.reset(); - this._triggerValuesFilters = new Set(); - - this.ddflpFilter = ''; - - this.dcsFilter = ''; - - this.epnFilter = ''; - if (fetch) { this._applyFilters(true); } @@ -163,11 +162,7 @@ export class RunsOverviewModel extends OverviewPageModel { * @return {Boolean} If any filter is active */ isAnyFilterActive() { - return this._filteringModel.isAnyFilterActive() - || this._triggerValuesFilters.size !== 0 - || this.ddflpFilter !== '' - || this.dcsFilter !== '' - || this.epnFilter !== ''; + return this._filteringModel.isAnyFilterActive(); } /** @@ -179,130 +174,6 @@ export class RunsOverviewModel extends OverviewPageModel { return this._filteringModel; } - /** - * Getter for the trigger values filter Set - * @return {Set} set of trigger filter values - */ - get triggerValuesFilters() { - return this._triggerValuesFilters; - } - - /** - * Setter for trigger values filter, this replaces the current set - * @param {Array} newTriggerValues new Set of values - * @return {undefined} - */ - set triggerValuesFilters(newTriggerValues) { - this._triggerValuesFilters = new Set(newTriggerValues); - this._applyFilters(); - } - - /** - * Returns the boolean of ddflp - * @return {Boolean} if ddflp is on - */ - getDdflpFilterOperation() { - return this.ddflpFilter; - } - - /** - * Sets the boolean of the filter if no new inputs were detected for 200 milliseconds - * @param {boolean} operation if the ddflp is on - * @return {undefined} - */ - setDdflpFilterOperation(operation) { - this.ddflpFilter = operation; - this._applyFilters(); - } - - /** - * Unchecks the ddflp checkbox and fetches all the runs. - * @return {undefined} - * - */ - removeDdflp() { - this.ddflpFilter = ''; - this._applyFilters(); - } - - /** - * Returns the boolean of dcs - * @return {Boolean} if dcs is on - */ - getDcsFilterOperation() { - return this.dcsFilter; - } - - /** - * Sets the boolean of the filter if no new inputs were detected for 200 milliseconds - * @param {boolean} operation if the dcs is on - * @return {undefined} - */ - setDcsFilterOperation(operation) { - this.dcsFilter = operation; - this._applyFilters(); - } - - /** - * Unchecks the dcs checkbox and fetches all the runs. - * @return {undefined} - */ - removeDcs() { - this.dcsFilter = ''; - this._applyFilters(); - } - - /** - * Returns the boolean of epn - * @return {Boolean} if epn is on - */ - getEpnFilterOperation() { - return this.epnFilter; - } - - /** - * Sets the boolean of the filter if no new inputs were detected for 200 milliseconds - * @param {boolean} operation if the epn is on - * @return {undefined} - */ - setEpnFilterOperation(operation) { - this.epnFilter = operation; - this._applyFilters(); - } - - /** - * Unchecks the epn checkbox and fetches all the runs. - * @return {undefined} - */ - removeEpn() { - this.epnFilter = ''; - this._applyFilters(); - } - - /** - * Returns the list of URL params corresponding to the currently applied filter - * - * @return {Object} the URL params - * - * @private - */ - _getFilterQueryParams() { - return { - ...this._triggerValuesFilters.size !== 0 && { - 'filter[triggerValues]': Array.from(this._triggerValuesFilters).join(), - }, - ...(this.ddflpFilter === true || this.ddflpFilter === false) && { - 'filter[ddflp]': this.ddflpFilter, - }, - ...(this.dcsFilter === true || this.dcsFilter === false) && { - 'filter[dcs]': this.dcsFilter, - }, - ...(this.epnFilter === true || this.epnFilter === false) && { - 'filter[epn]': this.epnFilter, - }, - }; - } - /** * Apply the current filtering and update the remote data list * diff --git a/lib/public/views/Runs/Overview/RunsWithQcModel.js b/lib/public/views/Runs/Overview/RunsWithQcModel.js index ad09ea4718..5fec9ad7e2 100644 --- a/lib/public/views/Runs/Overview/RunsWithQcModel.js +++ b/lib/public/views/Runs/Overview/RunsWithQcModel.js @@ -43,6 +43,8 @@ const qcFlagsExportConfigurationFactory = (detectors) => Object.fromEntries(dete import { ObservableData } from '../../../utilities/ObservableData.js'; import { DetectorType } from '../../../domain/enums/DetectorTypes.js'; import { mergeRemoteData } from '../../../utilities/mergeRemoteData.js'; +import { ToggleFilterModel } from '../../../components/Filters/common/filters/ToggleFilterModel.js'; +import { MultiCompositionFilterModel } from '../../../components/Filters/RunsFilter/MultiCompositionFilterModel.js'; /** * Merge QC summaries @@ -70,7 +72,11 @@ export class RunsWithQcModel extends RunsOverviewModel { constructor(model) { super(model); - this._mcReproducibleAsNotBad = false; + this._mcReproducibleAsNotBad = new ToggleFilterModel(); + + this.mcReproducibleAsNotBad.observe(() => { + this.load(); + }); this._runDetectorsSelectionModel = new RunDetectorsSelectionModel(); this._runDetectorsSelectionModel.bubbleTo(this); @@ -83,35 +89,21 @@ export class RunsWithQcModel extends RunsOverviewModel { verticalScrollEnabled: true, freezeFirstColumn: true, }); + + this._filteringModel.put('detectorsQc', new MultiCompositionFilterModel({ mcReproducibleAsNotBad: this._mcReproducibleAsNotBad })); } /** * @inheritdoc */ getRootEndpoint() { - const filter = {}; - filter.detectorsQc = { - mcReproducibleAsNotBad: this._mcReproducibleAsNotBad, - }; - - return buildUrl(super.getRootEndpoint(), { filter, include: { effectiveQcFlags: true } }); - } - - /** - * Set mcReproducibleAsNotBad - * - * @param {boolean} mcReproducibleAsNotBad new value - * @return {void} - */ - setMcReproducibleAsNotBad(mcReproducibleAsNotBad) { - this._mcReproducibleAsNotBad = mcReproducibleAsNotBad; - this.load(); + return buildUrl(super.getRootEndpoint(), { include: { effectiveQcFlags: true } }); } /** * Get mcReproducibleAsNotBad * - * @return {boolean} mcReproducibleAsNotBad + * @return {ToggleFilterModel} mcReproducibleAsNotBad */ get mcReproducibleAsNotBad() { return this._mcReproducibleAsNotBad; @@ -141,13 +133,15 @@ export class RunsWithQcModel extends RunsOverviewModel { * @param {ObservableData>} detectors$ detectors remote data observable */ registerDetectorsNotBadFractionFilterModels(detectors$) { + const detectorsQc = this._filteringModel.get('detectorsQc'); + detectors$.observe((observableData) => observableData.getCurrent().apply({ - Success: (detectors) => detectors.forEach(({ id }) => { - this._filteringModel.put(`detectorsQc[_${id}][notBadFraction]`, new NumericalComparisonFilterModel({ - scale: 0.01, - integer: false, - })); - }), + Success: (detectors) => + detectors.forEach(({ id }) => + detectorsQc.putFilter( + `_${id}`, // This should probably be changed to a flat name in the future + new MultiCompositionFilterModel({ notBadFraction: new NumericalComparisonFilterModel({ scale: 0.01, integer: false }) }), + )), })); } @@ -218,7 +212,7 @@ export class RunsWithQcModel extends RunsOverviewModel { detectorIds: detectors .filter(({ type }) => type === DetectorType.PHYSICAL) .map(({ id }) => id).join(','), - mcReproducibleAsNotBad: this._mcReproducibleAsNotBad, + mcReproducibleAsNotBad: this._mcReproducibleAsNotBad.isToggled(), })); const { data: qcSummary2 } = await getRemoteData(buildUrl('/api/qcFlags/summary', { @@ -232,7 +226,7 @@ export class RunsWithQcModel extends RunsOverviewModel { operator: 'none', }, }, - mcReproducibleAsNotBad: this._mcReproducibleAsNotBad, + mcReproducibleAsNotBad: this._mcReproducibleAsNotBad.isToggled(), })); this._qcSummary$.setCurrent(RemoteData.success(mergeQcSummaries([qcSummary1, qcSummary2]))); } catch (error) { diff --git a/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewModel.js b/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewModel.js index 4e602e4ef1..9928055705 100644 --- a/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewModel.js +++ b/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewModel.js @@ -19,11 +19,11 @@ import { FixedPdpBeamTypeRunsOverviewModel } from '../Overview/FixedPdpBeamTypeR import { jsonPatch } from '../../../utilities/fetch/jsonPatch.js'; import { jsonPut } from '../../../utilities/fetch/jsonPut.js'; import { SkimmingStage } from '../../../domain/enums/SkimmingStage.js'; -import { NumericalComparisonFilterModel } from '../../../components/Filters/common/filters/NumericalComparisonFilterModel.js'; import { jsonFetch } from '../../../utilities/fetch/jsonFetch.js'; import { mergeRemoteData } from '../../../utilities/mergeRemoteData.js'; import { RemoteDataSource } from '../../../utilities/fetch/RemoteDataSource.js'; import { DetectorType } from '../../../domain/enums/DetectorTypes.js'; +import { GaqFilterModel } from '../../../components/Filters/RunsFilter/GaqFilterModel.js'; const ALL_CPASS_PRODUCTIONS_REGEX = /cpass\d+/; const DETECTOR_NAMES_NOT_IN_CPASSES = ['EVS']; @@ -68,10 +68,7 @@ export class RunsPerDataPassOverviewModel extends FixedPdpBeamTypeRunsOverviewMo this._skimmableRuns$ = new ObservableData(RemoteData.notAsked()); this._skimmableRuns$.bubbleTo(this); - this._filteringModel.put('gaq[notBadFraction]', new NumericalComparisonFilterModel({ - scale: 0.01, - integer: false, - })); + this._filteringModel.put('gaq', new GaqFilterModel(this._mcReproducibleAsNotBad)); this._freezeOrUnfreezeActionState$ = new ObservableData(RemoteData.notAsked()); this._freezeOrUnfreezeActionState$.bubbleTo(this); @@ -142,14 +139,7 @@ export class RunsPerDataPassOverviewModel extends FixedPdpBeamTypeRunsOverviewMo * @inheritdoc */ getRootEndpoint() { - const gaqNotBadFilter = this._filteringModel.get('gaq[notBadFraction]'); - const filter = { dataPassIds: [this._dataPassId] }; - if (!gaqNotBadFilter.isEmpty) { - filter.gaq = { - mcReproducibleAsNotBad: this._mcReproducibleAsNotBad, - }; - } - + const filter = { ...this._filteringModel.normalized, dataPassIds: [this._dataPassId] }; return buildUrl(super.getRootEndpoint(), { filter }); } @@ -363,7 +353,7 @@ export class RunsPerDataPassOverviewModel extends FixedPdpBeamTypeRunsOverviewMo }); const url = buildUrl('/api/qcFlags/summary/gaq', { dataPassId: this._dataPassId, - mcReproducibleAsNotBad: this._mcReproducibleAsNotBad, + mcReproducibleAsNotBad: this._mcReproducibleAsNotBad.isToggled(), runNumber: runNumber, }); await this._gaqSummarySources[runNumber].fetch(url); diff --git a/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js b/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js index 8f63fb608b..dda853f084 100644 --- a/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js +++ b/lib/public/views/Runs/RunPerDataPass/RunsPerDataPassOverviewPage.js @@ -38,7 +38,7 @@ import { iconCaretBottom } from '/js/src/icons.js'; import { BkpRoles } from '../../../domain/enums/BkpRoles.js'; import { getInelasticInteractionRateColumns } from '../ActiveColumns/getInelasticInteractionRateActiveColumns.js'; import { exportTriggerAndModal } from '../../../components/common/dataExport/exportTriggerAndModal.js'; -import { mcReproducibleAsNotBadToggle } from '../mcReproducibleAsNotBadToggle.js'; +import { toggleFilter } from '../../../components/Filters/common/filters/toggleFilter.js'; const TABLEROW_HEIGHT = 59; // Estimate of the navbar and pagination elements height total; Needs to be updated in case of changes; @@ -191,7 +191,7 @@ export const RunsPerDataPassOverviewPage = ({ }); }, filter: ({ filteringModel }) => numericalComparisonFilter( - filteringModel.get('gaq[notBadFraction]'), + filteringModel.get('gaq').notBadFraction, { step: 0.1, selectorPrefix: 'gaqNotBadFraction' }, ), filterTooltip: 'not-bad fraction expressed as a percentage', @@ -229,10 +229,7 @@ export const RunsPerDataPassOverviewPage = ({ )), ]), ), - mcReproducibleAsNotBadToggle( - mcReproducibleAsNotBad, - () => perDataPassOverviewModel.setMcReproducibleAsNotBad(!mcReproducibleAsNotBad), - ), + toggleFilter(mcReproducibleAsNotBad, h('em', 'MC.R as not-bad'), 'mcReproducibleAsNotBadToggle'), h('.mlauto', qcSummaryLegendTooltip()), h('#actions-dropdown-button', DropdownComponent( h('button.btn.btn-primary', h('.flex-row.g2', ['Actions', iconCaretBottom()])), diff --git a/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewPage.js b/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewPage.js index 7526324b35..973500b106 100644 --- a/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewPage.js +++ b/lib/public/views/Runs/RunPerPeriod/RunsPerLhcPeriodOverviewPage.js @@ -27,8 +27,8 @@ import spinner from '../../../components/common/spinner.js'; import { getInelasticInteractionRateColumns } from '../ActiveColumns/getInelasticInteractionRateActiveColumns.js'; import { filtersPanelPopover } from '../../../components/Filters/common/filtersPanelPopover.js'; import { runNumbersFilter } from '../../../components/Filters/RunsFilter/runNumbersFilter.js'; -import { mcReproducibleAsNotBadToggle } from '../mcReproducibleAsNotBadToggle.js'; import { exportTriggerAndModal } from '../../../components/common/dataExport/exportTriggerAndModal.js'; +import { toggleFilter } from '../../../components/Filters/common/filters/toggleFilter.js'; const TABLEROW_HEIGHT = 62; // Estimate of the navbar and pagination elements height total; Needs to be updated in case of changes; @@ -113,10 +113,7 @@ export const RunsPerLhcPeriodOverviewPage = ({ runs: { perLhcPeriodOverviewModel filtersPanelPopover(perLhcPeriodOverviewModel, activeColumns, { profile: 'runsPerLhcPeriod' }), h('.pl2#runOverviewFilter', runNumbersFilter(perLhcPeriodOverviewModel.filteringModel.get('runNumbers'))), h('h2', `Good, physics runs of ${lhcPeriodStatistics.lhcPeriod.name}`), - mcReproducibleAsNotBadToggle( - mcReproducibleAsNotBad, - () => perLhcPeriodOverviewModel.setMcReproducibleAsNotBad(!mcReproducibleAsNotBad), - ), + toggleFilter(mcReproducibleAsNotBad, h('em', 'MC.R as not-bad'), 'mcReproducibleAsNotBadToggle'), exportTriggerAndModal(perLhcPeriodOverviewModel.exportModel, modalModel), ]), ...tabbedPanelComponent( diff --git a/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewPage.js b/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewPage.js index 55d4cdb988..5127c8df20 100644 --- a/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewPage.js +++ b/lib/public/views/Runs/RunsPerSimulationPass/RunsPerSimulationPassOverviewPage.js @@ -28,7 +28,7 @@ import { getInelasticInteractionRateColumns } from '../ActiveColumns/getInelasti import { exportTriggerAndModal } from '../../../components/common/dataExport/exportTriggerAndModal.js'; import { filtersPanelPopover } from '../../../components/Filters/common/filtersPanelPopover.js'; import { runNumbersFilter } from '../../../components/Filters/RunsFilter/runNumbersFilter.js'; -import { mcReproducibleAsNotBadToggle } from '../mcReproducibleAsNotBadToggle.js'; +import { toggleFilter } from '../../../components/Filters/common/filters/toggleFilter.js'; const TABLEROW_HEIGHT = 59; // Estimate of the navbar and pagination elements height total; Needs to be updated in case of changes; @@ -100,10 +100,7 @@ export const RunsPerSimulationPassOverviewPage = ({ '.flex-row.g1.items-center', breadcrumbs([commonTitle, h('h2#breadcrumb-simulation-pass-name', simulationPass.name)]), ), - mcReproducibleAsNotBadToggle( - mcReproducibleAsNotBad, - () => perSimulationPassOverviewModel.setMcReproducibleAsNotBad(!mcReproducibleAsNotBad), - ), + toggleFilter(mcReproducibleAsNotBad, h('em', 'MC.R as not-bad'), 'mcReproducibleAsNotBadToggle'), h('.mlauto', qcSummaryLegendTooltip()), exportTriggerAndModal(perSimulationPassOverviewModel.exportModel, modalModel, { autoMarginLeft: false }), frontLink( diff --git a/lib/public/views/Runs/mcReproducibleAsNotBadToggle.js b/lib/public/views/Runs/mcReproducibleAsNotBadToggle.js deleted file mode 100644 index 636ed0f245..0000000000 --- a/lib/public/views/Runs/mcReproducibleAsNotBadToggle.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @license - * Copyright CERN and copyright holders of ALICE O2. This software is - * distributed under the terms of the GNU General Public License v3 (GPL - * Version 3), copied verbatim in the file "COPYING". - * - * See http://alice-o2.web.cern.ch/license for full licensing information. - * - * In applying this license CERN does not waive the privileges and immunities - * granted to it by virtue of its status as an Intergovernmental Organization - * or submit itself to any jurisdiction. - */ - -import { switchInput } from '../../components/common/form/switchInput.js'; -import { h } from '/js/src/index.js'; - -/** - * Display a toggle switch to change interpretation of MC.Reproducible flag type from bad to not-bad - * - * @param {boolean} value current value - * @param {function} onChange to be called when switching - * @returns {Component} the toggle switch - */ -export const mcReproducibleAsNotBadToggle = (value, onChange) => h('#mcReproducibleAsNotBadToggle', switchInput( - value, - onChange, - { labelAfter: h('em', 'MC.R as not-bad') }, -)); diff --git a/test/public/runs/overview.test.js b/test/public/runs/overview.test.js index 807b821ffc..6adaac43de 100644 --- a/test/public/runs/overview.test.js +++ b/test/public/runs/overview.test.js @@ -600,7 +600,7 @@ module.exports = () => { it('Should successfully filter runs by their trigger value', async () => { await navigateToRunsOverview(page); - const filterInputSelectorPrefix = '#triggerValueCheckbox'; + const filterInputSelectorPrefix = '#triggerValue-checkbox-'; const offFilterSelector = `${filterInputSelectorPrefix}OFF`; const ltuFilterSelector = `${filterInputSelectorPrefix}LTU`; diff --git a/test/public/runs/runsPerDataPass.overview.test.js b/test/public/runs/runsPerDataPass.overview.test.js index 5eeef9c018..2ff7dfc4ac 100644 --- a/test/public/runs/runsPerDataPass.overview.test.js +++ b/test/public/runs/runsPerDataPass.overview.test.js @@ -523,8 +523,6 @@ module.exports = () => { it('should successfully apply gaqNotBadFraction filters', async () => { await navigateToRunsPerDataPass(page, 2, 1, 3); - await pressElement(page, '#openFilterToggle', true); - await page.waitForSelector('#gaqNotBadFraction-operator'); await page.select('#gaqNotBadFraction-operator', '<='); await fillInput(page, '#gaqNotBadFraction-operand', '80', ['change']); @@ -533,7 +531,6 @@ module.exports = () => { await pressElement(page, '#mcReproducibleAsNotBadToggle input', true); await expectColumnValues(page, 'runNumber', []); - await pressElement(page, '#openFilterToggle', true); await pressElement(page, '#reset-filters', true); await expectColumnValues(page, 'runNumber', ['108', '107', '106']); }); @@ -542,12 +539,8 @@ module.exports = () => { await page.waitForSelector('#detectorsQc-for-1-notBadFraction-operator'); await page.select('#detectorsQc-for-1-notBadFraction-operator', '<='); await fillInput(page, '#detectorsQc-for-1-notBadFraction-operand', '90', ['change']); - await expectColumnValues(page, 'runNumber', ['106']); - - await pressElement(page, '#mcReproducibleAsNotBadToggle input', true); await expectColumnValues(page, 'runNumber', ['107', '106']); - await pressElement(page, '#openFilterToggle', true); await pressElement(page, '#reset-filters', true); await expectColumnValues(page, 'runNumber', ['108', '107', '106']); });