From 355ddac9eb8c5511c8f0697b11375740b1a0c61e Mon Sep 17 00:00:00 2001 From: xsalonx Date: Thu, 14 Mar 2024 12:00:24 +0100 Subject: [PATCH 01/46] works --- .../Filters/RunsFilter/durationFilter.js | 69 ++++++++--- .../filters/comparisonOperatorFilter.js | 6 +- .../common/form/inputs/DurationInputModel.js | 107 ++++++++++++++++++ .../Runs/ActiveColumns/runsActiveColumns.js | 2 +- .../views/Runs/Overview/RunsOverviewModel.js | 32 ++---- 5 files changed, 174 insertions(+), 42 deletions(-) create mode 100644 lib/public/components/common/form/inputs/DurationInputModel.js diff --git a/lib/public/components/Filters/RunsFilter/durationFilter.js b/lib/public/components/Filters/RunsFilter/durationFilter.js index 1143ff71f2..c111ea789a 100644 --- a/lib/public/components/Filters/RunsFilter/durationFilter.js +++ b/lib/public/components/Filters/RunsFilter/durationFilter.js @@ -11,26 +11,61 @@ * or submit itself to any jurisdiction. */ -import { amountFilter } from '../common/filters/amountFilter.js'; +import { h } from '/js/src/index.js'; +import { comparisonOperatorFilter } from '../common/filters/comparisonOperatorFilter.js'; /** * Returns the run duration filter component * - * @param {RunsOverviewModel} runModel the runs model object - * + * @param {DurationInputModel} durationFilterModel the runs model object * @return {vnode} the duration filter */ -export const durationFilter = (runModel) => amountFilter( - runModel.runDurationFilter, - (filter) => { - runModel.runDurationFilter = filter; - }, - { - operatorAttributes: { - id: 'duration-operator', - }, - limitAttributes: { - id: 'duration-limit', - }, - }, -); +export const durationFilter = (durationFilterModel) => { + const { hours, minutes, seconds } = durationFilterModel.raw; + const inputs = [ + h('input.flex-grow', { + id: 'hours-input', + type: 'number', + min: 0, + value: hours, + oninput: (e) => { + const newHours = e.target.value; + if (!isNaN(newHours)) { + durationFilterModel.update({ hours: Number(newHours) }); + } + }, + }), + h('label', { for: 'hours-input' }, 'h'), + + h('input.flex-grow', { + id: 'minutes-input', + type: 'number', + min: 0, + value: minutes, + oninput: (e) => { + const newMinutes = e.target.value; + if (!isNaN(newMinutes)) { + durationFilterModel.update({ minutes: Number(newMinutes) }); + } + }, + }, 'm'), + h('label', { for: 'minutes-input' }, 'm'), + + h('input.flex-grow', { + id: 'seconds-input', + type: 'number', + min: 0, + value: seconds, + oninput: (e) => { + const newSeconds = e.target.value; + if (!isNaN(newSeconds)) { + durationFilterModel.update({ seconds: Number(newSeconds) }); + } + }, + }, 's'), + h('label', { for: 'seconds-input' }, 's'), + + ]; + + return comparisonOperatorFilter(inputs, durationFilterModel.operator, (operator) => durationFilterModel.operator = operator); +}; diff --git a/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js b/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js index d20dca35dc..a2bba69702 100644 --- a/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js +++ b/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js @@ -27,17 +27,17 @@ import { h } from '/js/src/index.js'; * Wrap a given input inside a comparison operator filter, prefixing the input by an operator selector * * @param {*} limitInput the input for the limit value (the one to wrap) - * @param {*} currentLimit the current limit value + * @param {*} currentOption the current option * @param {operatorChangeHandler} onOperatorChange callback called when the operator changes * @param {*} [operatorAttributes] the list of attributes to apply on the operator selector (note that value and * onchange attribute are override) * * @return {vnode} the filter component */ -export const comparisonOperatorFilter = (limitInput, currentLimit, onOperatorChange, operatorAttributes = {}) => h('.flex-row.g3', [ +export const comparisonOperatorFilter = (limitInput, currentOption, onOperatorChange, operatorAttributes = {}) => h('.flex-row.g3', [ h('', h('select.form-control', { ...operatorAttributes, - value: currentLimit, + value: currentOption, onchange: (e) => onOperatorChange(e.target.value), }, ['<', '<=', '=', '>=', '>'].map((operator) => h('option', { value: operator, diff --git a/lib/public/components/common/form/inputs/DurationInputModel.js b/lib/public/components/common/form/inputs/DurationInputModel.js new file mode 100644 index 0000000000..9032b250de --- /dev/null +++ b/lib/public/components/common/form/inputs/DurationInputModel.js @@ -0,0 +1,107 @@ +/** + * @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 { Observable } from '/js/src/index.js'; + +/** + * @typedef DurationInputRawData + * @property {number} days the number of days + * @property {number} hours the number of hours + * @property {number} minutes the number of minutes + * @property {number} seconds the number of seconds + */ + +/** + * Store the duration input + */ +export class DurationInputModel extends Observable { + /** + * Constructor + */ + constructor() { + super(); + + this._raw = { + hours: null, + minutes: null, + seconds: null, + }; + + /** + * Timestamp (ms) + * @type {number|null} + * @private + */ + this._value = null; + + this._operator = '='; + } + + get operator(){ + return this._operator; + } + + set operator(operator){ + this._operator = operator; + this.notify(); + } + + /** + * Update the inputs raw values + * @param {DurationInputRawData} raw the input raw values + * @return {void} + */ + update(raw) { + this._raw = { ...this._raw, ...raw }; + try { + this._value = + (this._raw.hours || 0) * 60 * 60 * 1000 + + (this._raw.minutes || 0) * 60 * 1000 + + (this._raw.seconds || 0) * 1000; + } catch (_) { + this._value = null; + } + + this.notify(); + } + + /** + * Reset the inputs to its initial state + * @return {void} + */ + clear() { + this._raw = { + hours: null, + minutes: null, + seconds: null, + }; + this._value = null; + this.notify(); + } + + /** + * Returns the raw input values + * @return {DurationInputRawData} the raw values + */ + get raw() { + return this._raw; + } + + /** + * Returns the current date represented by the inputs (null if no valid value is represented) + * @return {number|null} the current value + */ + get value() { + return this._value; + } +} diff --git a/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js b/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js index 3266433d7c..8f4ef3800a 100644 --- a/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js +++ b/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js @@ -262,7 +262,7 @@ export const runsActiveColumns = { noEllipsis: true, format: (_duration, run) => displayRunDuration(run), exportFormat: (_duration, run) => formatRunDuration(run), - filter: durationFilter, + filter: ({ runDurationFilterModel }) => durationFilter(runDurationFilterModel), }, environmentId: { name: 'Environment ID', diff --git a/lib/public/views/Runs/Overview/RunsOverviewModel.js b/lib/public/views/Runs/Overview/RunsOverviewModel.js index e1f4cf6951..8e8e90717f 100644 --- a/lib/public/views/Runs/Overview/RunsOverviewModel.js +++ b/lib/public/views/Runs/Overview/RunsOverviewModel.js @@ -21,6 +21,7 @@ import { EorReasonFilterModel } from '../../../components/Filters/RunsFilter/Eor import pick from '../../../utilities/pick.js'; import { OverviewPageModel } from '../../../models/OverviewModel.js'; import { getRemoteDataSlice } from '../../../utilities/fetch/getRemoteDataSlice.js'; +import { DurationInputModel } from '../../../components/common/form/inputs/DurationInputModel.js'; /** * Model representing handlers for runs page @@ -51,6 +52,9 @@ export class RunsOverviewModel extends OverviewPageModel { this._eorReasonsFilterModel.observe(() => this._applyFilters()); this._eorReasonsFilterModel.visualChange$.observe(() => this.notify()); + this._runDurationFilterModel = new DurationInputModel(); + this._runDurationFilterModel.observe(() => this._applyFilters()); + // Export items this._allRuns = RemoteData.NotAsked(); @@ -157,8 +161,6 @@ export class RunsOverviewModel extends OverviewPageModel { this.o2endFilterFromTime = '00:00'; this.o2endFilterToTime = '23:59'; - this._runDurationFilter = null; - this._lhcPeriodsFilter = null; this.environmentIdsFilter = ''; @@ -207,7 +209,7 @@ export class RunsOverviewModel extends OverviewPageModel { || this.o2endFilterTo !== '' || this.o2endFilterToTime !== '23:59' || this.o2endFilterFromTime !== '00:00' - || this._runDurationFilter !== null + || this._runDurationFilterModel.value !== null || this._lhcPeriodsFilter !== null || this.environmentIdsFilter !== '' || this.runQualitiesFilters.length !== 0 @@ -262,7 +264,7 @@ export class RunsOverviewModel extends OverviewPageModel { if (this.o2endFilterTo !== '') { this.activeFilters.push('O2 End to'); } - if (this._runDurationFilter !== null) { + if (this._runDurationFilterModel.value !== null) { this.activeFilters.push('Run duration'); } if (this._lhcPeriodsFilter !== null) { @@ -515,20 +517,8 @@ export class RunsOverviewModel extends OverviewPageModel { * Returns the run duration filter (filter is defined in minutes) * @return {{operator: string, limit: (number|null)}|null} The current run duration filter */ - get runDurationFilter() { - return this._runDurationFilter; - } - - /** - * Sets the limit of duration (in minutes) and the comparison operator to filter - * - * @param {{operator: string, limit: (number|null)}|null} newRunDurationFilter The new filter value - * - * @return {void} - */ - set runDurationFilter(newRunDurationFilter) { - this._runDurationFilter = newRunDurationFilter; - this._applyFilters(); + get runDurationFilterModel() { + return this._runDurationFilterModel; } /** @@ -895,10 +885,10 @@ export class RunsOverviewModel extends OverviewPageModel { 'filter[o2end][to]': new Date(`${this.o2endFilterTo.replace(/\//g, '-')}T${this.o2endFilterToTime}:59.999`).getTime(), }, - ...this._runDurationFilter && this._runDurationFilter.limit !== null && { - 'filter[runDuration][operator]': this._runDurationFilter.operator, + ...this._runDurationFilterModel.value && { + 'filter[runDuration][operator]': this._runDurationFilterModel.operator, // Convert filter to milliseconds - 'filter[runDuration][limit]': this._runDurationFilter.limit * 60 * 1000, + 'filter[runDuration][limit]': this._runDurationFilterModel.value, }, ...this._lhcPeriodsFilter && { 'filter[lhcPeriods]': this._lhcPeriodsFilter, From 9702196fdad77ba9d74da0812e8c546ea9bf82a2 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Thu, 14 Mar 2024 12:44:48 +0100 Subject: [PATCH 02/46] works --- .../Filters/RunsFilter/durationFilter.js | 90 ++++++++++--------- 1 file changed, 49 insertions(+), 41 deletions(-) diff --git a/lib/public/components/Filters/RunsFilter/durationFilter.js b/lib/public/components/Filters/RunsFilter/durationFilter.js index c111ea789a..e97ba22032 100644 --- a/lib/public/components/Filters/RunsFilter/durationFilter.js +++ b/lib/public/components/Filters/RunsFilter/durationFilter.js @@ -13,6 +13,7 @@ import { h } from '/js/src/index.js'; import { comparisonOperatorFilter } from '../common/filters/comparisonOperatorFilter.js'; +import { tooltip } from '../../common/popover/tooltip.js'; /** * Returns the run duration filter component @@ -22,50 +23,57 @@ import { comparisonOperatorFilter } from '../common/filters/comparisonOperatorFi */ export const durationFilter = (durationFilterModel) => { const { hours, minutes, seconds } = durationFilterModel.raw; - const inputs = [ - h('input.flex-grow', { - id: 'hours-input', - type: 'number', - min: 0, - value: hours, - oninput: (e) => { - const newHours = e.target.value; - if (!isNaN(newHours)) { - durationFilterModel.update({ hours: Number(newHours) }); - } - }, - }), - h('label', { for: 'hours-input' }, 'h'), - h('input.flex-grow', { - id: 'minutes-input', - type: 'number', - min: 0, - value: minutes, - oninput: (e) => { - const newMinutes = e.target.value; - if (!isNaN(newMinutes)) { - durationFilterModel.update({ minutes: Number(newMinutes) }); - } - }, - }, 'm'), - h('label', { for: 'minutes-input' }, 'm'), + const hoursInput = h('input.flex-grow', { + id: 'hours-input', + type: 'number', + min: 0, + value: hours, + oninput: (e) => { + const { value } = e.target; + durationFilterModel.update({ hours: value.length === 0 ? null : Number(value) }); + }, + }); + const minutesInput = h('input.flex-grow', { + id: 'minutes-input', + type: 'number', + min: 0, + value: minutes, + oninput: (e) => { + const { value } = e.target; + durationFilterModel.update({ minutes: value.length === 0 ? null : Number(value) }); + }, + }, 'm'); + const secondsInput = h('input.flex-grow', { + id: 'seconds-input', + type: 'number', + min: 0, + value: seconds, + oninput: (e) => { + const { value } = e.target; + durationFilterModel.update({ seconds: value.length === 0 ? null : Number(value) }); + }, + }, 's'); - h('input.flex-grow', { - id: 'seconds-input', - type: 'number', - min: 0, - value: seconds, - oninput: (e) => { - const newSeconds = e.target.value; - if (!isNaN(newSeconds)) { - durationFilterModel.update({ seconds: Number(newSeconds) }); - } - }, - }, 's'), - h('label', { for: 'seconds-input' }, 's'), + const inputs = h('.flex-row.w-40', [ - ]; + /* + * Tooltip(hoursInput, 'Hours'), + * tooltip(minutesInput, 'Minutes'), + * tooltip(secondsInput, 'Seconds'), + */ + hoursInput, + minutesInput, + secondsInput, + h('.flex-row.items-center.p2', [ + h('label', { for: 'hours-input' }, 'h'), + ':', + h('label', { for: 'minutes-input' }, 'm'), + ':', + h('label', { for: 'seconds-input' }, 's'), + ]), + ]); + // eslint-disable-next-line no-return-assign return comparisonOperatorFilter(inputs, durationFilterModel.operator, (operator) => durationFilterModel.operator = operator); }; From 4b87ce80a30daa05e4e28e50caab9d2acb6b6e91 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Thu, 14 Mar 2024 14:47:36 +0100 Subject: [PATCH 03/46] refactor --- .../Filters/RunsFilter/durationFilter.js | 17 +++----- ...NumericComparisonOperatorSelectionModel.js | 39 +++++++++++++++++++ .../filters/comparisonOperatorFilter.js | 17 ++++---- .../common/form/inputs/DurationInputModel.js | 11 ------ .../Runs/ActiveColumns/runsActiveColumns.js | 3 +- .../views/Runs/Overview/RunsOverviewModel.js | 19 +++++++-- 6 files changed, 69 insertions(+), 37 deletions(-) create mode 100644 lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js diff --git a/lib/public/components/Filters/RunsFilter/durationFilter.js b/lib/public/components/Filters/RunsFilter/durationFilter.js index e97ba22032..f5c72adb93 100644 --- a/lib/public/components/Filters/RunsFilter/durationFilter.js +++ b/lib/public/components/Filters/RunsFilter/durationFilter.js @@ -13,15 +13,15 @@ import { h } from '/js/src/index.js'; import { comparisonOperatorFilter } from '../common/filters/comparisonOperatorFilter.js'; -import { tooltip } from '../../common/popover/tooltip.js'; /** * Returns the run duration filter component * - * @param {DurationInputModel} durationFilterModel the runs model object - * @return {vnode} the duration filter + * @param {DurationInputModel} durationFilterModel the duration input model + * @param {NumericComparisonOperatorSelectionModel} operatorSelectionModel the comparison operator selection model + * @return {Component} the duration filter */ -export const durationFilter = (durationFilterModel) => { +export const durationFilter = (durationFilterModel, operatorSelectionModel) => { const { hours, minutes, seconds } = durationFilterModel.raw; const hoursInput = h('input.flex-grow', { @@ -56,12 +56,6 @@ export const durationFilter = (durationFilterModel) => { }, 's'); const inputs = h('.flex-row.w-40', [ - - /* - * Tooltip(hoursInput, 'Hours'), - * tooltip(minutesInput, 'Minutes'), - * tooltip(secondsInput, 'Seconds'), - */ hoursInput, minutesInput, secondsInput, @@ -74,6 +68,5 @@ export const durationFilter = (durationFilterModel) => { ]), ]); - // eslint-disable-next-line no-return-assign - return comparisonOperatorFilter(inputs, durationFilterModel.operator, (operator) => durationFilterModel.operator = operator); + return comparisonOperatorFilter(inputs, operatorSelectionModel); }; diff --git a/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js b/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js new file mode 100644 index 0000000000..8cc047ae53 --- /dev/null +++ b/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js @@ -0,0 +1,39 @@ +/** + * @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 { SelectionDropdownModel } from '../../../common/selection/dropdown/SelectionDropdownModel.js'; + +/** + * Model storing state of a selection of predfined numerical comparison operators + */ +export class NumericComparisonOperatorSelectionModel extends SelectionDropdownModel { + /** + * Constructor + */ + constructor() { + super({ + availableOptions: ['<', '<=', '=', '>=', '>'].map((op) => ({ value: op })), + multiple: false, + allowEmpty: false, + defaultSelection: [{ value: '>=' }], + }); + } + + // eslint-disable-next-line valid-jsdoc + /** + * @inheritDoc + */ + get selected() { + return super.selected[0]; + } +} diff --git a/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js b/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js index a2bba69702..a9638ff103 100644 --- a/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js +++ b/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js @@ -26,20 +26,19 @@ import { h } from '/js/src/index.js'; /** * Wrap a given input inside a comparison operator filter, prefixing the input by an operator selector * - * @param {*} limitInput the input for the limit value (the one to wrap) - * @param {*} currentOption the current option - * @param {operatorChangeHandler} onOperatorChange callback called when the operator changes + * @param {Compoenent} operandInput the input for the limit value (the one to wrap) + * @param {NumericComparisonOperatorSelectionModel} operatorSelectionModel the operator selection model * @param {*} [operatorAttributes] the list of attributes to apply on the operator selector (note that value and * onchange attribute are override) * * @return {vnode} the filter component */ -export const comparisonOperatorFilter = (limitInput, currentOption, onOperatorChange, operatorAttributes = {}) => h('.flex-row.g3', [ +export const comparisonOperatorFilter = (operandInput, operatorSelectionModel, operatorAttributes = {}) => h('.flex-row.g3', [ h('', h('select.form-control', { ...operatorAttributes, - value: currentOption, - onchange: (e) => onOperatorChange(e.target.value), - }, ['<', '<=', '=', '>=', '>'].map((operator) => h('option', { - value: operator, - }, operator)))), limitInput, + value: operatorSelectionModel.selected, + onchange: (e) => operatorSelectionModel.select(e.target.value), + }, operatorSelectionModel.options?.map(({ value }) => h('option', { + value, + }, value)))), operandInput, ]); diff --git a/lib/public/components/common/form/inputs/DurationInputModel.js b/lib/public/components/common/form/inputs/DurationInputModel.js index 9032b250de..515fd2ede6 100644 --- a/lib/public/components/common/form/inputs/DurationInputModel.js +++ b/lib/public/components/common/form/inputs/DurationInputModel.js @@ -43,17 +43,6 @@ export class DurationInputModel extends Observable { * @private */ this._value = null; - - this._operator = '='; - } - - get operator(){ - return this._operator; - } - - set operator(operator){ - this._operator = operator; - this.notify(); } /** diff --git a/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js b/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js index 8f4ef3800a..bbd7b9bec7 100644 --- a/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js +++ b/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js @@ -262,7 +262,8 @@ export const runsActiveColumns = { noEllipsis: true, format: (_duration, run) => displayRunDuration(run), exportFormat: (_duration, run) => formatRunDuration(run), - filter: ({ runDurationFilterModel }) => durationFilter(runDurationFilterModel), + filter: ({ runDurationFilterModel, runDurationOperatorSelectionModel }) => + durationFilter(runDurationFilterModel, runDurationOperatorSelectionModel), }, environmentId: { name: 'Environment ID', diff --git a/lib/public/views/Runs/Overview/RunsOverviewModel.js b/lib/public/views/Runs/Overview/RunsOverviewModel.js index 8e8e90717f..60319318a5 100644 --- a/lib/public/views/Runs/Overview/RunsOverviewModel.js +++ b/lib/public/views/Runs/Overview/RunsOverviewModel.js @@ -22,6 +22,8 @@ import pick from '../../../utilities/pick.js'; import { OverviewPageModel } from '../../../models/OverviewModel.js'; import { getRemoteDataSlice } from '../../../utilities/fetch/getRemoteDataSlice.js'; import { DurationInputModel } from '../../../components/common/form/inputs/DurationInputModel.js'; +import { NumericComparisonOperatorSelectionModel } + from '../../../components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js'; /** * Model representing handlers for runs page @@ -54,6 +56,8 @@ export class RunsOverviewModel extends OverviewPageModel { this._runDurationFilterModel = new DurationInputModel(); this._runDurationFilterModel.observe(() => this._applyFilters()); + this._runDurationOperatorSelectionModel = new NumericComparisonOperatorSelectionModel(); + this._runDurationOperatorSelectionModel.observe(() => this._applyFilters()); // Export items this._allRuns = RemoteData.NotAsked(); @@ -514,13 +518,21 @@ export class RunsOverviewModel extends OverviewPageModel { } /** - * Returns the run duration filter (filter is defined in minutes) - * @return {{operator: string, limit: (number|null)}|null} The current run duration filter + * Returns the run duration filter model + * @return {DurationInputModel} The current run duration filter model */ get runDurationFilterModel() { return this._runDurationFilterModel; } + /** + * Returns the run duration filter operator selection model + * @return {NumericComparisonOperatorSelectionModel} selection model + */ + get runDurationOperatorSelectionModel() { + return this._runDurationOperatorSelectionModel; + } + /** * Returns the current environment id(s) filter * @return {String} The current environment id(s) filter @@ -886,8 +898,7 @@ export class RunsOverviewModel extends OverviewPageModel { new Date(`${this.o2endFilterTo.replace(/\//g, '-')}T${this.o2endFilterToTime}:59.999`).getTime(), }, ...this._runDurationFilterModel.value && { - 'filter[runDuration][operator]': this._runDurationFilterModel.operator, - // Convert filter to milliseconds + 'filter[runDuration][operator]': this._runDurationOperatorSelectionModel.selected, 'filter[runDuration][limit]': this._runDurationFilterModel.value, }, ...this._lhcPeriodsFilter && { From d4363086b140b4162364b65cb11def9aada77118 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Thu, 14 Mar 2024 15:04:41 +0100 Subject: [PATCH 04/46] add model --- .../common/filters/NumericFilterModel.js | 55 +++++++++++++++++++ .../views/Runs/Overview/RunsOverviewModel.js | 10 ++++ 2 files changed, 65 insertions(+) create mode 100644 lib/public/components/Filters/common/filters/NumericFilterModel.js diff --git a/lib/public/components/Filters/common/filters/NumericFilterModel.js b/lib/public/components/Filters/common/filters/NumericFilterModel.js new file mode 100644 index 0000000000..c3afd1c901 --- /dev/null +++ b/lib/public/components/Filters/common/filters/NumericFilterModel.js @@ -0,0 +1,55 @@ +/** + * @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 { NumericComparisonOperatorSelectionModel } from './NumericComparisonOperatorSelectionModel.js'; +import { Observable } from '/js/src/index.js'; + +/** + * Model storing state of a expected value of something with respect of comprison operator + */ +export class NumericFilterModel extends Observable { + /** + * Constructor + */ + constructor() { + super(); + + this._operatorSelectionModel = new NumericComparisonOperatorSelectionModel(); + this._operatorSelectionModel.bubbleTo(this); + + this._value = null; + } + + /** + * Get value - operand in comparison + */ + get value() { + return this._value; + } + + /** + * Set current value of operand + * @param {number} value value + */ + set value(value) { + this._value = value; + this.notify(); + } + + /** + * Get operator selection model + */ + get operatorSelectionModel() { + return this._operatorSelectionModel; + } +} diff --git a/lib/public/views/Runs/Overview/RunsOverviewModel.js b/lib/public/views/Runs/Overview/RunsOverviewModel.js index 60319318a5..278f8fecde 100644 --- a/lib/public/views/Runs/Overview/RunsOverviewModel.js +++ b/lib/public/views/Runs/Overview/RunsOverviewModel.js @@ -24,6 +24,7 @@ import { getRemoteDataSlice } from '../../../utilities/fetch/getRemoteDataSlice. import { DurationInputModel } from '../../../components/common/form/inputs/DurationInputModel.js'; import { NumericComparisonOperatorSelectionModel } from '../../../components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js'; +import { NumericFilterModel } from '../../../components/Filters/common/filters/NumericFilterModel.js'; /** * Model representing handlers for runs page @@ -59,6 +60,15 @@ export class RunsOverviewModel extends OverviewPageModel { this._runDurationOperatorSelectionModel = new NumericComparisonOperatorSelectionModel(); this._runDurationOperatorSelectionModel.observe(() => this._applyFilters()); + this._nEpnsFilterModel = new NumericFilterModel(); + this._nEpnsFilterModel.observe(() => this._applyFilters()); + + this._nDetectorsFilterModel = new NumericFilterModel(); + this._nDetectorsFilterModel.observe(() => this._applyFilters()); + + this._nFlpsFilterModel = new NumericFilterModel(); + this._nFlpsFilterModel.observe(() => this._applyFilters()); + // Export items this._allRuns = RemoteData.NotAsked(); From eda937b667e0e794fdb8a02788335bd2057a973c Mon Sep 17 00:00:00 2001 From: xsalonx Date: Fri, 15 Mar 2024 08:58:43 +0100 Subject: [PATCH 05/46] nEPS filter works --- .../components/Filters/RunsFilter/nEpns.js | 36 ------------- .../common/filters/NumericFilterModel.js | 12 +++++ .../Filters/common/filters/amountFilter.js | 51 ++++++------------- .../Runs/ActiveColumns/runsActiveColumns.js | 15 ++++-- .../views/Runs/Overview/RunsOverviewModel.js | 28 +++------- 5 files changed, 47 insertions(+), 95 deletions(-) delete mode 100644 lib/public/components/Filters/RunsFilter/nEpns.js diff --git a/lib/public/components/Filters/RunsFilter/nEpns.js b/lib/public/components/Filters/RunsFilter/nEpns.js deleted file mode 100644 index 069a1dbaa8..0000000000 --- a/lib/public/components/Filters/RunsFilter/nEpns.js +++ /dev/null @@ -1,36 +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 { amountFilter } from '../common/filters/amountFilter.js'; - -/** - * Returns the nEpns filter component - * @param {RunsOverviewModel} runModel The runs model object - * @return {vnode} A text box and operator that lets the user look for logs with a specific number of EPNs - */ -const nEpnsFilter = (runModel) => amountFilter( - runModel.nEpnsFilter, - (filter) => { - runModel.nEpnsFilter = filter; - }, - { - operatorAttributes: { - id: 'nEpns-operator', - }, - limitAttributes: { - id: 'nEpns-limit', - }, - }, -); - -export default nEpnsFilter; diff --git a/lib/public/components/Filters/common/filters/NumericFilterModel.js b/lib/public/components/Filters/common/filters/NumericFilterModel.js index c3afd1c901..102e07748a 100644 --- a/lib/public/components/Filters/common/filters/NumericFilterModel.js +++ b/lib/public/components/Filters/common/filters/NumericFilterModel.js @@ -30,6 +30,18 @@ export class NumericFilterModel extends Observable { this._value = null; } + /** + * Resotre to default + * @param {boolean} [silent = false] clear silently + * @return {void} + */ + clear(silent = false) { + this._value = null; + if (!silent) { + this.notify(); + } + } + /** * Get value - operand in comparison */ diff --git a/lib/public/components/Filters/common/filters/amountFilter.js b/lib/public/components/Filters/common/filters/amountFilter.js index bc9a0fec4c..e1d71917b0 100644 --- a/lib/public/components/Filters/common/filters/amountFilter.js +++ b/lib/public/components/Filters/common/filters/amountFilter.js @@ -22,52 +22,33 @@ import { h } from '/js/src/index.js'; /** * Returns a component which provide an amount filter, allowing to choose a limit and the comparison operator to apply * - * @param {{operator: string, limit: (number|null)}|null} currentValue the operator to use to compare the limit to the actual - * values - * @param {filterChangeHandler} onChange callback called any time the operator OR the limit changes - * @param {{operatorAttributes: (object|undefined), limitAttributes: (object|undefined)}} options eventual options to configure the filter: use - * `operatorAttributes` to define the attributes of the operator selection component and `limitAttributes` to define the attributes of the - * limit input component + * @param {NumericFilterModel} numericFilterModel filter model + * @param {{operatorAttributes: (object|undefined), operandAttributes: (object|undefined)}} options eventual options to configure the filter: use + * `operatorAttributes` to define the attributes of the operator selection component and `operandAttributes` to define the attributes of the + * operand input component * * @return {vnode} the component */ -export const amountFilter = (currentValue, onChange, options) => { - const { operator, limit } = currentValue || { operator: '=', limit: null }; - const { operatorAttributes = {}, limitAttributes = {} } = options; - - // eslint-disable-next-line require-jsdoc - const updateFilter = ({ newOperator, newLimit }) => { - onChange({ - operator: newOperator || operator, - limit: newLimit !== undefined ? newLimit : limit, - }); - }; +export const amountFilter = (numericFilterModel, options) => { + const { operatorSelectionModel } = numericFilterModel; return comparisonOperatorFilter( h('input.flex-grow', { type: 'number', - min: 0, - value: limit, + min: numericFilterModel.minValue, + max: numericFilterModel.maxValue, + value: numericFilterModel.value, oninput: (e) => { - let newLimit; - - if (e.target.value === '') { - newLimit = null; + const { value } = e.target; + if (value === '') { + numericFilterModel.value = null; } else { - const value = parseInt(e.target.value, 10); - if (!isNaN(value)) { - newLimit = value; - } - } - - if (newLimit !== undefined && newLimit !== limit) { - updateFilter({ newLimit }); + numericFilterModel.value = Number(value); } }, - ...limitAttributes, + ...options.operandAttributes, }, ''), - operator, - (newOperator) => updateFilter({ newOperator }), - operatorAttributes, + operatorSelectionModel, + options.operatorAttributes, ); }; diff --git a/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js b/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js index bbd7b9bec7..a91917f1de 100644 --- a/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js +++ b/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js @@ -30,7 +30,6 @@ import { durationFilter } from '../../../components/Filters/RunsFilter/durationF import { displayRunDuration } from '../format/displayRunDuration.js'; import fillNumbersFilter from '../../../components/Filters/RunsFilter/fillNumbers.js'; import { frontLink } from '../../../components/common/navigation/frontLink.js'; -import nEpnsFilter from '../../../components/Filters/RunsFilter/nEpns.js'; import triggerValueFilter from '../../../components/Filters/RunsFilter/triggerValue.js'; import lhcPeriodsFilter from '../../../components/Filters/RunsFilter/lhcPeriod.js'; import { formatRunType } from '../../../utilities/formatting/formatRunType.js'; @@ -52,6 +51,7 @@ import { formatTagsList } from '../../Tags/format/formatTagsList.js'; import { buttonLinkWithDropdown } from '../../../components/common/selection/infoLoggerButtonGroup/buttonLinkWithDropdown.js'; import { CopyToClipboardComponent } from '../../../components/common/selection/infoLoggerButtonGroup/CopyToClipboardComponent.js'; import { infologgerLinksComponents } from '../../../components/common/infologger/infologgerLinksComponents.js'; +import { amountFilter } from '../../../components/Filters/common/filters/amountFilter.js'; /** * List of active columns for a generic runs table @@ -291,7 +291,7 @@ export const runsActiveColumns = { name: 'DETs #', visible: false, classes: 'w-2 f6 w-wrapped', - filter: nDetectorsFilter, + // filter: nDetectorsFilter, }, nEpns: { name: 'EPNs #', @@ -300,14 +300,21 @@ export const runsActiveColumns = { classes: 'w-2 f6 w-wrapped', // eslint-disable-next-line no-extra-parens format: (nEpns, run) => run.epn ? (typeof nEpns === 'number' ? nEpns : 'ON') : 'OFF', - filter: nEpnsFilter, + filter: ({ nEpnsFilterModel }) => amountFilter(nEpnsFilterModel, { + operatorAttributes: { + id: 'nEpns-operator', + }, + operandAttributes: { + id: 'nEpns-limit', + }, + }), }, nFlps: { name: 'FLPs #', visible: true, profiles: [profiles.none, 'lhcFill', 'environment'], classes: 'w-2 f6 w-wrapped', - filter: nFlpsFilter, + // filter: nFlpsFilter, }, nSubtimeframes: { name: '# of STFs', diff --git a/lib/public/views/Runs/Overview/RunsOverviewModel.js b/lib/public/views/Runs/Overview/RunsOverviewModel.js index 278f8fecde..3315c7ea77 100644 --- a/lib/public/views/Runs/Overview/RunsOverviewModel.js +++ b/lib/public/views/Runs/Overview/RunsOverviewModel.js @@ -185,7 +185,7 @@ export class RunsOverviewModel extends OverviewPageModel { this.nDetectorsFilter = null; - this._nEpnsFilter = null; + this._nEpnsFilterModel.clear(true); this.nFlpsFilter = null; @@ -229,7 +229,7 @@ export class RunsOverviewModel extends OverviewPageModel { || this.runQualitiesFilters.length !== 0 || this._triggerValuesFilters.size !== 0 || this.nDetectorsFilter !== null - || this._nEpnsFilter !== null + || this._nEpnsFilterModel.value !== null || this.nFlpsFilter !== null || this.ddflpFilter !== '' || this.dcsFilter !== '' @@ -296,7 +296,7 @@ export class RunsOverviewModel extends OverviewPageModel { if (this.nDetectorsFilter !== null) { this.activeFilters.push('# of detectors'); } - if (this._nEpnsFilter !== null) { + if (this._nEpnsFilterModel.value !== null) { this.activeFilters.push('# of epns'); } if (this.nFlpsFilter !== null) { @@ -639,8 +639,8 @@ export class RunsOverviewModel extends OverviewPageModel { * Returns the current amount of epns filter * @return {{operator: string, limit: (number|null)}|null} The current amount of epns filters */ - get nEpnsFilter() { - return this._nEpnsFilter; + get nEpnsFilterModel() { + return this._nEpnsFilterModel; } /** @@ -651,18 +651,6 @@ export class RunsOverviewModel extends OverviewPageModel { return this.nFlpsFilter; } - /** - * Sets the limit of epns and the comparison operator to filter if no new inputs were detected for 200 milliseconds - * - * @param {{operator: string, limit: (number|null)}|null} newNEpns The new filter value - * - * @return {void} - */ - set nEpnsFilter(newNEpns) { - this._nEpnsFilter = newNEpns; - this._applyFilters(); - } - /** * Sets the limit of flps and the comparison operator to filter if no new inputs were detected for 200 milliseconds * @@ -931,9 +919,9 @@ export class RunsOverviewModel extends OverviewPageModel { 'filter[nFlps][operator]': this.nFlpsFilter.operator, 'filter[nFlps][limit]': this.nFlpsFilter.limit, }, - ...this.nEpnsFilter && this.nEpnsFilter.limit !== null && { - 'filter[nEpns][operator]': this.nEpnsFilter.operator, - 'filter[nEpns][limit]': this.nEpnsFilter.limit, + ...this._nEpnsFilterModel.value && { + 'filter[nEpns][operator]': this._nEpnsFilterModel.operatorSelectionModel.selected, + 'filter[nEpns][limit]': this._nEpnsFilterModel.value, }, ...(this.ddflpFilter === true || this.ddflpFilter === false) && { 'filter[ddflp]': this.ddflpFilter, From 8c1879454daee0e2f7a57d761813e852bdb36a10 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Fri, 15 Mar 2024 09:22:20 +0100 Subject: [PATCH 06/46] filters work --- .../Filters/RunsFilter/nDetectors.js | 30 ------------------ .../Runs/ActiveColumns/runsActiveColumns.js | 11 +++++-- .../views/Runs/Overview/RunsOverviewModel.js | 31 ++++++------------- 3 files changed, 17 insertions(+), 55 deletions(-) delete mode 100644 lib/public/components/Filters/RunsFilter/nDetectors.js diff --git a/lib/public/components/Filters/RunsFilter/nDetectors.js b/lib/public/components/Filters/RunsFilter/nDetectors.js deleted file mode 100644 index c5941f1608..0000000000 --- a/lib/public/components/Filters/RunsFilter/nDetectors.js +++ /dev/null @@ -1,30 +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 { amountFilter } from '../common/filters/amountFilter.js'; - -/** - * Returns the author filter component - * @param {RunsOverviewModel} runModel the runs model object - * @return {vnode} A text box that lets the user look for logs with a specific author - */ -const nDetectorsFilter = (runModel) => amountFilter(runModel.getNDetectorsFilter(), (filter) => runModel.setNDetectorsFilter(filter), { - operatorAttributes: { - id: 'nDetectors-operator', - }, - limitAttributes: { - id: 'nDetectors-limit', - }, -}); - -export default nDetectorsFilter; diff --git a/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js b/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js index a91917f1de..2b581e1790 100644 --- a/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js +++ b/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js @@ -16,8 +16,6 @@ import runNumberFilter from '../../../components/Filters/RunsFilter/runNumber.js import o2startFilter from '../../../components/Filters/RunsFilter/o2start.js'; import o2endFilter from '../../../components/Filters/RunsFilter/o2stop.js'; import environmentIdFilter from '../../../components/Filters/RunsFilter/environmentId.js'; -import nDetectorsFilter from '../../../components/Filters/RunsFilter/nDetectors.js'; -import nFlpsFilter from '../../../components/Filters/RunsFilter/nFlps.js'; import odcTopologyFullName from '../../../components/Filters/RunsFilter/odcTopologyFullName.js'; import { displayRunEorReasonsOverview } from '../format/displayRunEorReasonOverview.js'; import { detectorsFilterComponent } from '../../../components/Filters/RunsFilter/detectorsFilterComponent.js'; @@ -291,7 +289,14 @@ export const runsActiveColumns = { name: 'DETs #', visible: false, classes: 'w-2 f6 w-wrapped', - // filter: nDetectorsFilter, + // filter: ({ nDetectorsFilterModel }) => amountFilter(nDetectorsFilterModel, { + // operatorAttributes: { + // id: 'nDetectors-operator', + // }, + // operandAttributes: { + // id: 'nDetectors-limit', + // }, + // }), }, nEpns: { name: 'EPNs #', diff --git a/lib/public/views/Runs/Overview/RunsOverviewModel.js b/lib/public/views/Runs/Overview/RunsOverviewModel.js index 3315c7ea77..5b18ca948b 100644 --- a/lib/public/views/Runs/Overview/RunsOverviewModel.js +++ b/lib/public/views/Runs/Overview/RunsOverviewModel.js @@ -183,7 +183,7 @@ export class RunsOverviewModel extends OverviewPageModel { this._triggerValuesFilters = new Set(); - this.nDetectorsFilter = null; + this._nDetectorsFilterModel.clear(true); this._nEpnsFilterModel.clear(true); @@ -228,7 +228,7 @@ export class RunsOverviewModel extends OverviewPageModel { || this.environmentIdsFilter !== '' || this.runQualitiesFilters.length !== 0 || this._triggerValuesFilters.size !== 0 - || this.nDetectorsFilter !== null + || this._nDetectorsFilterModel.value !== null || this._nEpnsFilterModel.value !== null || this.nFlpsFilter !== null || this.ddflpFilter !== '' @@ -293,7 +293,7 @@ export class RunsOverviewModel extends OverviewPageModel { if (this._triggerValuesFilters.size !== 0) { this.activeFilters.push('Trigger Value'); } - if (this.nDetectorsFilter !== null) { + if (this._nDetectorsFilterModel.value !== null) { this.activeFilters.push('# of detectors'); } if (this._nEpnsFilterModel.value !== null) { @@ -616,23 +616,10 @@ export class RunsOverviewModel extends OverviewPageModel { /** * Returns the amount of detectors filters - * @return {{operator: string, limit: (number|null)}|null} The current amount of detectors filters + * @return {NumericFilterModel} The current amount of detectors filters */ - getNDetectorsFilter() { - return this.nDetectorsFilter; - } - - /** - * Sets the limit of detectors and the comparison operator to filter if no new inputs were detected for 200 - * milliseconds - * - * @param {{operator: string, limit: (number|null)}|null} newNDetectors The new filter value - * - * @return {void} - */ - setNDetectorsFilter(newNDetectors) { - this.nDetectorsFilter = newNDetectors; - this._applyFilters(); + get nDetectorsFilterModel() { + return this._nDetectorsFilterModel; } /** @@ -911,9 +898,9 @@ export class RunsOverviewModel extends OverviewPageModel { ...this._triggerValuesFilters.size !== 0 && { 'filter[triggerValues]': Array.from(this._triggerValuesFilters).join(), }, - ...this.nDetectorsFilter && this.nDetectorsFilter.limit !== null && { - 'filter[nDetectors][operator]': this.nDetectorsFilter.operator, - 'filter[nDetectors][limit]': this.nDetectorsFilter.limit, + ...this._nDetectorsFilterModel.value !== null && { + 'filter[nDetectors][operator]': this._nDetectorsFilterModel.operatorSelectionModel.selected, + 'filter[nDetectors][limit]': this._nDetectorsFilterModel.value, }, ...this.nFlpsFilter && this.nFlpsFilter.limit !== null && { 'filter[nFlps][operator]': this.nFlpsFilter.operator, From 15b5dac6da4ab3688feaede49e8d15e39b472168 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Fri, 15 Mar 2024 09:29:57 +0100 Subject: [PATCH 07/46] filter works --- .../components/Filters/RunsFilter/nFlps.js | 30 -------------- .../common/filters/NumericFilterModel.js | 6 ++- .../Runs/ActiveColumns/runsActiveColumns.js | 28 ++++++++----- .../views/Runs/Overview/RunsOverviewModel.js | 40 +++++++------------ 4 files changed, 36 insertions(+), 68 deletions(-) delete mode 100644 lib/public/components/Filters/RunsFilter/nFlps.js diff --git a/lib/public/components/Filters/RunsFilter/nFlps.js b/lib/public/components/Filters/RunsFilter/nFlps.js deleted file mode 100644 index 8144d85a96..0000000000 --- a/lib/public/components/Filters/RunsFilter/nFlps.js +++ /dev/null @@ -1,30 +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 { amountFilter } from '../common/filters/amountFilter.js'; - -/** - * Returns the author filter component - * @param {RunsOverviewModel} runModel the run model object - * @return {vnode} A text box that lets the user look for logs with a specific author - */ -const nFlpsFilter = (runModel) => amountFilter(runModel.getNFlpsFilter(), (filter) => runModel.setNFlpsFilter(filter), { - operatorAttributes: { - id: 'nFlps-operator', - }, - limitAttributes: { - id: 'nFlps-limit', - }, -}); - -export default nFlpsFilter; diff --git a/lib/public/components/Filters/common/filters/NumericFilterModel.js b/lib/public/components/Filters/common/filters/NumericFilterModel.js index 102e07748a..85b7e5e7fa 100644 --- a/lib/public/components/Filters/common/filters/NumericFilterModel.js +++ b/lib/public/components/Filters/common/filters/NumericFilterModel.js @@ -25,7 +25,11 @@ export class NumericFilterModel extends Observable { super(); this._operatorSelectionModel = new NumericComparisonOperatorSelectionModel(); - this._operatorSelectionModel.bubbleTo(this); + this._operatorSelectionModel.observe(() => { + if (this._value !== null) { + this.notify(); + } + }); this._value = null; } diff --git a/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js b/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js index 2b581e1790..0a29955cbb 100644 --- a/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js +++ b/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js @@ -289,22 +289,21 @@ export const runsActiveColumns = { name: 'DETs #', visible: false, classes: 'w-2 f6 w-wrapped', - // filter: ({ nDetectorsFilterModel }) => amountFilter(nDetectorsFilterModel, { - // operatorAttributes: { - // id: 'nDetectors-operator', - // }, - // operandAttributes: { - // id: 'nDetectors-limit', - // }, - // }), + filter: ({ nDetectorsFilterModel }) => amountFilter(nDetectorsFilterModel, { + operatorAttributes: { + id: 'nDetectors-operator', + }, + operandAttributes: { + id: 'nDetectors-limit', + }, + }), }, nEpns: { name: 'EPNs #', visible: true, profiles: [profiles.none, 'lhcFill', 'environment'], classes: 'w-2 f6 w-wrapped', - // eslint-disable-next-line no-extra-parens - format: (nEpns, run) => run.epn ? (typeof nEpns === 'number' ? nEpns : 'ON') : 'OFF', + format: (nEpns, run) => run.epn ? typeof nEpns === 'number' ? nEpns : 'ON' : 'OFF', filter: ({ nEpnsFilterModel }) => amountFilter(nEpnsFilterModel, { operatorAttributes: { id: 'nEpns-operator', @@ -319,7 +318,14 @@ export const runsActiveColumns = { visible: true, profiles: [profiles.none, 'lhcFill', 'environment'], classes: 'w-2 f6 w-wrapped', - // filter: nFlpsFilter, + filter: ({ nFlpsFilterModel }) => amountFilter(nFlpsFilterModel, { + operatorAttributes: { + id: 'nFlps-operator', + }, + operandAttributes: { + id: 'nFlps-limit', + }, + }), }, nSubtimeframes: { name: '# of STFs', diff --git a/lib/public/views/Runs/Overview/RunsOverviewModel.js b/lib/public/views/Runs/Overview/RunsOverviewModel.js index 5b18ca948b..b4c14570c3 100644 --- a/lib/public/views/Runs/Overview/RunsOverviewModel.js +++ b/lib/public/views/Runs/Overview/RunsOverviewModel.js @@ -187,7 +187,7 @@ export class RunsOverviewModel extends OverviewPageModel { this._nEpnsFilterModel.clear(true); - this.nFlpsFilter = null; + this._nFlpsFilterModel.clear(true); this.ddflpFilter = ''; @@ -230,7 +230,7 @@ export class RunsOverviewModel extends OverviewPageModel { || this._triggerValuesFilters.size !== 0 || this._nDetectorsFilterModel.value !== null || this._nEpnsFilterModel.value !== null - || this.nFlpsFilter !== null + || this._nFlpsFilterModel.value !== null || this.ddflpFilter !== '' || this.dcsFilter !== '' || this.epnFilter !== '' @@ -299,7 +299,7 @@ export class RunsOverviewModel extends OverviewPageModel { if (this._nEpnsFilterModel.value !== null) { this.activeFilters.push('# of epns'); } - if (this.nFlpsFilter !== null) { + if (this._nFlpsFilterModel.value !== null) { this.activeFilters.push('# of flps'); } if (this.ddflpFilter !== '') { @@ -615,39 +615,27 @@ export class RunsOverviewModel extends OverviewPageModel { } /** - * Returns the amount of detectors filters - * @return {NumericFilterModel} The current amount of detectors filters + * Returns the detectors amount filter model + * @return {NumericFilterModel} the amount filter moment */ get nDetectorsFilterModel() { return this._nDetectorsFilterModel; } /** - * Returns the current amount of epns filter - * @return {{operator: string, limit: (number|null)}|null} The current amount of epns filters + * Returns the EPNs amount filter model + * @return {NumericFilterModel} The current amount of epns filters */ get nEpnsFilterModel() { return this._nEpnsFilterModel; } /** - * Returns the current amount of flps filter - * @return {{operator: string, limit: (number|null)}|null} The current amount of flps filters + * Returns the FLPs amount filter model + * @return {NumericFilterModel} the amount filter moment */ - getNFlpsFilter() { - return this.nFlpsFilter; - } - - /** - * Sets the limit of flps and the comparison operator to filter if no new inputs were detected for 200 milliseconds - * - * @param {{operator: string, limit: (number|null)}|null} newNFlps The new filter value - * - * @return {void} - */ - setNFlpsFilter(newNFlps) { - this.nFlpsFilter = newNFlps; - this._applyFilters(); + get nFlpsFilterModel() { + return this._nFlpsFilterModel; } /** @@ -902,9 +890,9 @@ export class RunsOverviewModel extends OverviewPageModel { 'filter[nDetectors][operator]': this._nDetectorsFilterModel.operatorSelectionModel.selected, 'filter[nDetectors][limit]': this._nDetectorsFilterModel.value, }, - ...this.nFlpsFilter && this.nFlpsFilter.limit !== null && { - 'filter[nFlps][operator]': this.nFlpsFilter.operator, - 'filter[nFlps][limit]': this.nFlpsFilter.limit, + ...this.nFlpsFilterModel.value && { + 'filter[nFlps][operator]': this._nFlpsFilterModel.operatorSelectionModel.selected, + 'filter[nFlps][limit]': this._nFlpsFilterModel.value, }, ...this._nEpnsFilterModel.value && { 'filter[nEpns][operator]': this._nEpnsFilterModel.operatorSelectionModel.selected, From c3b2f8614b665f4b8b46e3c80a4e0196a0da9328 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Fri, 15 Mar 2024 09:45:06 +0100 Subject: [PATCH 08/46] pol --- .../Filters/common/filters/NumericFilterModel.js | 7 ++----- .../common/form/inputs/DurationInputModel.js | 7 ++++--- .../views/Runs/Overview/RunsOverviewModel.js | 15 +++++++++++---- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/lib/public/components/Filters/common/filters/NumericFilterModel.js b/lib/public/components/Filters/common/filters/NumericFilterModel.js index 85b7e5e7fa..7746c8d8df 100644 --- a/lib/public/components/Filters/common/filters/NumericFilterModel.js +++ b/lib/public/components/Filters/common/filters/NumericFilterModel.js @@ -36,14 +36,11 @@ export class NumericFilterModel extends Observable { /** * Resotre to default - * @param {boolean} [silent = false] clear silently * @return {void} */ - clear(silent = false) { + reset() { this._value = null; - if (!silent) { - this.notify(); - } + this._operatorSelectionModel.reset(); } /** diff --git a/lib/public/components/common/form/inputs/DurationInputModel.js b/lib/public/components/common/form/inputs/DurationInputModel.js index 515fd2ede6..3e28df3941 100644 --- a/lib/public/components/common/form/inputs/DurationInputModel.js +++ b/lib/public/components/common/form/inputs/DurationInputModel.js @@ -15,7 +15,6 @@ import { Observable } from '/js/src/index.js'; /** * @typedef DurationInputRawData - * @property {number} days the number of days * @property {number} hours the number of hours * @property {number} minutes the number of minutes * @property {number} seconds the number of seconds @@ -57,6 +56,9 @@ export class DurationInputModel extends Observable { (this._raw.hours || 0) * 60 * 60 * 1000 + (this._raw.minutes || 0) * 60 * 1000 + (this._raw.seconds || 0) * 1000; + if (this._value === 0) { + this.reset(); + } } catch (_) { this._value = null; } @@ -68,14 +70,13 @@ export class DurationInputModel extends Observable { * Reset the inputs to its initial state * @return {void} */ - clear() { + reset() { this._raw = { hours: null, minutes: null, seconds: null, }; this._value = null; - this.notify(); } /** diff --git a/lib/public/views/Runs/Overview/RunsOverviewModel.js b/lib/public/views/Runs/Overview/RunsOverviewModel.js index b4c14570c3..dc9579ef8d 100644 --- a/lib/public/views/Runs/Overview/RunsOverviewModel.js +++ b/lib/public/views/Runs/Overview/RunsOverviewModel.js @@ -58,7 +58,11 @@ export class RunsOverviewModel extends OverviewPageModel { this._runDurationFilterModel = new DurationInputModel(); this._runDurationFilterModel.observe(() => this._applyFilters()); this._runDurationOperatorSelectionModel = new NumericComparisonOperatorSelectionModel(); - this._runDurationOperatorSelectionModel.observe(() => this._applyFilters()); + this._runDurationOperatorSelectionModel.observe(() => { + if (this._runDurationFilterModel.value !== null) { + this._applyFilters(); + } + }); this._nEpnsFilterModel = new NumericFilterModel(); this._nEpnsFilterModel.observe(() => this._applyFilters()); @@ -183,11 +187,14 @@ export class RunsOverviewModel extends OverviewPageModel { this._triggerValuesFilters = new Set(); - this._nDetectorsFilterModel.clear(true); + this._nDetectorsFilterModel.reset(); - this._nEpnsFilterModel.clear(true); + this._nEpnsFilterModel.reset(); - this._nFlpsFilterModel.clear(true); + this._nFlpsFilterModel.reset(); + + this._runDurationFilterModel.reset(); + this._runDurationOperatorSelectionModel.reset(); this.ddflpFilter = ''; From e9e742080a030edfe75f5053a6751cf3129c0fe5 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Fri, 15 Mar 2024 09:58:16 +0100 Subject: [PATCH 09/46] pol --- .../Filters/RunsFilter/durationFilter.js | 8 ++-- .../common/form/inputs/DurationInputModel.js | 41 ++++++++++++++++++- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/lib/public/components/Filters/RunsFilter/durationFilter.js b/lib/public/components/Filters/RunsFilter/durationFilter.js index f5c72adb93..7d7c6aa408 100644 --- a/lib/public/components/Filters/RunsFilter/durationFilter.js +++ b/lib/public/components/Filters/RunsFilter/durationFilter.js @@ -22,7 +22,7 @@ import { comparisonOperatorFilter } from '../common/filters/comparisonOperatorFi * @return {Component} the duration filter */ export const durationFilter = (durationFilterModel, operatorSelectionModel) => { - const { hours, minutes, seconds } = durationFilterModel.raw; + const { hours, minutes, seconds, secondsMin, minutesMin, } = durationFilterModel.raw; const hoursInput = h('input.flex-grow', { id: 'hours-input', @@ -37,7 +37,8 @@ export const durationFilter = (durationFilterModel, operatorSelectionModel) => { const minutesInput = h('input.flex-grow', { id: 'minutes-input', type: 'number', - min: 0, + min: minutesMin, + max: 60, value: minutes, oninput: (e) => { const { value } = e.target; @@ -47,7 +48,8 @@ export const durationFilter = (durationFilterModel, operatorSelectionModel) => { const secondsInput = h('input.flex-grow', { id: 'seconds-input', type: 'number', - min: 0, + min: secondsMin, + max: 60, value: seconds, oninput: (e) => { const { value } = e.target; diff --git a/lib/public/components/common/form/inputs/DurationInputModel.js b/lib/public/components/common/form/inputs/DurationInputModel.js index 3e28df3941..d48a81e11a 100644 --- a/lib/public/components/common/form/inputs/DurationInputModel.js +++ b/lib/public/components/common/form/inputs/DurationInputModel.js @@ -50,12 +50,37 @@ export class DurationInputModel extends Observable { * @return {void} */ update(raw) { - this._raw = { ...this._raw, ...raw }; try { + const previousVal = this._raw; + this._raw = { ...this._raw, ...raw }; + + if (this._raw.seconds === 60) { + this._raw.minutes++; + this._raw.seconds = 0; + } + if (this._raw.minutes === 60) { + this._raw.hours++; + this._raw.minutes = 0; + } + + if (this._raw.seconds === -1) { + this._raw.minutes--; + this._raw.seconds = 59; + } + if (this._raw.minutes === -1) { + this._raw.hours--; + this._raw.minutes = 59; + } + + if (this._raw.hours < 0) { + this.update(previousVal); + return; + } this._value = (this._raw.hours || 0) * 60 * 60 * 1000 + (this._raw.minutes || 0) * 60 * 1000 + (this._raw.seconds || 0) * 1000; + if (this._value === 0) { this.reset(); } @@ -66,6 +91,20 @@ export class DurationInputModel extends Observable { this.notify(); } + /** + * Min input for minutes input + */ + get minutesMin() { + return this._raw.hours > 0 ? -1 : 0; + } + + /** + * Min input for seconds input + */ + get secondsMin() { + return this._raw.minutes > 0 ? -1 : 0; + } + /** * Reset the inputs to its initial state * @return {void} From 5e98f0458bbd4a06a813647ab166267b48345402 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Fri, 15 Mar 2024 10:17:23 +0100 Subject: [PATCH 10/46] linter --- lib/public/components/Filters/RunsFilter/durationFilter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/public/components/Filters/RunsFilter/durationFilter.js b/lib/public/components/Filters/RunsFilter/durationFilter.js index 7d7c6aa408..74fed3111e 100644 --- a/lib/public/components/Filters/RunsFilter/durationFilter.js +++ b/lib/public/components/Filters/RunsFilter/durationFilter.js @@ -22,7 +22,7 @@ import { comparisonOperatorFilter } from '../common/filters/comparisonOperatorFi * @return {Component} the duration filter */ export const durationFilter = (durationFilterModel, operatorSelectionModel) => { - const { hours, minutes, seconds, secondsMin, minutesMin, } = durationFilterModel.raw; + const { hours, minutes, seconds, secondsMin, minutesMin } = durationFilterModel.raw; const hoursInput = h('input.flex-grow', { id: 'hours-input', From 3cbb87fa17aea82c7f82686b861a2cc8c37069da Mon Sep 17 00:00:00 2001 From: xsalonx Date: Fri, 15 Mar 2024 10:21:32 +0100 Subject: [PATCH 11/46] add default --- .../filters/NumericComparisonOperatorSelectionModel.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js b/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js index 8cc047ae53..525cf7c79e 100644 --- a/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js +++ b/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js @@ -19,13 +19,14 @@ import { SelectionDropdownModel } from '../../../common/selection/dropdown/Selec export class NumericComparisonOperatorSelectionModel extends SelectionDropdownModel { /** * Constructor + * @param {string} defaultOperator one of ['<', '<=', '=', '>=', '>'] operators */ - constructor() { + constructor(defaultOperator = '>=') { super({ availableOptions: ['<', '<=', '=', '>=', '>'].map((op) => ({ value: op })), multiple: false, allowEmpty: false, - defaultSelection: [{ value: '>=' }], + defaultSelection: [{ value: defaultOperator }], }); } From 1a61b69e3ed8b7b98cb552c5cd60df7ad824c83e Mon Sep 17 00:00:00 2001 From: xsalonx Date: Fri, 15 Mar 2024 10:22:40 +0100 Subject: [PATCH 12/46] docs --- .../components/Filters/common/filters/NumericFilterModel.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/public/components/Filters/common/filters/NumericFilterModel.js b/lib/public/components/Filters/common/filters/NumericFilterModel.js index 7746c8d8df..99cbc285e3 100644 --- a/lib/public/components/Filters/common/filters/NumericFilterModel.js +++ b/lib/public/components/Filters/common/filters/NumericFilterModel.js @@ -35,7 +35,7 @@ export class NumericFilterModel extends Observable { } /** - * Resotre to default + * Reset to default * @return {void} */ reset() { @@ -44,7 +44,7 @@ export class NumericFilterModel extends Observable { } /** - * Get value - operand in comparison + * Get value - operand to comparison */ get value() { return this._value; From 2d1731af1c0fdcbb7046b6f8946d3463f15c2057 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Fri, 15 Mar 2024 10:25:59 +0100 Subject: [PATCH 13/46] conf min max --- .../common/filters/NumericFilterModel.js | 19 ++++++++++++++++++- .../Filters/common/filters/amountFilter.js | 5 ----- .../views/Runs/Overview/RunsOverviewModel.js | 6 +++--- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/lib/public/components/Filters/common/filters/NumericFilterModel.js b/lib/public/components/Filters/common/filters/NumericFilterModel.js index 99cbc285e3..6e2d033544 100644 --- a/lib/public/components/Filters/common/filters/NumericFilterModel.js +++ b/lib/public/components/Filters/common/filters/NumericFilterModel.js @@ -21,9 +21,12 @@ export class NumericFilterModel extends Observable { /** * Constructor */ - constructor() { + constructor({ min, max }) { super(); + this._minValue = min; + this._maxValue = max; + this._operatorSelectionModel = new NumericComparisonOperatorSelectionModel(); this._operatorSelectionModel.observe(() => { if (this._value !== null) { @@ -34,6 +37,20 @@ export class NumericFilterModel extends Observable { this._value = null; } + /** + * Get minimum allowed value + */ + get minValue() { + return this._minValue; + } + + /** + * Get maximum allowed value + */ + get maxValue() { + return this._maxValue; + } + /** * Reset to default * @return {void} diff --git a/lib/public/components/Filters/common/filters/amountFilter.js b/lib/public/components/Filters/common/filters/amountFilter.js index e1d71917b0..78a1b57875 100644 --- a/lib/public/components/Filters/common/filters/amountFilter.js +++ b/lib/public/components/Filters/common/filters/amountFilter.js @@ -14,11 +14,6 @@ import { comparisonOperatorFilter } from './comparisonOperatorFilter.js'; import { h } from '/js/src/index.js'; -/** - * @callback filterChangeHandler - * @param {{operator: string, limit: (number|number)}|null} newFilter the new filter value - */ - /** * Returns a component which provide an amount filter, allowing to choose a limit and the comparison operator to apply * diff --git a/lib/public/views/Runs/Overview/RunsOverviewModel.js b/lib/public/views/Runs/Overview/RunsOverviewModel.js index dc9579ef8d..7d96397e3e 100644 --- a/lib/public/views/Runs/Overview/RunsOverviewModel.js +++ b/lib/public/views/Runs/Overview/RunsOverviewModel.js @@ -64,13 +64,13 @@ export class RunsOverviewModel extends OverviewPageModel { } }); - this._nEpnsFilterModel = new NumericFilterModel(); + this._nEpnsFilterModel = new NumericFilterModel({ min: 0 }); this._nEpnsFilterModel.observe(() => this._applyFilters()); - this._nDetectorsFilterModel = new NumericFilterModel(); + this._nDetectorsFilterModel = new NumericFilterModel({ min: 0 }); this._nDetectorsFilterModel.observe(() => this._applyFilters()); - this._nFlpsFilterModel = new NumericFilterModel(); + this._nFlpsFilterModel = new NumericFilterModel({ min: 0 }); this._nFlpsFilterModel.observe(() => this._applyFilters()); // Export items From 2475d197385eee15f2c6b9fe82bda22c6542873a Mon Sep 17 00:00:00 2001 From: xsalonx Date: Fri, 15 Mar 2024 10:27:04 +0100 Subject: [PATCH 14/46] docs --- .../components/Filters/common/filters/NumericFilterModel.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/public/components/Filters/common/filters/NumericFilterModel.js b/lib/public/components/Filters/common/filters/NumericFilterModel.js index 6e2d033544..4c18fe53ca 100644 --- a/lib/public/components/Filters/common/filters/NumericFilterModel.js +++ b/lib/public/components/Filters/common/filters/NumericFilterModel.js @@ -20,6 +20,9 @@ import { Observable } from '/js/src/index.js'; export class NumericFilterModel extends Observable { /** * Constructor + * @param {object} configuration configration + * @param {number} [configration.minValue] minimal value allowed in model + * @param {number} [configration.maxValue] maximum value allowed in model */ constructor({ min, max }) { super(); From b37573a86897f987909666f1760fbac24f313c08 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Fri, 15 Mar 2024 10:29:22 +0100 Subject: [PATCH 15/46] value --- .../Filters/common/filters/amountFilter.js | 2 +- .../common/filters/comparisonOperatorFilter.js | 14 ++------------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/lib/public/components/Filters/common/filters/amountFilter.js b/lib/public/components/Filters/common/filters/amountFilter.js index 78a1b57875..ab105c8264 100644 --- a/lib/public/components/Filters/common/filters/amountFilter.js +++ b/lib/public/components/Filters/common/filters/amountFilter.js @@ -22,7 +22,7 @@ import { h } from '/js/src/index.js'; * `operatorAttributes` to define the attributes of the operator selection component and `operandAttributes` to define the attributes of the * operand input component * - * @return {vnode} the component + * @return {Component} the filter component */ export const amountFilter = (numericFilterModel, options) => { const { operatorSelectionModel } = numericFilterModel; diff --git a/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js b/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js index a9638ff103..293900899e 100644 --- a/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js +++ b/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js @@ -13,25 +13,15 @@ import { h } from '/js/src/index.js'; -/** - * Handle an operator change event - * - * @callback operatorChangeHandler - * - * @param {string} operator the new operator value - * - * @return {*} - */ - /** * Wrap a given input inside a comparison operator filter, prefixing the input by an operator selector * - * @param {Compoenent} operandInput the input for the limit value (the one to wrap) + * @param {Compoenent} operandInput the input for the operand value (the one to wrap) * @param {NumericComparisonOperatorSelectionModel} operatorSelectionModel the operator selection model * @param {*} [operatorAttributes] the list of attributes to apply on the operator selector (note that value and * onchange attribute are override) * - * @return {vnode} the filter component + * @return {ComponenT} the filter component */ export const comparisonOperatorFilter = (operandInput, operatorSelectionModel, operatorAttributes = {}) => h('.flex-row.g3', [ h('', h('select.form-control', { From 78950b5b3d7ac8f68a3974cca72a6dacb7e4dd86 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Fri, 15 Mar 2024 10:52:19 +0100 Subject: [PATCH 16/46] change default --- .../filters/NumericComparisonOperatorSelectionModel.js | 4 ++-- .../components/Filters/common/filters/NumericFilterModel.js | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js b/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js index 525cf7c79e..9237a49312 100644 --- a/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js +++ b/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js @@ -19,9 +19,9 @@ import { SelectionDropdownModel } from '../../../common/selection/dropdown/Selec export class NumericComparisonOperatorSelectionModel extends SelectionDropdownModel { /** * Constructor - * @param {string} defaultOperator one of ['<', '<=', '=', '>=', '>'] operators + * @param {string} [defaultOperator = '='] one of ['<', '<=', '=', '>=', '>'] operators */ - constructor(defaultOperator = '>=') { + constructor(defaultOperator = '=') { super({ availableOptions: ['<', '<=', '=', '>=', '>'].map((op) => ({ value: op })), multiple: false, diff --git a/lib/public/components/Filters/common/filters/NumericFilterModel.js b/lib/public/components/Filters/common/filters/NumericFilterModel.js index 4c18fe53ca..dfe8bd89f5 100644 --- a/lib/public/components/Filters/common/filters/NumericFilterModel.js +++ b/lib/public/components/Filters/common/filters/NumericFilterModel.js @@ -23,14 +23,15 @@ export class NumericFilterModel extends Observable { * @param {object} configuration configration * @param {number} [configration.minValue] minimal value allowed in model * @param {number} [configration.maxValue] maximum value allowed in model + * @param {number} [configration.defaultOperator] defaultOperator one of ['<', '<=', '=', '>=', '>'] operators */ - constructor({ min, max }) { + constructor({ min, max, defaultOperator }) { super(); this._minValue = min; this._maxValue = max; - this._operatorSelectionModel = new NumericComparisonOperatorSelectionModel(); + this._operatorSelectionModel = new NumericComparisonOperatorSelectionModel(defaultOperator); this._operatorSelectionModel.observe(() => { if (this._value !== null) { this.notify(); From 7f03dff850b0680b44d15f7484cccee741add6bb Mon Sep 17 00:00:00 2001 From: xsalonx Date: Fri, 15 Mar 2024 11:01:35 +0100 Subject: [PATCH 17/46] test WIP --- test/public/runs/overview.test.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/public/runs/overview.test.js b/test/public/runs/overview.test.js index 8b43057f76..933313430d 100644 --- a/test/public/runs/overview.test.js +++ b/test/public/runs/overview.test.js @@ -532,10 +532,7 @@ module.exports = () => { it('should successfully filter on duration', async () => { await goToPage(page, 'run-overview'); - waitForTimeout(100); - await pressElement(page, '#openFilterToggle'); - await waitForTimeout(200); const runDurationOperatorSelector = '#duration-operator'; const runDurationOperator = await page.$(runDurationOperatorSelector) || null; @@ -548,10 +545,7 @@ module.exports = () => { await page.focus(runDurationLimitSelector); await page.keyboard.type('1500'); - await waitForTimeout(300); - await page.select(runDurationOperatorSelector, '='); - await waitForTimeout(300); let runDurationList = await page.evaluate(() => Array.from(document.querySelectorAll('tbody tr')).map((row) => { const rowId = row.id; From 58d31e59c6483d8606b42e21638d306e45e6fe84 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Fri, 15 Mar 2024 11:54:58 +0100 Subject: [PATCH 18/46] Test --- test/public/defaults.js | 5 +++ test/public/runs/overview.test.js | 69 ++++++++++++++----------------- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/test/public/defaults.js b/test/public/defaults.js index ddb6bd7c14..206cb8d934 100644 --- a/test/public/defaults.js +++ b/test/public/defaults.js @@ -308,6 +308,11 @@ module.exports.expectInnerTextTo = async (page, selector, validator) => { expect(validator(actualInnerText), `"${actualInnerText}" is invalid with respect of given validator`).to.be.true; }; +module.exports.expectSelectToBe = async (page, selector, selectedOption) => { + await page.waitForSelector(selector); + expect(await (await page.$(selector)).evaluate(({ value }) => value)).to.be.equal(selectedOption); +}; + /** * Evaluate and return the html content of a given element handler * @param {{evaluate}} elementHandler the puppeteer handler of the element to inspect diff --git a/test/public/runs/overview.test.js b/test/public/runs/overview.test.js index 933313430d..59b5025908 100644 --- a/test/public/runs/overview.test.js +++ b/test/public/runs/overview.test.js @@ -23,6 +23,9 @@ const { goToPage, checkColumnBalloon, waitForNetworkIdleAndRedraw, + expectSelectToBe, + getAllDataFields, + waitForTableDataReload, } = require('../defaults'); const { RunDefinition } = require('../../../lib/server/services/run/getRunDefinition.js'); const { RUN_QUALITIES, RunQualities } = require('../../../lib/domain/enums/RunQualities.js'); @@ -534,46 +537,38 @@ module.exports = () => { await goToPage(page, 'run-overview'); await pressElement(page, '#openFilterToggle'); - const runDurationOperatorSelector = '#duration-operator'; - const runDurationOperator = await page.$(runDurationOperatorSelector) || null; - expect(runDurationOperator).to.not.be.null; - expect(await runDurationOperator.evaluate((element) => element.value)).to.equal('='); + const runDurationFilterDivSelector = '.runDuration-filter'; + const operatorSelector = `${runDurationFilterDivSelector} select`; + const hoursSelector = `${runDurationFilterDivSelector} input:nth-of-type(1)`; + const minutesSelector = `${runDurationFilterDivSelector} input:nth-of-type(2)`; + await expectSelectToBe(page, operatorSelector, '='); - const runDurationLimitSelector = '#duration-limit'; - const runDurationLimit = await page.$(runDurationLimitSelector) || null; - expect(runDurationLimit).to.not.be.null; + await waitForTableDataReload(page, () => fillInput(page, hoursSelector, 26)); + let runDurationList = await getAllDataFields(page, 'runDuration'); + expect(runDurationList).to.be.lengthOf.greaterThan(0); + expect(runDurationList.every((runDuration) => runDuration === '26:00:00')).to.be.true; - await page.focus(runDurationLimitSelector); - await page.keyboard.type('1500'); - await page.select(runDurationOperatorSelector, '='); - - let runDurationList = await page.evaluate(() => Array.from(document.querySelectorAll('tbody tr')).map((row) => { - const rowId = row.id; - return document.querySelector(`#${rowId}-runDuration-text`)?.innerText; - })); - - expect(runDurationList.every((runDuration) => { - const time = runDuration.replace('*', ''); - return time === '25:00:00'; - })).to.be.true; - - await page.$eval(runDurationLimitSelector, (input) => { - input.value = ''; + await waitForTableDataReload(page, async () => { + await fillInput(page, hoursSelector, 0); + await fillInput(page, minutesSelector, 60); }); - await page.focus(runDurationLimitSelector); - await page.keyboard.type('3000'); - await waitForTimeout(300); - - await page.select(runDurationOperatorSelector, '>='); - await waitForTimeout(300); - - // Expect only unknown - runDurationList = await page.evaluate(() => Array.from(document.querySelectorAll('tbody tr')).map((row) => { - const rowId = row.id; - return document.querySelector(`#${rowId}-runDuration-text`)?.innerText; - })); - - expect(runDurationList.every((runDuration) => runDuration === 'UNKNOWN')).to.be.true; + runDurationList = await getAllDataFields(page, 'runDuration'); + expect(runDurationList).to.be.lengthOf.greaterThan(0); + expect(runDurationList.every((runDuration) => runDuration === '01:00:00')).to.be.true; + + await waitForTableDataReload(page, () => page.select(operatorSelector, '>=')); + runDurationList = await getAllDataFields(page, 'runDuration'); + expect(runDurationList).to.be.lengthOf.greaterThan(0); + expect(runDurationList.every((runDuration) => runDuration >= '01:00:00')).to.be.true; + + await waitForTableDataReload(page, async () => { + await page.select(operatorSelector, '<='); + await fillInput(page, minutesSelector, 0); + await fillInput(page, hoursSelector, 10); + }); + runDurationList = await getAllDataFields(page, 'runDuration'); + expect(runDurationList).to.be.lengthOf.greaterThan(0); + expect(runDurationList.every((runDuration) => runDuration <= '10:00:00')).to.be.true; }); it('Should successfully filter runs by their run quality', async () => { From 3661588679c6c846f323be9fda0f8877c048b698 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Wed, 27 Mar 2024 12:56:02 +0100 Subject: [PATCH 19/46] remove override --- .../filters/NumericComparisonOperatorSelectionModel.js | 8 -------- .../Filters/common/filters/comparisonOperatorFilter.js | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js b/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js index 9237a49312..73ca7a70f7 100644 --- a/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js +++ b/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js @@ -29,12 +29,4 @@ export class NumericComparisonOperatorSelectionModel extends SelectionDropdownMo defaultSelection: [{ value: defaultOperator }], }); } - - // eslint-disable-next-line valid-jsdoc - /** - * @inheritDoc - */ - get selected() { - return super.selected[0]; - } } diff --git a/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js b/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js index 293900899e..a1093175ec 100644 --- a/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js +++ b/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js @@ -26,7 +26,7 @@ import { h } from '/js/src/index.js'; export const comparisonOperatorFilter = (operandInput, operatorSelectionModel, operatorAttributes = {}) => h('.flex-row.g3', [ h('', h('select.form-control', { ...operatorAttributes, - value: operatorSelectionModel.selected, + value: operatorSelectionModel.selected[0], onchange: (e) => operatorSelectionModel.select(e.target.value), }, operatorSelectionModel.options?.map(({ value }) => h('option', { value, From a06640dcf35e1de6a12e258264db68e6ca8cc6dd Mon Sep 17 00:00:00 2001 From: xsalonx Date: Wed, 27 Mar 2024 12:56:36 +0100 Subject: [PATCH 20/46] refactor --- .../common/filters/NumericComparisonOperatorSelectionModel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js b/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js index 73ca7a70f7..bad56254c6 100644 --- a/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js +++ b/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js @@ -23,7 +23,7 @@ export class NumericComparisonOperatorSelectionModel extends SelectionDropdownMo */ constructor(defaultOperator = '=') { super({ - availableOptions: ['<', '<=', '=', '>=', '>'].map((op) => ({ value: op })), + availableOptions: ['<', '<=', '=', '>=', '>'].map((operator) => ({ value: operator })), multiple: false, allowEmpty: false, defaultSelection: [{ value: defaultOperator }], From 21c1cf40b2946d7f8cefff312cc6ae3710482f14 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Wed, 27 Mar 2024 12:58:24 +0100 Subject: [PATCH 21/46] use SelectionMode --- .../common/filters/NumericComparisonOperatorSelectionModel.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js b/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js index bad56254c6..0c640d422a 100644 --- a/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js +++ b/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js @@ -11,12 +11,12 @@ * or submit itself to any jurisdiction. */ -import { SelectionDropdownModel } from '../../../common/selection/dropdown/SelectionDropdownModel.js'; +import { SelectionModel } from '../../../common/selection/SelectionModel.js'; /** * Model storing state of a selection of predfined numerical comparison operators */ -export class NumericComparisonOperatorSelectionModel extends SelectionDropdownModel { +export class NumericComparisonOperatorSelectionModel extends SelectionModel { /** * Constructor * @param {string} [defaultOperator = '='] one of ['<', '<=', '=', '>=', '>'] operators From 0ab6860e2fcd7cea751538139d0661c97677623b Mon Sep 17 00:00:00 2001 From: xsalonx Date: Wed, 27 Mar 2024 13:03:21 +0100 Subject: [PATCH 22/46] rename --- .../Filters/RunsFilter/durationFilter.js | 2 +- ...NumericComparisonOperatorSelectionModel.js | 32 ------------------- .../common/filters/NumericFilterModel.js | 4 +-- .../filters/comparisonOperatorFilter.js | 2 +- .../views/Runs/Overview/RunsOverviewModel.js | 8 ++--- 5 files changed, 8 insertions(+), 40 deletions(-) delete mode 100644 lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js diff --git a/lib/public/components/Filters/RunsFilter/durationFilter.js b/lib/public/components/Filters/RunsFilter/durationFilter.js index 74fed3111e..857fa91b39 100644 --- a/lib/public/components/Filters/RunsFilter/durationFilter.js +++ b/lib/public/components/Filters/RunsFilter/durationFilter.js @@ -18,7 +18,7 @@ import { comparisonOperatorFilter } from '../common/filters/comparisonOperatorFi * Returns the run duration filter component * * @param {DurationInputModel} durationFilterModel the duration input model - * @param {NumericComparisonOperatorSelectionModel} operatorSelectionModel the comparison operator selection model + * @param {ComparisonOperatorSelectionModel} operatorSelectionModel the comparison operator selection model * @return {Component} the duration filter */ export const durationFilter = (durationFilterModel, operatorSelectionModel) => { diff --git a/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js b/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js deleted file mode 100644 index 0c640d422a..0000000000 --- a/lib/public/components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js +++ /dev/null @@ -1,32 +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 { SelectionModel } from '../../../common/selection/SelectionModel.js'; - -/** - * Model storing state of a selection of predfined numerical comparison operators - */ -export class NumericComparisonOperatorSelectionModel extends SelectionModel { - /** - * Constructor - * @param {string} [defaultOperator = '='] one of ['<', '<=', '=', '>=', '>'] operators - */ - constructor(defaultOperator = '=') { - super({ - availableOptions: ['<', '<=', '=', '>=', '>'].map((operator) => ({ value: operator })), - multiple: false, - allowEmpty: false, - defaultSelection: [{ value: defaultOperator }], - }); - } -} diff --git a/lib/public/components/Filters/common/filters/NumericFilterModel.js b/lib/public/components/Filters/common/filters/NumericFilterModel.js index dfe8bd89f5..0007999daa 100644 --- a/lib/public/components/Filters/common/filters/NumericFilterModel.js +++ b/lib/public/components/Filters/common/filters/NumericFilterModel.js @@ -11,7 +11,7 @@ * or submit itself to any jurisdiction. */ -import { NumericComparisonOperatorSelectionModel } from './NumericComparisonOperatorSelectionModel.js'; +import { ComparisonOperatorSelectionModel } from './ComparisonOperatorSelectionModel.js'; import { Observable } from '/js/src/index.js'; /** @@ -31,7 +31,7 @@ export class NumericFilterModel extends Observable { this._minValue = min; this._maxValue = max; - this._operatorSelectionModel = new NumericComparisonOperatorSelectionModel(defaultOperator); + this._operatorSelectionModel = new ComparisonOperatorSelectionModel(defaultOperator); this._operatorSelectionModel.observe(() => { if (this._value !== null) { this.notify(); diff --git a/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js b/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js index a1093175ec..74220c0e54 100644 --- a/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js +++ b/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js @@ -17,7 +17,7 @@ import { h } from '/js/src/index.js'; * Wrap a given input inside a comparison operator filter, prefixing the input by an operator selector * * @param {Compoenent} operandInput the input for the operand value (the one to wrap) - * @param {NumericComparisonOperatorSelectionModel} operatorSelectionModel the operator selection model + * @param {ComparisonOperatorSelectionModel} operatorSelectionModel the operator selection model * @param {*} [operatorAttributes] the list of attributes to apply on the operator selector (note that value and * onchange attribute are override) * diff --git a/lib/public/views/Runs/Overview/RunsOverviewModel.js b/lib/public/views/Runs/Overview/RunsOverviewModel.js index 7d96397e3e..b2da297662 100644 --- a/lib/public/views/Runs/Overview/RunsOverviewModel.js +++ b/lib/public/views/Runs/Overview/RunsOverviewModel.js @@ -22,8 +22,8 @@ import pick from '../../../utilities/pick.js'; import { OverviewPageModel } from '../../../models/OverviewModel.js'; import { getRemoteDataSlice } from '../../../utilities/fetch/getRemoteDataSlice.js'; import { DurationInputModel } from '../../../components/common/form/inputs/DurationInputModel.js'; -import { NumericComparisonOperatorSelectionModel } - from '../../../components/Filters/common/filters/NumericComparisonOperatorSelectionModel.js'; +import { ComparisonOperatorSelectionModel } + from '../../../components/Filters/common/filters/ComparisonOperatorSelectionModel.js'; import { NumericFilterModel } from '../../../components/Filters/common/filters/NumericFilterModel.js'; /** @@ -57,7 +57,7 @@ export class RunsOverviewModel extends OverviewPageModel { this._runDurationFilterModel = new DurationInputModel(); this._runDurationFilterModel.observe(() => this._applyFilters()); - this._runDurationOperatorSelectionModel = new NumericComparisonOperatorSelectionModel(); + this._runDurationOperatorSelectionModel = new ComparisonOperatorSelectionModel(); this._runDurationOperatorSelectionModel.observe(() => { if (this._runDurationFilterModel.value !== null) { this._applyFilters(); @@ -544,7 +544,7 @@ export class RunsOverviewModel extends OverviewPageModel { /** * Returns the run duration filter operator selection model - * @return {NumericComparisonOperatorSelectionModel} selection model + * @return {ComparisonOperatorSelectionModel} selection model */ get runDurationOperatorSelectionModel() { return this._runDurationOperatorSelectionModel; From 144317c155099a3d14a8bd74c7507372bf9439e7 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Wed, 27 Mar 2024 13:03:25 +0100 Subject: [PATCH 23/46] rename --- .../ComparisonOperatorSelectionModel.js | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 lib/public/components/Filters/common/filters/ComparisonOperatorSelectionModel.js diff --git a/lib/public/components/Filters/common/filters/ComparisonOperatorSelectionModel.js b/lib/public/components/Filters/common/filters/ComparisonOperatorSelectionModel.js new file mode 100644 index 0000000000..8237c54327 --- /dev/null +++ b/lib/public/components/Filters/common/filters/ComparisonOperatorSelectionModel.js @@ -0,0 +1,32 @@ +/** + * @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 storing state of a selection of predefined comparison operators + */ +export class ComparisonOperatorSelectionModel extends SelectionModel { + /** + * Constructor + * @param {string} [defaultOperator = '='] one of ['<', '<=', '=', '>=', '>'] operators + */ + constructor(defaultOperator = '=') { + super({ + availableOptions: ['<', '<=', '=', '>=', '>'].map((operator) => ({ value: operator })), + multiple: false, + allowEmpty: false, + defaultSelection: [{ value: defaultOperator }], + }); + } +} From 40b70e2e23c5f48cb77b49384bb26754028dc08b Mon Sep 17 00:00:00 2001 From: xsalonx Date: Wed, 27 Mar 2024 13:11:47 +0100 Subject: [PATCH 24/46] bug --- lib/public/components/Filters/RunsFilter/durationFilter.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/public/components/Filters/RunsFilter/durationFilter.js b/lib/public/components/Filters/RunsFilter/durationFilter.js index 857fa91b39..b4fb0eec11 100644 --- a/lib/public/components/Filters/RunsFilter/durationFilter.js +++ b/lib/public/components/Filters/RunsFilter/durationFilter.js @@ -22,7 +22,8 @@ import { comparisonOperatorFilter } from '../common/filters/comparisonOperatorFi * @return {Component} the duration filter */ export const durationFilter = (durationFilterModel, operatorSelectionModel) => { - const { hours, minutes, seconds, secondsMin, minutesMin } = durationFilterModel.raw; + const { hours, minutes, seconds } = durationFilterModel.raw; + const { secondsMin, minutesMin } = durationFilterModel; const hoursInput = h('input.flex-grow', { id: 'hours-input', From 68a01d7014fe04e47b6bcc967f800bfbff280b54 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Wed, 27 Mar 2024 13:18:00 +0100 Subject: [PATCH 25/46] simp --- .../Filters/RunsFilter/durationFilter.js | 9 ++--- .../common/form/inputs/DurationInputModel.js | 38 ------------------- 2 files changed, 4 insertions(+), 43 deletions(-) diff --git a/lib/public/components/Filters/RunsFilter/durationFilter.js b/lib/public/components/Filters/RunsFilter/durationFilter.js index b4fb0eec11..876b87f10e 100644 --- a/lib/public/components/Filters/RunsFilter/durationFilter.js +++ b/lib/public/components/Filters/RunsFilter/durationFilter.js @@ -23,7 +23,6 @@ import { comparisonOperatorFilter } from '../common/filters/comparisonOperatorFi */ export const durationFilter = (durationFilterModel, operatorSelectionModel) => { const { hours, minutes, seconds } = durationFilterModel.raw; - const { secondsMin, minutesMin } = durationFilterModel; const hoursInput = h('input.flex-grow', { id: 'hours-input', @@ -38,8 +37,8 @@ export const durationFilter = (durationFilterModel, operatorSelectionModel) => { const minutesInput = h('input.flex-grow', { id: 'minutes-input', type: 'number', - min: minutesMin, - max: 60, + min: 0, + max: 59, value: minutes, oninput: (e) => { const { value } = e.target; @@ -49,8 +48,8 @@ export const durationFilter = (durationFilterModel, operatorSelectionModel) => { const secondsInput = h('input.flex-grow', { id: 'seconds-input', type: 'number', - min: secondsMin, - max: 60, + min: 0, + max: 59, value: seconds, oninput: (e) => { const { value } = e.target; diff --git a/lib/public/components/common/form/inputs/DurationInputModel.js b/lib/public/components/common/form/inputs/DurationInputModel.js index d48a81e11a..87912d8453 100644 --- a/lib/public/components/common/form/inputs/DurationInputModel.js +++ b/lib/public/components/common/form/inputs/DurationInputModel.js @@ -51,31 +51,7 @@ export class DurationInputModel extends Observable { */ update(raw) { try { - const previousVal = this._raw; this._raw = { ...this._raw, ...raw }; - - if (this._raw.seconds === 60) { - this._raw.minutes++; - this._raw.seconds = 0; - } - if (this._raw.minutes === 60) { - this._raw.hours++; - this._raw.minutes = 0; - } - - if (this._raw.seconds === -1) { - this._raw.minutes--; - this._raw.seconds = 59; - } - if (this._raw.minutes === -1) { - this._raw.hours--; - this._raw.minutes = 59; - } - - if (this._raw.hours < 0) { - this.update(previousVal); - return; - } this._value = (this._raw.hours || 0) * 60 * 60 * 1000 + (this._raw.minutes || 0) * 60 * 1000 + @@ -91,20 +67,6 @@ export class DurationInputModel extends Observable { this.notify(); } - /** - * Min input for minutes input - */ - get minutesMin() { - return this._raw.hours > 0 ? -1 : 0; - } - - /** - * Min input for seconds input - */ - get secondsMin() { - return this._raw.minutes > 0 ? -1 : 0; - } - /** * Reset the inputs to its initial state * @return {void} From ff0da598f0aa430acd84ea445ae1e207571d7123 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Wed, 27 Mar 2024 13:36:28 +0100 Subject: [PATCH 26/46] cherry-picked --- .../views/Runs/Overview/RunsOverviewModel.js | 97 +++++++++++++------ 1 file changed, 67 insertions(+), 30 deletions(-) diff --git a/lib/public/views/Runs/Overview/RunsOverviewModel.js b/lib/public/views/Runs/Overview/RunsOverviewModel.js index b2da297662..65e62ac80b 100644 --- a/lib/public/views/Runs/Overview/RunsOverviewModel.js +++ b/lib/public/views/Runs/Overview/RunsOverviewModel.js @@ -187,11 +187,11 @@ export class RunsOverviewModel extends OverviewPageModel { this._triggerValuesFilters = new Set(); - this._nDetectorsFilterModel.reset(); + this.nDetectorsFilter = null; - this._nEpnsFilterModel.reset(); + this._nEpnsFilter = null; - this._nFlpsFilterModel.reset(); + this.nFlpsFilter = null; this._runDurationFilterModel.reset(); this._runDurationOperatorSelectionModel.reset(); @@ -235,9 +235,9 @@ export class RunsOverviewModel extends OverviewPageModel { || this.environmentIdsFilter !== '' || this.runQualitiesFilters.length !== 0 || this._triggerValuesFilters.size !== 0 - || this._nDetectorsFilterModel.value !== null - || this._nEpnsFilterModel.value !== null - || this._nFlpsFilterModel.value !== null + || this.nDetectorsFilter !== null + || this._nEpnsFilter !== null + || this.nFlpsFilter !== null || this.ddflpFilter !== '' || this.dcsFilter !== '' || this.epnFilter !== '' @@ -300,13 +300,13 @@ export class RunsOverviewModel extends OverviewPageModel { if (this._triggerValuesFilters.size !== 0) { this.activeFilters.push('Trigger Value'); } - if (this._nDetectorsFilterModel.value !== null) { + if (this.nDetectorsFilter !== null) { this.activeFilters.push('# of detectors'); } - if (this._nEpnsFilterModel.value !== null) { + if (this._nEpnsFilter !== null) { this.activeFilters.push('# of epns'); } - if (this._nFlpsFilterModel.value !== null) { + if (this.nFlpsFilter !== null) { this.activeFilters.push('# of flps'); } if (this.ddflpFilter !== '') { @@ -622,27 +622,64 @@ export class RunsOverviewModel extends OverviewPageModel { } /** - * Returns the detectors amount filter model - * @return {NumericFilterModel} the amount filter moment + * Returns the amount of detectors filters + * @return {{operator: string, limit: (number|null)}|null} The current amount of detectors filters */ - get nDetectorsFilterModel() { - return this._nDetectorsFilterModel; + getNDetectorsFilter() { + return this.nDetectorsFilter; } /** - * Returns the EPNs amount filter model - * @return {NumericFilterModel} The current amount of epns filters + * Sets the limit of detectors and the comparison operator to filter if no new inputs were detected for 200 + * milliseconds + * + * @param {{operator: string, limit: (number|null)}|null} newNDetectors The new filter value + * + * @return {void} + */ + setNDetectorsFilter(newNDetectors) { + this.nDetectorsFilter = newNDetectors; + this._applyFilters(); + } + + /** + * Returns the current amount of epns filter + * @return {{operator: string, limit: (number|null)}|null} The current amount of epns filters + */ + get nEpnsFilter() { + return this._nEpnsFilter; + } + + /** + * Returns the current amount of flps filter + * @return {{operator: string, limit: (number|null)}|null} The current amount of flps filters */ - get nEpnsFilterModel() { - return this._nEpnsFilterModel; + getNFlpsFilter() { + return this.nFlpsFilter; } /** - * Returns the FLPs amount filter model - * @return {NumericFilterModel} the amount filter moment + * Sets the limit of epns and the comparison operator to filter if no new inputs were detected for 200 milliseconds + * + * @param {{operator: string, limit: (number|null)}|null} newNEpns The new filter value + * + * @return {void} + */ + set nEpnsFilter(newNEpns) { + this._nEpnsFilter = newNEpns; + this._applyFilters(); + } + + /** + * Sets the limit of flps and the comparison operator to filter if no new inputs were detected for 200 milliseconds + * + * @param {{operator: string, limit: (number|null)}|null} newNFlps The new filter value + * + * @return {void} */ - get nFlpsFilterModel() { - return this._nFlpsFilterModel; + setNFlpsFilter(newNFlps) { + this.nFlpsFilter = newNFlps; + this._applyFilters(); } /** @@ -893,17 +930,17 @@ export class RunsOverviewModel extends OverviewPageModel { ...this._triggerValuesFilters.size !== 0 && { 'filter[triggerValues]': Array.from(this._triggerValuesFilters).join(), }, - ...this._nDetectorsFilterModel.value !== null && { - 'filter[nDetectors][operator]': this._nDetectorsFilterModel.operatorSelectionModel.selected, - 'filter[nDetectors][limit]': this._nDetectorsFilterModel.value, + ...this.nDetectorsFilter && this.nDetectorsFilter.limit !== null && { + 'filter[nDetectors][operator]': this.nDetectorsFilter.operator, + 'filter[nDetectors][limit]': this.nDetectorsFilter.limit, }, - ...this.nFlpsFilterModel.value && { - 'filter[nFlps][operator]': this._nFlpsFilterModel.operatorSelectionModel.selected, - 'filter[nFlps][limit]': this._nFlpsFilterModel.value, + ...this.nFlpsFilter && this.nFlpsFilter.limit !== null && { + 'filter[nFlps][operator]': this.nFlpsFilter.operator, + 'filter[nFlps][limit]': this.nFlpsFilter.limit, }, - ...this._nEpnsFilterModel.value && { - 'filter[nEpns][operator]': this._nEpnsFilterModel.operatorSelectionModel.selected, - 'filter[nEpns][limit]': this._nEpnsFilterModel.value, + ...this.nEpnsFilter && this.nEpnsFilter.limit !== null && { + 'filter[nEpns][operator]': this.nEpnsFilter.operator, + 'filter[nEpns][limit]': this.nEpnsFilter.limit, }, ...(this.ddflpFilter === true || this.ddflpFilter === false) && { 'filter[ddflp]': this.ddflpFilter, From 7d57685ff9fa9899e06c1843eb6ecce30a7c907b Mon Sep 17 00:00:00 2001 From: xsalonx Date: Wed, 27 Mar 2024 13:40:23 +0100 Subject: [PATCH 27/46] cherry -pick --- .../Filters/RunsFilter/nDetectors.js | 30 ++++++++++++++++ .../components/Filters/RunsFilter/nEpns.js | 36 +++++++++++++++++++ .../components/Filters/RunsFilter/nFlps.js | 30 ++++++++++++++++ .../Runs/ActiveColumns/runsActiveColumns.js | 34 +++++------------- 4 files changed, 104 insertions(+), 26 deletions(-) create mode 100644 lib/public/components/Filters/RunsFilter/nDetectors.js create mode 100644 lib/public/components/Filters/RunsFilter/nEpns.js create mode 100644 lib/public/components/Filters/RunsFilter/nFlps.js diff --git a/lib/public/components/Filters/RunsFilter/nDetectors.js b/lib/public/components/Filters/RunsFilter/nDetectors.js new file mode 100644 index 0000000000..c5941f1608 --- /dev/null +++ b/lib/public/components/Filters/RunsFilter/nDetectors.js @@ -0,0 +1,30 @@ +/** + * @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 { amountFilter } from '../common/filters/amountFilter.js'; + +/** + * Returns the author filter component + * @param {RunsOverviewModel} runModel the runs model object + * @return {vnode} A text box that lets the user look for logs with a specific author + */ +const nDetectorsFilter = (runModel) => amountFilter(runModel.getNDetectorsFilter(), (filter) => runModel.setNDetectorsFilter(filter), { + operatorAttributes: { + id: 'nDetectors-operator', + }, + limitAttributes: { + id: 'nDetectors-limit', + }, +}); + +export default nDetectorsFilter; diff --git a/lib/public/components/Filters/RunsFilter/nEpns.js b/lib/public/components/Filters/RunsFilter/nEpns.js new file mode 100644 index 0000000000..069a1dbaa8 --- /dev/null +++ b/lib/public/components/Filters/RunsFilter/nEpns.js @@ -0,0 +1,36 @@ +/** + * @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 { amountFilter } from '../common/filters/amountFilter.js'; + +/** + * Returns the nEpns filter component + * @param {RunsOverviewModel} runModel The runs model object + * @return {vnode} A text box and operator that lets the user look for logs with a specific number of EPNs + */ +const nEpnsFilter = (runModel) => amountFilter( + runModel.nEpnsFilter, + (filter) => { + runModel.nEpnsFilter = filter; + }, + { + operatorAttributes: { + id: 'nEpns-operator', + }, + limitAttributes: { + id: 'nEpns-limit', + }, + }, +); + +export default nEpnsFilter; diff --git a/lib/public/components/Filters/RunsFilter/nFlps.js b/lib/public/components/Filters/RunsFilter/nFlps.js new file mode 100644 index 0000000000..8144d85a96 --- /dev/null +++ b/lib/public/components/Filters/RunsFilter/nFlps.js @@ -0,0 +1,30 @@ +/** + * @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 { amountFilter } from '../common/filters/amountFilter.js'; + +/** + * Returns the author filter component + * @param {RunsOverviewModel} runModel the run model object + * @return {vnode} A text box that lets the user look for logs with a specific author + */ +const nFlpsFilter = (runModel) => amountFilter(runModel.getNFlpsFilter(), (filter) => runModel.setNFlpsFilter(filter), { + operatorAttributes: { + id: 'nFlps-operator', + }, + limitAttributes: { + id: 'nFlps-limit', + }, +}); + +export default nFlpsFilter; diff --git a/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js b/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js index 0a29955cbb..48723a17e1 100644 --- a/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js +++ b/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js @@ -49,7 +49,9 @@ import { formatTagsList } from '../../Tags/format/formatTagsList.js'; import { buttonLinkWithDropdown } from '../../../components/common/selection/infoLoggerButtonGroup/buttonLinkWithDropdown.js'; import { CopyToClipboardComponent } from '../../../components/common/selection/infoLoggerButtonGroup/CopyToClipboardComponent.js'; import { infologgerLinksComponents } from '../../../components/common/infologger/infologgerLinksComponents.js'; -import { amountFilter } from '../../../components/Filters/common/filters/amountFilter.js'; +import nDetectorsFilter from '../../../components/Filters/RunsFilter/nDetectors.js'; +import nEpnsFilter from '../../../components/Filters/RunsFilter/nEpns.js'; +import nFlpsFilter from '../../../components/Filters/RunsFilter/nFlps.js'; /** * List of active columns for a generic runs table @@ -289,43 +291,23 @@ export const runsActiveColumns = { name: 'DETs #', visible: false, classes: 'w-2 f6 w-wrapped', - filter: ({ nDetectorsFilterModel }) => amountFilter(nDetectorsFilterModel, { - operatorAttributes: { - id: 'nDetectors-operator', - }, - operandAttributes: { - id: 'nDetectors-limit', - }, - }), + filter: nDetectorsFilter, }, nEpns: { name: 'EPNs #', visible: true, profiles: [profiles.none, 'lhcFill', 'environment'], classes: 'w-2 f6 w-wrapped', - format: (nEpns, run) => run.epn ? typeof nEpns === 'number' ? nEpns : 'ON' : 'OFF', - filter: ({ nEpnsFilterModel }) => amountFilter(nEpnsFilterModel, { - operatorAttributes: { - id: 'nEpns-operator', - }, - operandAttributes: { - id: 'nEpns-limit', - }, - }), + // eslint-disable-next-line no-extra-parens + format: (nEpns, run) => run.epn ? (typeof nEpns === 'number' ? nEpns : 'ON') : 'OFF', + filter: nEpnsFilter, }, nFlps: { name: 'FLPs #', visible: true, profiles: [profiles.none, 'lhcFill', 'environment'], classes: 'w-2 f6 w-wrapped', - filter: ({ nFlpsFilterModel }) => amountFilter(nFlpsFilterModel, { - operatorAttributes: { - id: 'nFlps-operator', - }, - operandAttributes: { - id: 'nFlps-limit', - }, - }), + filter: nFlpsFilter, }, nSubtimeframes: { name: '# of STFs', From 71cb30de283ecb3f6e41aa25989ff7cbe0e9c234 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Wed, 27 Mar 2024 13:41:05 +0100 Subject: [PATCH 28/46] rm --- .../common/filters/NumericFilterModel.js | 89 ------------------- 1 file changed, 89 deletions(-) delete mode 100644 lib/public/components/Filters/common/filters/NumericFilterModel.js diff --git a/lib/public/components/Filters/common/filters/NumericFilterModel.js b/lib/public/components/Filters/common/filters/NumericFilterModel.js deleted file mode 100644 index 0007999daa..0000000000 --- a/lib/public/components/Filters/common/filters/NumericFilterModel.js +++ /dev/null @@ -1,89 +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 { ComparisonOperatorSelectionModel } from './ComparisonOperatorSelectionModel.js'; -import { Observable } from '/js/src/index.js'; - -/** - * Model storing state of a expected value of something with respect of comprison operator - */ -export class NumericFilterModel extends Observable { - /** - * Constructor - * @param {object} configuration configration - * @param {number} [configration.minValue] minimal value allowed in model - * @param {number} [configration.maxValue] maximum value allowed in model - * @param {number} [configration.defaultOperator] defaultOperator one of ['<', '<=', '=', '>=', '>'] operators - */ - constructor({ min, max, defaultOperator }) { - super(); - - this._minValue = min; - this._maxValue = max; - - this._operatorSelectionModel = new ComparisonOperatorSelectionModel(defaultOperator); - this._operatorSelectionModel.observe(() => { - if (this._value !== null) { - this.notify(); - } - }); - - this._value = null; - } - - /** - * Get minimum allowed value - */ - get minValue() { - return this._minValue; - } - - /** - * Get maximum allowed value - */ - get maxValue() { - return this._maxValue; - } - - /** - * Reset to default - * @return {void} - */ - reset() { - this._value = null; - this._operatorSelectionModel.reset(); - } - - /** - * Get value - operand to comparison - */ - get value() { - return this._value; - } - - /** - * Set current value of operand - * @param {number} value value - */ - set value(value) { - this._value = value; - this.notify(); - } - - /** - * Get operator selection model - */ - get operatorSelectionModel() { - return this._operatorSelectionModel; - } -} From 14fc5a8002eada60dd4f36659746c834fb93febc Mon Sep 17 00:00:00 2001 From: xsalonx Date: Wed, 27 Mar 2024 13:42:19 +0100 Subject: [PATCH 29/46] cleanup --- lib/public/views/Runs/ActiveColumns/runsActiveColumns.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js b/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js index 48723a17e1..bbd7b9bec7 100644 --- a/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js +++ b/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js @@ -16,6 +16,8 @@ import runNumberFilter from '../../../components/Filters/RunsFilter/runNumber.js import o2startFilter from '../../../components/Filters/RunsFilter/o2start.js'; import o2endFilter from '../../../components/Filters/RunsFilter/o2stop.js'; import environmentIdFilter from '../../../components/Filters/RunsFilter/environmentId.js'; +import nDetectorsFilter from '../../../components/Filters/RunsFilter/nDetectors.js'; +import nFlpsFilter from '../../../components/Filters/RunsFilter/nFlps.js'; import odcTopologyFullName from '../../../components/Filters/RunsFilter/odcTopologyFullName.js'; import { displayRunEorReasonsOverview } from '../format/displayRunEorReasonOverview.js'; import { detectorsFilterComponent } from '../../../components/Filters/RunsFilter/detectorsFilterComponent.js'; @@ -28,6 +30,7 @@ import { durationFilter } from '../../../components/Filters/RunsFilter/durationF import { displayRunDuration } from '../format/displayRunDuration.js'; import fillNumbersFilter from '../../../components/Filters/RunsFilter/fillNumbers.js'; import { frontLink } from '../../../components/common/navigation/frontLink.js'; +import nEpnsFilter from '../../../components/Filters/RunsFilter/nEpns.js'; import triggerValueFilter from '../../../components/Filters/RunsFilter/triggerValue.js'; import lhcPeriodsFilter from '../../../components/Filters/RunsFilter/lhcPeriod.js'; import { formatRunType } from '../../../utilities/formatting/formatRunType.js'; @@ -49,9 +52,6 @@ import { formatTagsList } from '../../Tags/format/formatTagsList.js'; import { buttonLinkWithDropdown } from '../../../components/common/selection/infoLoggerButtonGroup/buttonLinkWithDropdown.js'; import { CopyToClipboardComponent } from '../../../components/common/selection/infoLoggerButtonGroup/CopyToClipboardComponent.js'; import { infologgerLinksComponents } from '../../../components/common/infologger/infologgerLinksComponents.js'; -import nDetectorsFilter from '../../../components/Filters/RunsFilter/nDetectors.js'; -import nEpnsFilter from '../../../components/Filters/RunsFilter/nEpns.js'; -import nFlpsFilter from '../../../components/Filters/RunsFilter/nFlps.js'; /** * List of active columns for a generic runs table From 6dda2bc600d6e5cb95458d54ba6abfe2c2606658 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Wed, 27 Mar 2024 13:44:01 +0100 Subject: [PATCH 30/46] cl --- .../Filters/common/filters/amountFilter.js | 58 +++++++++++++------ 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/lib/public/components/Filters/common/filters/amountFilter.js b/lib/public/components/Filters/common/filters/amountFilter.js index ab105c8264..bc9a0fec4c 100644 --- a/lib/public/components/Filters/common/filters/amountFilter.js +++ b/lib/public/components/Filters/common/filters/amountFilter.js @@ -14,36 +14,60 @@ import { comparisonOperatorFilter } from './comparisonOperatorFilter.js'; import { h } from '/js/src/index.js'; +/** + * @callback filterChangeHandler + * @param {{operator: string, limit: (number|number)}|null} newFilter the new filter value + */ + /** * Returns a component which provide an amount filter, allowing to choose a limit and the comparison operator to apply * - * @param {NumericFilterModel} numericFilterModel filter model - * @param {{operatorAttributes: (object|undefined), operandAttributes: (object|undefined)}} options eventual options to configure the filter: use - * `operatorAttributes` to define the attributes of the operator selection component and `operandAttributes` to define the attributes of the - * operand input component + * @param {{operator: string, limit: (number|null)}|null} currentValue the operator to use to compare the limit to the actual + * values + * @param {filterChangeHandler} onChange callback called any time the operator OR the limit changes + * @param {{operatorAttributes: (object|undefined), limitAttributes: (object|undefined)}} options eventual options to configure the filter: use + * `operatorAttributes` to define the attributes of the operator selection component and `limitAttributes` to define the attributes of the + * limit input component * - * @return {Component} the filter component + * @return {vnode} the component */ -export const amountFilter = (numericFilterModel, options) => { - const { operatorSelectionModel } = numericFilterModel; +export const amountFilter = (currentValue, onChange, options) => { + const { operator, limit } = currentValue || { operator: '=', limit: null }; + const { operatorAttributes = {}, limitAttributes = {} } = options; + + // eslint-disable-next-line require-jsdoc + const updateFilter = ({ newOperator, newLimit }) => { + onChange({ + operator: newOperator || operator, + limit: newLimit !== undefined ? newLimit : limit, + }); + }; return comparisonOperatorFilter( h('input.flex-grow', { type: 'number', - min: numericFilterModel.minValue, - max: numericFilterModel.maxValue, - value: numericFilterModel.value, + min: 0, + value: limit, oninput: (e) => { - const { value } = e.target; - if (value === '') { - numericFilterModel.value = null; + let newLimit; + + if (e.target.value === '') { + newLimit = null; } else { - numericFilterModel.value = Number(value); + const value = parseInt(e.target.value, 10); + if (!isNaN(value)) { + newLimit = value; + } + } + + if (newLimit !== undefined && newLimit !== limit) { + updateFilter({ newLimit }); } }, - ...options.operandAttributes, + ...limitAttributes, }, ''), - operatorSelectionModel, - options.operatorAttributes, + operator, + (newOperator) => updateFilter({ newOperator }), + operatorAttributes, ); }; From e639db8b629a59745c4ca66295b3a34efff8c723 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Wed, 27 Mar 2024 13:47:56 +0100 Subject: [PATCH 31/46] revoke --- .../filters/comparisonOperatorFilter.js | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js b/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js index 74220c0e54..d20dca35dc 100644 --- a/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js +++ b/lib/public/components/Filters/common/filters/comparisonOperatorFilter.js @@ -13,22 +13,33 @@ import { h } from '/js/src/index.js'; +/** + * Handle an operator change event + * + * @callback operatorChangeHandler + * + * @param {string} operator the new operator value + * + * @return {*} + */ + /** * Wrap a given input inside a comparison operator filter, prefixing the input by an operator selector * - * @param {Compoenent} operandInput the input for the operand value (the one to wrap) - * @param {ComparisonOperatorSelectionModel} operatorSelectionModel the operator selection model + * @param {*} limitInput the input for the limit value (the one to wrap) + * @param {*} currentLimit the current limit value + * @param {operatorChangeHandler} onOperatorChange callback called when the operator changes * @param {*} [operatorAttributes] the list of attributes to apply on the operator selector (note that value and * onchange attribute are override) * - * @return {ComponenT} the filter component + * @return {vnode} the filter component */ -export const comparisonOperatorFilter = (operandInput, operatorSelectionModel, operatorAttributes = {}) => h('.flex-row.g3', [ +export const comparisonOperatorFilter = (limitInput, currentLimit, onOperatorChange, operatorAttributes = {}) => h('.flex-row.g3', [ h('', h('select.form-control', { ...operatorAttributes, - value: operatorSelectionModel.selected[0], - onchange: (e) => operatorSelectionModel.select(e.target.value), - }, operatorSelectionModel.options?.map(({ value }) => h('option', { - value, - }, value)))), operandInput, + value: currentLimit, + onchange: (e) => onOperatorChange(e.target.value), + }, ['<', '<=', '=', '>=', '>'].map((operator) => h('option', { + value: operator, + }, operator)))), limitInput, ]); From 1d5e0c7d27a3b4af778733d45af28ae2456f72c2 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Wed, 27 Mar 2024 13:58:34 +0100 Subject: [PATCH 32/46] remove --- .../components/Filters/RunsFilter/durationFilter.js | 6 +++++- lib/public/views/Runs/Overview/RunsOverviewModel.js | 10 ---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/lib/public/components/Filters/RunsFilter/durationFilter.js b/lib/public/components/Filters/RunsFilter/durationFilter.js index 876b87f10e..2bb49927ed 100644 --- a/lib/public/components/Filters/RunsFilter/durationFilter.js +++ b/lib/public/components/Filters/RunsFilter/durationFilter.js @@ -70,5 +70,9 @@ export const durationFilter = (durationFilterModel, operatorSelectionModel) => { ]), ]); - return comparisonOperatorFilter(inputs, operatorSelectionModel); + return comparisonOperatorFilter( + inputs, + operatorSelectionModel.selected[0], + (operator) => operatorSelectionModel.select(operator), + ); }; diff --git a/lib/public/views/Runs/Overview/RunsOverviewModel.js b/lib/public/views/Runs/Overview/RunsOverviewModel.js index 65e62ac80b..759cf860e4 100644 --- a/lib/public/views/Runs/Overview/RunsOverviewModel.js +++ b/lib/public/views/Runs/Overview/RunsOverviewModel.js @@ -24,7 +24,6 @@ import { getRemoteDataSlice } from '../../../utilities/fetch/getRemoteDataSlice. import { DurationInputModel } from '../../../components/common/form/inputs/DurationInputModel.js'; import { ComparisonOperatorSelectionModel } from '../../../components/Filters/common/filters/ComparisonOperatorSelectionModel.js'; -import { NumericFilterModel } from '../../../components/Filters/common/filters/NumericFilterModel.js'; /** * Model representing handlers for runs page @@ -64,15 +63,6 @@ export class RunsOverviewModel extends OverviewPageModel { } }); - this._nEpnsFilterModel = new NumericFilterModel({ min: 0 }); - this._nEpnsFilterModel.observe(() => this._applyFilters()); - - this._nDetectorsFilterModel = new NumericFilterModel({ min: 0 }); - this._nDetectorsFilterModel.observe(() => this._applyFilters()); - - this._nFlpsFilterModel = new NumericFilterModel({ min: 0 }); - this._nFlpsFilterModel.observe(() => this._applyFilters()); - // Export items this._allRuns = RemoteData.NotAsked(); From cd9d679210c341c2e854cffbc31f48fce8ff10aa Mon Sep 17 00:00:00 2001 From: xsalonx Date: Thu, 30 May 2024 12:13:36 +0200 Subject: [PATCH 33/46] ref --- .../common/form/inputs/DurationInputModel.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/public/components/common/form/inputs/DurationInputModel.js b/lib/public/components/common/form/inputs/DurationInputModel.js index 87912d8453..5e39da4ae9 100644 --- a/lib/public/components/common/form/inputs/DurationInputModel.js +++ b/lib/public/components/common/form/inputs/DurationInputModel.js @@ -52,15 +52,16 @@ export class DurationInputModel extends Observable { update(raw) { try { this._raw = { ...this._raw, ...raw }; - this._value = - (this._raw.hours || 0) * 60 * 60 * 1000 + - (this._raw.minutes || 0) * 60 * 1000 + - (this._raw.seconds || 0) * 1000; - - if (this._value === 0) { - this.reset(); + const { hours, minutes, seconds } = this._values; + if (hours ?? minutes ?? seconds ?? null === null) { + this._value = null; + } else { + this._value = + (this._raw.hours || 0) * 60 * 60 * 1000 + + (this._raw.minutes || 0) * 60 * 1000 + + (this._raw.seconds || 0) * 1000; } - } catch (_) { + } catch { this._value = null; } From c4c9fc7e25c690f367e2e04c349e95192f0bb052 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Thu, 30 May 2024 12:17:40 +0200 Subject: [PATCH 34/46] ch --- lib/public/components/Filters/RunsFilter/durationFilter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/public/components/Filters/RunsFilter/durationFilter.js b/lib/public/components/Filters/RunsFilter/durationFilter.js index 2bb49927ed..618a388f20 100644 --- a/lib/public/components/Filters/RunsFilter/durationFilter.js +++ b/lib/public/components/Filters/RunsFilter/durationFilter.js @@ -57,7 +57,7 @@ export const durationFilter = (durationFilterModel, operatorSelectionModel) => { }, }, 's'); - const inputs = h('.flex-row.w-40', [ + const inputs = h('.flex-row.w-100', [ hoursInput, minutesInput, secondsInput, From b59e0d2e6a379ef636301464cddd2e735ed222df Mon Sep 17 00:00:00 2001 From: xsalonx Date: Thu, 30 May 2024 12:36:20 +0200 Subject: [PATCH 35/46] add model --- .../common/filters/DurationFilterModel.js | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 lib/public/components/Filters/common/filters/DurationFilterModel.js diff --git a/lib/public/components/Filters/common/filters/DurationFilterModel.js b/lib/public/components/Filters/common/filters/DurationFilterModel.js new file mode 100644 index 0000000000..6fc7f186b2 --- /dev/null +++ b/lib/public/components/Filters/common/filters/DurationFilterModel.js @@ -0,0 +1,51 @@ +/** + * @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 { Observable } from '/js/src/index.js'; + +const { DurationInputModel } = require('../../../common/form/inputs/DurationInputModel.js'); +const { ComparisonOperatorSelectionModel } = require('./ComparisonOperatorSelectionModel.js'); + +/** + * Duration filter model which stores time value and selected operator + */ +export class DurationFilterModel extends Observable { + /** + * Constructor + */ + constructor() { + super(); + this._durationInputModel = new DurationInputModel(); + this._durationInputModel.bubbleTo(this); + this._operatorSelectionModel = new ComparisonOperatorSelectionModel(); + this._operatorSelectionModel.bubbleTo(this); + } + + /** + * Retrun duration input model + * + * @return {DurationInputModel} duration input model + */ + get durationInputModel() { + return this._durationInputModel; + } + + /** + * Return operator selection model + * + * @return {ComparisonOperatorSelectionModel} operator selection model + */ + get operatorSelectionModel() { + return this._operatorSelectionModel; + } +} From a4933f2b17002f557cbdce110d6c3094958f92ab Mon Sep 17 00:00:00 2001 From: xsalonx Date: Thu, 30 May 2024 12:58:07 +0200 Subject: [PATCH 36/46] ref WIP --- .../common/filters/DurationFilterModel.js | 38 ++++++++++++++++++- .../views/Runs/Overview/RunsOverviewModel.js | 36 +++++------------- 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/lib/public/components/Filters/common/filters/DurationFilterModel.js b/lib/public/components/Filters/common/filters/DurationFilterModel.js index 6fc7f186b2..db89290102 100644 --- a/lib/public/components/Filters/common/filters/DurationFilterModel.js +++ b/lib/public/components/Filters/common/filters/DurationFilterModel.js @@ -25,10 +25,27 @@ export class DurationFilterModel extends Observable { */ constructor() { super(); + this._visualChange$ = new Observable(); + this._durationInputModel = new DurationInputModel(); this._durationInputModel.bubbleTo(this); this._operatorSelectionModel = new ComparisonOperatorSelectionModel(); - this._operatorSelectionModel.bubbleTo(this); + this._operatorSelectionModel.observe(() => { + if (this._durationInputModel.value === null) { + this._visualChange$.notify(); + } else { + this.notify(); + } + }); + } + + /** + * Returns the observable notified any time there is a visual change which has no impact on the actual filter value + * + * @return {Observable} the observable + */ + get visualChange$() { + return this._visualChange$; } /** @@ -48,4 +65,23 @@ export class DurationFilterModel extends Observable { get operatorSelectionModel() { return this._operatorSelectionModel; } + + /** + * States if the filter has been filled + * + * @return {boolean} true if the filter has been filled + */ + isEmpty() { + return this._durationInputModel.value !== null; + } + + /** + * Reset the filter to its default value + * + * @return {void} + */ + reset() { + this._durationInputModel.reset(); + this._operatorSelectionModel.reset(); + } } diff --git a/lib/public/views/Runs/Overview/RunsOverviewModel.js b/lib/public/views/Runs/Overview/RunsOverviewModel.js index 759cf860e4..8ebf2f54d8 100644 --- a/lib/public/views/Runs/Overview/RunsOverviewModel.js +++ b/lib/public/views/Runs/Overview/RunsOverviewModel.js @@ -21,9 +21,7 @@ import { EorReasonFilterModel } from '../../../components/Filters/RunsFilter/Eor import pick from '../../../utilities/pick.js'; import { OverviewPageModel } from '../../../models/OverviewModel.js'; import { getRemoteDataSlice } from '../../../utilities/fetch/getRemoteDataSlice.js'; -import { DurationInputModel } from '../../../components/common/form/inputs/DurationInputModel.js'; -import { ComparisonOperatorSelectionModel } - from '../../../components/Filters/common/filters/ComparisonOperatorSelectionModel.js'; +import { DurationFilterModel } from '../../../components/Filters/common/filters/DurationFilterModel.js'; /** * Model representing handlers for runs page @@ -52,16 +50,11 @@ export class RunsOverviewModel extends OverviewPageModel { this._eorReasonsFilterModel = new EorReasonFilterModel(); this._eorReasonsFilterModel.observe(() => this._applyFilters()); - this._eorReasonsFilterModel.visualChange$.observe(() => this.notify()); + this._eorReasonsFilterModel.visualChange$.bubbleTo(this); - this._runDurationFilterModel = new DurationInputModel(); + this._runDurationFilterModel = new DurationFilterModel(); this._runDurationFilterModel.observe(() => this._applyFilters()); - this._runDurationOperatorSelectionModel = new ComparisonOperatorSelectionModel(); - this._runDurationOperatorSelectionModel.observe(() => { - if (this._runDurationFilterModel.value !== null) { - this._applyFilters(); - } - }); + this._runDurationFilterModel.visualChange$.bubbleTo(this); // Export items this._allRuns = RemoteData.NotAsked(); @@ -184,7 +177,6 @@ export class RunsOverviewModel extends OverviewPageModel { this.nFlpsFilter = null; this._runDurationFilterModel.reset(); - this._runDurationOperatorSelectionModel.reset(); this.ddflpFilter = ''; @@ -220,7 +212,7 @@ export class RunsOverviewModel extends OverviewPageModel { || this.o2endFilterTo !== '' || this.o2endFilterToTime !== '23:59' || this.o2endFilterFromTime !== '00:00' - || this._runDurationFilterModel.value !== null + || !this._runDurationFilterModel.isEmpty() || this._lhcPeriodsFilter !== null || this.environmentIdsFilter !== '' || this.runQualitiesFilters.length !== 0 @@ -275,7 +267,7 @@ export class RunsOverviewModel extends OverviewPageModel { if (this.o2endFilterTo !== '') { this.activeFilters.push('O2 End to'); } - if (this._runDurationFilterModel.value !== null) { + if (!this._runDurationFilterModel.isEmpty()) { this.activeFilters.push('Run duration'); } if (this._lhcPeriodsFilter !== null) { @@ -526,20 +518,12 @@ export class RunsOverviewModel extends OverviewPageModel { /** * Returns the run duration filter model - * @return {DurationInputModel} The current run duration filter model + * @return {DurationFilterModel} The current run duration filter model */ get runDurationFilterModel() { return this._runDurationFilterModel; } - /** - * Returns the run duration filter operator selection model - * @return {ComparisonOperatorSelectionModel} selection model - */ - get runDurationOperatorSelectionModel() { - return this._runDurationOperatorSelectionModel; - } - /** * Returns the current environment id(s) filter * @return {String} The current environment id(s) filter @@ -904,9 +888,9 @@ export class RunsOverviewModel extends OverviewPageModel { 'filter[o2end][to]': new Date(`${this.o2endFilterTo.replace(/\//g, '-')}T${this.o2endFilterToTime}:59.999`).getTime(), }, - ...this._runDurationFilterModel.value && { - 'filter[runDuration][operator]': this._runDurationOperatorSelectionModel.selected, - 'filter[runDuration][limit]': this._runDurationFilterModel.value, + ...!this._runDurationFilterModel.isEmpty() && { + 'filter[runDuration][operator]': this._runDurationFilterModel.operatorSelectionModel.selected[0], + 'filter[runDuration][limit]': this._runDurationFilterModel.durationInputModel.value, }, ...this._lhcPeriodsFilter && { 'filter[lhcPeriods]': this._lhcPeriodsFilter, From 5d6c9414a0dd04ec78090f38e71d3f0fa686f00e Mon Sep 17 00:00:00 2001 From: xsalonx Date: Thu, 30 May 2024 13:30:21 +0200 Subject: [PATCH 37/46] ref --- .../Filters/RunsFilter/durationFilter.js | 42 +++++++++++-------- .../common/filters/DurationFilterModel.js | 6 +-- .../common/form/inputs/DurationInputModel.js | 8 ++-- .../Runs/ActiveColumns/runsActiveColumns.js | 3 +- 4 files changed, 33 insertions(+), 26 deletions(-) diff --git a/lib/public/components/Filters/RunsFilter/durationFilter.js b/lib/public/components/Filters/RunsFilter/durationFilter.js index 618a388f20..03321b85e2 100644 --- a/lib/public/components/Filters/RunsFilter/durationFilter.js +++ b/lib/public/components/Filters/RunsFilter/durationFilter.js @@ -15,24 +15,38 @@ import { h } from '/js/src/index.js'; import { comparisonOperatorFilter } from '../common/filters/comparisonOperatorFilter.js'; /** - * Returns the run duration filter component + * Returns the duration filter component * - * @param {DurationInputModel} durationFilterModel the duration input model - * @param {ComparisonOperatorSelectionModel} operatorSelectionModel the comparison operator selection model + * @param {DurationFilterModel} durationFilterModel the duration filter model * @return {Component} the duration filter */ -export const durationFilter = (durationFilterModel, operatorSelectionModel) => { - const { hours, minutes, seconds } = durationFilterModel.raw; +export const durationFilter = (durationFilterModel) => { + const { durationInputModel, operatorSelectionModel } = durationFilterModel; + + const { hours, minutes, seconds } = durationInputModel.raw; + + /** + * Return oninput handler for given time unit + * + * @param {'hours'|'minutes'|'seconds'} unitName time unit + * @return {function(InputEvent, void)} oninput handler for input for given time unit + */ + const updateInputModelByTimeUnit = (unitName) => (e) => { + const { value } = e.target; + const parsedValue = Number(value); + if (value.length > 0 && 0 <= parsedValue && parsedValue < 60) { + durationInputModel.update({ [unitName]: parsedValue }); + } else { + durationFilterModel.visualChange$.notify(); + } + }; const hoursInput = h('input.flex-grow', { id: 'hours-input', type: 'number', min: 0, value: hours, - oninput: (e) => { - const { value } = e.target; - durationFilterModel.update({ hours: value.length === 0 ? null : Number(value) }); - }, + oninput: updateInputModelByTimeUnit('hours'), }); const minutesInput = h('input.flex-grow', { id: 'minutes-input', @@ -40,10 +54,7 @@ export const durationFilter = (durationFilterModel, operatorSelectionModel) => { min: 0, max: 59, value: minutes, - oninput: (e) => { - const { value } = e.target; - durationFilterModel.update({ minutes: value.length === 0 ? null : Number(value) }); - }, + oninput: updateInputModelByTimeUnit('minutes'), }, 'm'); const secondsInput = h('input.flex-grow', { id: 'seconds-input', @@ -51,10 +62,7 @@ export const durationFilter = (durationFilterModel, operatorSelectionModel) => { min: 0, max: 59, value: seconds, - oninput: (e) => { - const { value } = e.target; - durationFilterModel.update({ seconds: value.length === 0 ? null : Number(value) }); - }, + oninput: updateInputModelByTimeUnit('seconds'), }, 's'); const inputs = h('.flex-row.w-100', [ diff --git a/lib/public/components/Filters/common/filters/DurationFilterModel.js b/lib/public/components/Filters/common/filters/DurationFilterModel.js index db89290102..7eaeda7fc5 100644 --- a/lib/public/components/Filters/common/filters/DurationFilterModel.js +++ b/lib/public/components/Filters/common/filters/DurationFilterModel.js @@ -13,8 +13,8 @@ import { Observable } from '/js/src/index.js'; -const { DurationInputModel } = require('../../../common/form/inputs/DurationInputModel.js'); -const { ComparisonOperatorSelectionModel } = require('./ComparisonOperatorSelectionModel.js'); +import { DurationInputModel } from '../../../common/form/inputs/DurationInputModel.js'; +import { ComparisonOperatorSelectionModel } from './ComparisonOperatorSelectionModel.js'; /** * Duration filter model which stores time value and selected operator @@ -72,7 +72,7 @@ export class DurationFilterModel extends Observable { * @return {boolean} true if the filter has been filled */ isEmpty() { - return this._durationInputModel.value !== null; + return this._durationInputModel.value === null; } /** diff --git a/lib/public/components/common/form/inputs/DurationInputModel.js b/lib/public/components/common/form/inputs/DurationInputModel.js index 5e39da4ae9..964c7042e6 100644 --- a/lib/public/components/common/form/inputs/DurationInputModel.js +++ b/lib/public/components/common/form/inputs/DurationInputModel.js @@ -16,8 +16,8 @@ import { Observable } from '/js/src/index.js'; /** * @typedef DurationInputRawData * @property {number} hours the number of hours - * @property {number} minutes the number of minutes - * @property {number} seconds the number of seconds + * @property {number} minutes the number of minutes, from range [0, 59] + * @property {number} seconds the number of seconds, from range [0, 59] */ /** @@ -52,8 +52,8 @@ export class DurationInputModel extends Observable { update(raw) { try { this._raw = { ...this._raw, ...raw }; - const { hours, minutes, seconds } = this._values; - if (hours ?? minutes ?? seconds ?? null === null) { + const { hours, minutes, seconds } = this._raw; + if ((hours ?? minutes ?? seconds ?? null) === null) { this._value = null; } else { this._value = diff --git a/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js b/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js index bbd7b9bec7..8f4ef3800a 100644 --- a/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js +++ b/lib/public/views/Runs/ActiveColumns/runsActiveColumns.js @@ -262,8 +262,7 @@ export const runsActiveColumns = { noEllipsis: true, format: (_duration, run) => displayRunDuration(run), exportFormat: (_duration, run) => formatRunDuration(run), - filter: ({ runDurationFilterModel, runDurationOperatorSelectionModel }) => - durationFilter(runDurationFilterModel, runDurationOperatorSelectionModel), + filter: ({ runDurationFilterModel }) => durationFilter(runDurationFilterModel), }, environmentId: { name: 'Environment ID', From fe670059980a5c8f00cd465622bba7cc75cdc6d2 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Thu, 30 May 2024 13:33:09 +0200 Subject: [PATCH 38/46] fix --- lib/public/components/Filters/RunsFilter/durationFilter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/public/components/Filters/RunsFilter/durationFilter.js b/lib/public/components/Filters/RunsFilter/durationFilter.js index 03321b85e2..a3a4accbf5 100644 --- a/lib/public/components/Filters/RunsFilter/durationFilter.js +++ b/lib/public/components/Filters/RunsFilter/durationFilter.js @@ -34,8 +34,8 @@ export const durationFilter = (durationFilterModel) => { const updateInputModelByTimeUnit = (unitName) => (e) => { const { value } = e.target; const parsedValue = Number(value); - if (value.length > 0 && 0 <= parsedValue && parsedValue < 60) { - durationInputModel.update({ [unitName]: parsedValue }); + if (value.length > 0 || 0 <= parsedValue && parsedValue < 60) { + durationInputModel.update({ [unitName]: value.length > 0 ? parsedValue : null }); } else { durationFilterModel.visualChange$.notify(); } From f165e239cac1e04783a08e4c057e163beaf92dd0 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Thu, 30 May 2024 13:33:35 +0200 Subject: [PATCH 39/46] fix --- .../components/Filters/RunsFilter/durationFilter.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/public/components/Filters/RunsFilter/durationFilter.js b/lib/public/components/Filters/RunsFilter/durationFilter.js index a3a4accbf5..b168062269 100644 --- a/lib/public/components/Filters/RunsFilter/durationFilter.js +++ b/lib/public/components/Filters/RunsFilter/durationFilter.js @@ -31,7 +31,7 @@ export const durationFilter = (durationFilterModel) => { * @param {'hours'|'minutes'|'seconds'} unitName time unit * @return {function(InputEvent, void)} oninput handler for input for given time unit */ - const updateInputModelByTimeUnit = (unitName) => (e) => { + const updateInputModelHandlerByTimeUnit = (unitName) => (e) => { const { value } = e.target; const parsedValue = Number(value); if (value.length > 0 || 0 <= parsedValue && parsedValue < 60) { @@ -46,7 +46,7 @@ export const durationFilter = (durationFilterModel) => { type: 'number', min: 0, value: hours, - oninput: updateInputModelByTimeUnit('hours'), + oninput: updateInputModelHandlerByTimeUnit('hours'), }); const minutesInput = h('input.flex-grow', { id: 'minutes-input', @@ -54,7 +54,7 @@ export const durationFilter = (durationFilterModel) => { min: 0, max: 59, value: minutes, - oninput: updateInputModelByTimeUnit('minutes'), + oninput: updateInputModelHandlerByTimeUnit('minutes'), }, 'm'); const secondsInput = h('input.flex-grow', { id: 'seconds-input', @@ -62,7 +62,7 @@ export const durationFilter = (durationFilterModel) => { min: 0, max: 59, value: seconds, - oninput: updateInputModelByTimeUnit('seconds'), + oninput: updateInputModelHandlerByTimeUnit('seconds'), }, 's'); const inputs = h('.flex-row.w-100', [ From 318d0a5514820fd265be40a3b3ce19720664996d Mon Sep 17 00:00:00 2001 From: xsalonx Date: Thu, 30 May 2024 14:47:32 +0200 Subject: [PATCH 40/46] cleanup --- test/public/defaults.js | 5 ----- test/public/runs/overview.test.js | 12 ++++-------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/test/public/defaults.js b/test/public/defaults.js index 037d10b889..46a21eaf6a 100644 --- a/test/public/defaults.js +++ b/test/public/defaults.js @@ -352,11 +352,6 @@ module.exports.expectInnerTextTo = async (page, selector, validator) => { expect(validator(actualInnerText), `"${actualInnerText}" is invalid with respect of given validator`).to.be.true; }; -module.exports.expectSelectToBe = async (page, selector, selectedOption) => { - await page.waitForSelector(selector); - expect(await (await page.$(selector)).evaluate(({ value }) => value)).to.be.equal(selectedOption); -}; - /** * Expect an element to have a text valid against given validator * @param {Object} page Puppeteer page object. diff --git a/test/public/runs/overview.test.js b/test/public/runs/overview.test.js index 606f74014e..00e538e8fc 100644 --- a/test/public/runs/overview.test.js +++ b/test/public/runs/overview.test.js @@ -23,9 +23,9 @@ const { goToPage, checkColumnBalloon, waitForNetworkIdleAndRedraw, - expectSelectToBe, getAllDataFields, waitForTableDataReload, + expectInputValue, } = require('../defaults'); const { RunDefinition } = require('../../../lib/server/services/run/getRunDefinition.js'); const { RUN_QUALITIES, RunQualities } = require('../../../lib/domain/enums/RunQualities.js'); @@ -545,19 +545,15 @@ module.exports = () => { const operatorSelector = `${runDurationFilterDivSelector} select`; const hoursSelector = `${runDurationFilterDivSelector} input:nth-of-type(1)`; const minutesSelector = `${runDurationFilterDivSelector} input:nth-of-type(2)`; - await expectSelectToBe(page, operatorSelector, '='); + await expectInputValue(page, operatorSelector, '='); await waitForTableDataReload(page, () => fillInput(page, hoursSelector, 26)); let runDurationList = await getAllDataFields(page, 'runDuration'); expect(runDurationList).to.be.lengthOf.greaterThan(0); expect(runDurationList.every((runDuration) => runDuration === '26:00:00')).to.be.true; - await waitForTableDataReload(page, async () => { - await fillInput(page, hoursSelector, 0); - await fillInput(page, minutesSelector, 60); - }); - runDurationList = await getAllDataFields(page, 'runDuration'); - expect(runDurationList).to.be.lengthOf.greaterThan(0); + await fillInput(page, hoursSelector, 0); + await fillInput(page, minutesSelector, 60); expect(runDurationList.every((runDuration) => runDuration === '01:00:00')).to.be.true; await waitForTableDataReload(page, () => page.select(operatorSelector, '>=')); From 7d0539676bc4288695eb5f9ab5e511e0a730960a Mon Sep 17 00:00:00 2001 From: xsalonx Date: Thu, 30 May 2024 16:14:39 +0200 Subject: [PATCH 41/46] test WIP --- test/public/defaults.js | 14 +++++--- test/public/runs/overview.test.js | 55 ++++++++++++++++++++----------- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/test/public/defaults.js b/test/public/defaults.js index 46a21eaf6a..4be108db3c 100644 --- a/test/public/defaults.js +++ b/test/public/defaults.js @@ -566,7 +566,7 @@ module.exports.expectColumnValues = async (page, columnId, expectedInnerTextValu * @param {string} expectedValuesRegex string that regex constructor `RegExp(expectedValuesRegex)` returns desired regular expression * @param {object} options options * @param {'every'|'some'} [options.valuesCheckingMode = 'every'] whether all values are expected to match regex or at least one - * @param {boolean} [options.negation] if true it's expected not to match given regex + * @param {boolean} [options.negation = false] if true it's expected not to match given regex * * @return {Promise} revoled once column values were checked */ @@ -575,13 +575,17 @@ module.exports.checkColumnValuesWithRegex = async (page, columnId, expectedValue valuesCheckingMode = 'every', negation = false, } = options; + console.log(columnId, expectedValuesRegex, valuesCheckingMode, negation, 'TOBEC') + await page.waitForFunction((columnId, regexString, valuesCheckingMode, negation) => { // Browser context, be careful when modifying - const names = [...document.querySelectorAll(`table tbody .column-${columnId}`)].map(({ innerText }) => innerText); - return names.length - && names[valuesCheckingMode]((name) => + const innerTexts = [...document.querySelectorAll(`table tbody .column-${columnId}`)].map(({ innerText }) => innerText); + // return false; + return innerTexts.length > 0 && innerTexts.every((name) => RegExp('26:00:00').test(name)); + return innerTexts.length + && innerTexts[valuesCheckingMode]((name) => negation ? !RegExp(regexString).test(name) : RegExp(regexString).test(name)); - }, { timeout: 1500 }, columnId, expectedValuesRegex, valuesCheckingMode, negation); + }, { timeout: 2500 }, columnId, expectedValuesRegex, valuesCheckingMode, negation); }; /** diff --git a/test/public/runs/overview.test.js b/test/public/runs/overview.test.js index 00e538e8fc..1030d3abb3 100644 --- a/test/public/runs/overview.test.js +++ b/test/public/runs/overview.test.js @@ -26,6 +26,8 @@ const { getAllDataFields, waitForTableDataReload, expectInputValue, + checkColumnValuesWithRegex, + getColumnCellsInnerTexts, } = require('../defaults'); const { RunDefinition } = require('../../../lib/server/services/run/getRunDefinition.js'); const { RUN_QUALITIES, RunQualities } = require('../../../lib/domain/enums/RunQualities.js'); @@ -547,28 +549,43 @@ module.exports = () => { const minutesSelector = `${runDurationFilterDivSelector} input:nth-of-type(2)`; await expectInputValue(page, operatorSelector, '='); - await waitForTableDataReload(page, () => fillInput(page, hoursSelector, 26)); - let runDurationList = await getAllDataFields(page, 'runDuration'); - expect(runDurationList).to.be.lengthOf.greaterThan(0); - expect(runDurationList.every((runDuration) => runDuration === '26:00:00')).to.be.true; + // Case 1 + await fillInput(page, hoursSelector, 26); + console.log(await page.evaluate(() => { + model.runs.overviewModel.runDurationFilterModel.durationInputModel.raw; + }), 'TOBEC dur 1') - await fillInput(page, hoursSelector, 0); - await fillInput(page, minutesSelector, 60); - expect(runDurationList.every((runDuration) => runDuration === '01:00:00')).to.be.true; + try { + await checkColumnValuesWithRegex(page, 'runDuration', '26:00:00'); + } catch { + console.log(await getColumnCellsInnerTexts(page, 'runDuration'), 'TOBEC case 1'); + } - await waitForTableDataReload(page, () => page.select(operatorSelector, '>=')); - runDurationList = await getAllDataFields(page, 'runDuration'); - expect(runDurationList).to.be.lengthOf.greaterThan(0); - expect(runDurationList.every((runDuration) => runDuration >= '01:00:00')).to.be.true; + - await waitForTableDataReload(page, async () => { - await page.select(operatorSelector, '<='); - await fillInput(page, minutesSelector, 0); - await fillInput(page, hoursSelector, 10); - }); - runDurationList = await getAllDataFields(page, 'runDuration'); - expect(runDurationList).to.be.lengthOf.greaterThan(0); - expect(runDurationList.every((runDuration) => runDuration <= '10:00:00')).to.be.true; + // Case 2 + await fillInput(page, hoursSelector, 1); + await fillInput(page, minutesSelector, 0); + console.log(await page.evaluate(() => { + model.runs.overviewModel.runDurationFilterModel.durationInputModel.raw; + }), 'TOBEC dur 2') + + try { + await checkColumnValuesWithRegex(page, 'runDuration', '01:00:00'); + } catch { + console.log(await getColumnCellsInnerTexts(page, 'runDuration'), 'TOBEC case 2'); + } + + // Case 3 + await page.select(operatorSelector, '>='); + // await checkColumnValuesWithRegex(page, 'runDuration', /[0-9][1-9]:[0-5][0-9]:[0-5][0-9]/); + await checkColumnValuesWithRegex(page, 'runDuration'); + + // Case 4 + await page.select(operatorSelector, '<='); + await fillInput(page, minutesSelector, 0); + await fillInput(page, hoursSelector, 10); + await checkColumnValuesWithRegex(page, 'runDuration', /(10:00:00)|(0[1-9]:[0-5][0-9]:[0-5][0-9])/); }); it('Should successfully filter runs by their run quality', async () => { From 6b013dd589529967fd35d2b7fff1bbb8b0aedd2d Mon Sep 17 00:00:00 2001 From: xsalonx Date: Mon, 3 Jun 2024 14:34:10 +0200 Subject: [PATCH 42/46] fix --- test/public/defaults.js | 11 +++++------ test/public/runs/overview.test.js | 17 ++--------------- 2 files changed, 7 insertions(+), 21 deletions(-) diff --git a/test/public/defaults.js b/test/public/defaults.js index 2d61fac150..614dbcb8b9 100644 --- a/test/public/defaults.js +++ b/test/public/defaults.js @@ -595,7 +595,7 @@ module.exports.expectColumnValues = async (page, columnId, expectedInnerTextValu * * @param {puppeteer.Page} page the puppeteer page * @param {string} columnId column id - * @param {string} expectedValuesRegex string that regex constructor `RegExp(expectedValuesRegex)` returns desired regular expression + * @param {string|RegExp} expectedValuesRegex string that regex constructor `RegExp(expectedValuesRegex)` returns desired regular expression * @param {object} options options * @param {'every'|'some'} [options.valuesCheckingMode = 'every'] whether all values are expected to match regex or at least one * @param {boolean} [options.negation = false] if true it's expected not to match given regex @@ -607,17 +607,16 @@ module.exports.checkColumnValuesWithRegex = async (page, columnId, expectedValue valuesCheckingMode = 'every', negation = false, } = options; - console.log(columnId, expectedValuesRegex, valuesCheckingMode, negation, 'TOBEC') -  + + const adjustedRegExp = new RegExp(expectedValuesRegex).toString().slice(1, -1); + await page.waitForFunction((columnId, regexString, valuesCheckingMode, negation) => { // Browser context, be careful when modifying const innerTexts = [...document.querySelectorAll(`table tbody .column-${columnId}`)].map(({ innerText }) => innerText); - // return false; - // return innerTexts.length > 0 && innerTexts.every((name) => RegExp('26:00:00').test(name)); return innerTexts.length && innerTexts[valuesCheckingMode]((name) => negation ? !RegExp(regexString).test(name) : RegExp(regexString).test(name)); - }, { timeout: 1500 }, columnId, expectedValuesRegex, valuesCheckingMode, negation); + }, { timeout: 1500 }, columnId, adjustedRegExp, valuesCheckingMode, negation); }; /** diff --git a/test/public/runs/overview.test.js b/test/public/runs/overview.test.js index 864c9f59fc..3ed9a8742d 100644 --- a/test/public/runs/overview.test.js +++ b/test/public/runs/overview.test.js @@ -25,7 +25,6 @@ const { waitForNetworkIdleAndRedraw, expectInputValue, checkColumnValuesWithRegex, - getColumnCellsInnerTexts, waitForDownload, expectColumnValues, } = require('../defaults'); @@ -552,12 +551,6 @@ module.exports = () => { // Case 1 await fillInput(page, hoursSelector, 26); - console.log(await page.evaluate(() => { - const { raw } = model.runs.overviewModel.runDurationFilterModel.durationInputModel; - const { selected } = model.runs.overviewModel.runDurationFilterModel.operatorSelectionModel; - return { raw, selected }; - }), 'TOBEC dur 1'); - await checkColumnValuesWithRegex(page, 'runDuration', '26:00:00'); // Case 2 @@ -567,18 +560,12 @@ module.exports = () => { // Case 3 await page.select(operatorSelector, '>='); - console.log(await page.evaluate(() => { - const { raw } = model.runs.overviewModel.runDurationFilterModel.durationInputModel; - const { selected } = model.runs.overviewModel.runDurationFilterModel.operatorSelectionModel; - return { raw, selected }; - }), 'TOBEC dur 3'); - // await checkColumnValuesWithRegex(page, 'runDuration', /([1-9][0-9])|(0[1-9]):[0-5][0-9]:[0-5][0-9]/); - await checkColumnValuesWithRegex(page, 'runDuration', /[0-9:]+/); + await checkColumnValuesWithRegex(page, 'runDuration', /(UNKNOWN)|(([1-9][0-9])|(0[1-9]):[0-5][0-9]:[0-5][0-9])/); // Case 4 await page.select(operatorSelector, '<='); - await fillInput(page, minutesSelector, 0); await fillInput(page, hoursSelector, 10); + await fillInput(page, minutesSelector, 0); await checkColumnValuesWithRegex(page, 'runDuration', /(10:00:00)|(0[1-9]:[0-5][0-9]:[0-5][0-9])/); }); From c353f541a4b778beeb3ed98c4681ed5cdea00c96 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Mon, 3 Jun 2024 15:03:10 +0200 Subject: [PATCH 43/46] fix --- test/public/runs/runsPerDataPass.overview.test.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/public/runs/runsPerDataPass.overview.test.js b/test/public/runs/runsPerDataPass.overview.test.js index dce2d87366..56275378a9 100644 --- a/test/public/runs/runsPerDataPass.overview.test.js +++ b/test/public/runs/runsPerDataPass.overview.test.js @@ -315,7 +315,8 @@ module.exports = () => { await goToPage(page, 'runs-per-data-pass', { queryParameters: { dataPassId: 2 } }); await pressElement(page, '#openFilterToggle'); - await page.select('.runDuration-filter select', '>='); + const runDurationFilterDivSelector = '.runDuration-filter'; + const minutesSelector = `${runDurationFilterDivSelector} input:nth-of-type(2)`; /** * Invokation of page.select and fillInput in case of amountFilter results in two concurrent, @@ -325,7 +326,7 @@ module.exports = () => { await page.select('.runDuration-filter select', '>='); await pressElement(page, '#openFilterToggle'); await pressElement(page, '#openFilterToggle'); - await fillInput(page, '.runDuration-filter input[type=number]', '10'); + await fillInput(page, minutesSelector, 10); await expectColumnValues(page, 'runNumber', ['55', '1']); From 73425be93e7b8dda0e8790c7290d6fed35f1d390 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Tue, 4 Jun 2024 11:14:26 +0200 Subject: [PATCH 44/46] fix --- lib/public/components/Filters/RunsFilter/durationFilter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/public/components/Filters/RunsFilter/durationFilter.js b/lib/public/components/Filters/RunsFilter/durationFilter.js index b168062269..58c5d16795 100644 --- a/lib/public/components/Filters/RunsFilter/durationFilter.js +++ b/lib/public/components/Filters/RunsFilter/durationFilter.js @@ -34,7 +34,7 @@ export const durationFilter = (durationFilterModel) => { const updateInputModelHandlerByTimeUnit = (unitName) => (e) => { const { value } = e.target; const parsedValue = Number(value); - if (value.length > 0 || 0 <= parsedValue && parsedValue < 60) { + if (value.length > 0 || 0 <= parsedValue && (unitName === 'hours' || parsedValue < 60)) { durationInputModel.update({ [unitName]: value.length > 0 ? parsedValue : null }); } else { durationFilterModel.visualChange$.notify(); From 2507a4a03feef7b375558237ddf5e4ad14117441 Mon Sep 17 00:00:00 2001 From: xsalonx Date: Tue, 4 Jun 2024 11:17:38 +0200 Subject: [PATCH 45/46] simp --- .../components/common/form/inputs/DurationInputModel.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/public/components/common/form/inputs/DurationInputModel.js b/lib/public/components/common/form/inputs/DurationInputModel.js index 964c7042e6..49f77d0b53 100644 --- a/lib/public/components/common/form/inputs/DurationInputModel.js +++ b/lib/public/components/common/form/inputs/DurationInputModel.js @@ -56,10 +56,9 @@ export class DurationInputModel extends Observable { if ((hours ?? minutes ?? seconds ?? null) === null) { this._value = null; } else { - this._value = - (this._raw.hours || 0) * 60 * 60 * 1000 + - (this._raw.minutes || 0) * 60 * 1000 + - (this._raw.seconds || 0) * 1000; + this._value = (hours || 0) * 60 * 60 * 1000 + + (minutes || 0) * 60 * 1000 + + (seconds || 0) * 1000; } } catch { this._value = null; From bf412c4ffc7024ff7b58735e3db5fe5eb5b45d3a Mon Sep 17 00:00:00 2001 From: xsalonx Date: Tue, 4 Jun 2024 14:59:02 +0200 Subject: [PATCH 46/46] add method --- test/public/defaults.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/public/defaults.js b/test/public/defaults.js index 11bffa6bd8..05da69cce0 100644 --- a/test/public/defaults.js +++ b/test/public/defaults.js @@ -589,6 +589,35 @@ module.exports.expectColumnValues = async (page, columnId, expectedInnerTextValu expect(await this.getColumnCellsInnerTexts(page, columnId)).to.have.all.ordered.members(expectedInnerTextValues); }; +/** + * Method to check cells of a row with given id have expected innerText + * + * @param {puppeteer.Page} page the puppeteer page + * @param {stirng} rowId row id + * @param {Object} [expectedInnerTextValues] values expected in the row + * + * @return {Promise} resolve once row's values were checked + */ +module.exports.expectRowValues = async (page, rowId, expectedInnerTextValues) => { + try { + await page.waitForFunction(async (rowId, expectedInnerTextValues) => { + for (const columnId in expectedInnerTextValues) { + const actualValue = (await document.querySelectorAll(`table tbody td:nth-of-type(${rowId}) .column-${columnId}`)).innerText; + if (expectedInnerTextValues[columnId] == actualValue) { + return false; + } + } + return true; + }, rowId, expectedInnerTextValues); + } catch { + const rowInnerTexts = {}; + for (const columnId in expectedInnerTextValues) { + rowInnerTexts[columnId] = (await document.querySelectorAll(`table tbody td:nth-of-type(${rowId}) .column-${columnId}`)).innerText; + } + expect(rowInnerTexts).to.eql(expectedInnerTextValues); + } +}; + /** * Generic method to validate inner text of cells belonging column with given id. * It checks exact match with given values