Skip to content

Commit 916dcae

Browse files
authored
Merge branch 'main' into improv/O2B-1520/Add-filtering-by-run-intervals-to-envs
2 parents 5d612c0 + 5fd8dba commit 916dcae

21 files changed

Lines changed: 810 additions & 252 deletions

lib/domain/dtos/filters/LhcFillsFilterDto.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@
1212
*/
1313
const Joi = require('joi');
1414
const { validateRange, RANGE_INVALID } = require('../../../utilities/rangeUtils');
15+
const { validateTimeDuration } = require('../../../utilities/validateTime');
1516

1617
exports.LhcFillsFilterDto = Joi.object({
1718
hasStableBeams: Joi.boolean(),
1819
fillNumbers: Joi.string().trim().custom(validateRange).messages({
1920
[RANGE_INVALID]: '{{#message}}',
2021
'string.base': 'Fill numbers must be comma-separated numbers or ranges (e.g. 12,15-18)',
2122
}),
23+
beamDuration: validateTimeDuration,
2224
});

lib/domain/dtos/filters/NumericalComparisonDto.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,5 @@ exports.FloatComparisonDto = Joi.object({
2424
operator: Joi.string().valid(...NUMERICAL_COMPARISON_OPERATORS),
2525
limit: Joi.number().min(0),
2626
});
27+
28+
exports.NUMERICAL_COMPARISON_OPERATORS = NUMERICAL_COMPARISON_OPERATORS;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* @license
3+
* Copyright CERN and copyright holders of ALICE Trg. This software is
4+
* distributed under the terms of the GNU General Public License v3 (GPL
5+
* Version 3), copied verbatim in the file "COPYING".
6+
*
7+
* See http://alice-Trg.web.cern.ch/license for full licensing information.
8+
*
9+
* In applying this license CERN does not waive the privileges and immunities
10+
* granted to it by virtue of its status as an Intergovernmental Organization
11+
* or submit itself to any jurisdiction.
12+
*/
13+
14+
import { comparisonOperatorFilter } from '../common/filters/comparisonOperatorFilter.js';
15+
import { rawTextFilter } from '../common/filters/rawTextFilter.js';
16+
17+
/**
18+
* Component to filter LHC-fills by beam duration
19+
*
20+
* @param {TextComparisonFilterModel} beamDurationFilterModel beamDurationFilterModel
21+
* @returns {Component} the text field
22+
*/
23+
export const beamDurationFilter = (beamDurationFilterModel) => {
24+
const durationFilter = rawTextFilter(
25+
beamDurationFilterModel.operandInputModel,
26+
{ id: 'beam-duration-filter-operand', classes: ['w-100', 'beam-duration-filter'], placeholder: 'e.g 16:14:15 (HH:MM:SS)' },
27+
);
28+
29+
return comparisonOperatorFilter(durationFilter, beamDurationFilterModel.operatorSelectionModel.current, (value) =>
30+
beamDurationFilterModel.operatorSelectionModel.select(value), { id: 'beam-duration-filter-operator' });
31+
};
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/**
2+
* @license
3+
* Copyright CERN and copyright holders of ALICE O2. This software is
4+
* distributed under the terms of the GNU General Public License v3 (GPL
5+
* Version 3), copied verbatim in the file "COPYING".
6+
*
7+
* See http://alice-o2.web.cern.ch/license for full licensing information.
8+
*
9+
* In applying this license CERN does not waive the privileges and immunities
10+
* granted to it by virtue of its status as an Intergovernmental Organization
11+
* or submit itself to any jurisdiction.
12+
*/
13+
import { ComparisonSelectionModel } from './ComparisonSelectionModel.js';
14+
import { FilterModel } from '../FilterModel.js';
15+
import { RawTextFilterModel } from './RawTextFilterModel.js';
16+
17+
/**
18+
* TextComparisonFilterModel
19+
*/
20+
export class TextComparisonFilterModel extends FilterModel {
21+
/**
22+
* Constructor
23+
*/
24+
constructor() {
25+
super();
26+
27+
this._operatorSelectionModel = new ComparisonSelectionModel();
28+
this._operatorSelectionModel.visualChange$.bubbleTo(this._visualChange$);
29+
30+
this._operandInputModel = new RawTextFilterModel();
31+
// Unless the filter contains a value don't apply the filters.
32+
this._operatorSelectionModel.observe(() => this._operandInputModel.value ? this.notify() : this._visualChange$.notify());
33+
this._addSubmodel(this._operandInputModel);
34+
}
35+
36+
/**
37+
* Return raw text filter model
38+
*
39+
* @return {RawTextFilterModel} operand input model
40+
*/
41+
get operandInputModel() {
42+
return this._operandInputModel;
43+
}
44+
45+
/**
46+
* Get operator selection model
47+
*
48+
* @return {ComparisonSelectionModel} selection model
49+
*/
50+
get operatorSelectionModel() {
51+
return this._operatorSelectionModel;
52+
}
53+
54+
/**
55+
* @inheritDoc
56+
*/
57+
reset() {
58+
this._operandInputModel.reset();
59+
this._operatorSelectionModel.reset();
60+
}
61+
62+
/**
63+
* @inheritDoc
64+
*/
65+
get normalized() {
66+
return {
67+
operator: this._operatorSelectionModel.current,
68+
limit: this._operandInputModel.value,
69+
};
70+
}
71+
72+
/**
73+
* @inheritDoc
74+
*/
75+
get isEmpty() {
76+
return !this._operandInputModel.value;
77+
}
78+
}

lib/public/components/Filters/common/filters/rawTextFilter.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@ import { h } from '/js/src/index.js';
2323
* @return {Component} the filter
2424
*/
2525
export const rawTextFilter = (filterModel, configuration) => {
26-
const { classes = [], placeholder = '' } = configuration || {};
26+
const { classes = [], placeholder = '', id } = configuration || {};
2727
return h(
2828
'input',
2929
{
3030
type: 'text',
31+
id: id,
3132
class: classes.join(' '),
3233
value: filterModel.value,
3334
placeholder: placeholder,

lib/public/views/Home/Overview/HomePageModel.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export class HomePageModel extends Observable {
3232
this._logsOverviewModel = new LogsOverviewModel(model, true);
3333
this._logsOverviewModel.bubbleTo(this);
3434

35-
this._lhcFillsOverviewModel = new LhcFillsOverviewModel(true);
35+
this._lhcFillsOverviewModel = new LhcFillsOverviewModel(model, true);
3636
this._lhcFillsOverviewModel.bubbleTo(this);
3737
}
3838

lib/public/views/LhcFills/ActiveColumns/lhcFillsActiveColumns.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { formatBeamType } from '../../../utilities/formatting/formatBeamType.js'
2525
import { frontLink } from '../../../components/common/navigation/frontLink.js';
2626
import { toggleStableBeamOnlyFilter } from '../../../components/Filters/LhcFillsFilter/stableBeamFilter.js';
2727
import { fillNumberFilter } from '../../../components/Filters/LhcFillsFilter/fillNumberFilter.js';
28+
import { beamDurationFilter } from '../../../components/Filters/LhcFillsFilter/beamDurationFilter.js';
2829

2930
/**
3031
* List of active columns for a lhc fills table
@@ -108,6 +109,7 @@ export const lhcFillsActiveColumns = {
108109

109110
return '-';
110111
},
112+
filter: (lhcFillModel) => beamDurationFilter(lhcFillModel.filteringModel.get('beamDuration')),
111113
profiles: {
112114
lhcFill: true,
113115
environment: true,

lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { StableBeamFilterModel } from '../../../components/Filters/LhcFillsFilte
1717
import { RawTextFilterModel } from '../../../components/Filters/common/filters/RawTextFilterModel.js';
1818
import { OverviewPageModel } from '../../../models/OverviewModel.js';
1919
import { addStatisticsToLhcFill } from '../../../services/lhcFill/addStatisticsToLhcFill.js';
20+
import { TextComparisonFilterModel } from '../../../components/Filters/common/filters/TextComparisonFilterModel.js';
2021

2122
/**
2223
* Model for the LHC fills overview page
@@ -27,17 +28,19 @@ export class LhcFillsOverviewModel extends OverviewPageModel {
2728
/**
2829
* Constructor
2930
*
31+
* @param {model} model global model
3032
* @param {boolean} [stableBeamsOnly=false] if true, overview will load stable beam only
3133
*/
3234
constructor(stableBeamsOnly = false) {
3335
super();
3436

3537
this._filteringModel = new FilteringModel({
3638
fillNumbers: new RawTextFilterModel(),
39+
beamDuration: new TextComparisonFilterModel(),
3740
hasStableBeams: new StableBeamFilterModel(),
3841
});
3942

40-
this._filteringModel.observe(() => this._applyFilters(true));
43+
this._filteringModel.observe(() => this._applyFilters());
4144
this._filteringModel.visualChange$.bubbleTo(this);
4245

4346
this.reset(false);
@@ -61,7 +64,10 @@ export class LhcFillsOverviewModel extends OverviewPageModel {
6164
* @inheritDoc
6265
*/
6366
getRootEndpoint() {
64-
return buildUrl('/api/lhcFills', { filter: this.filteringModel.normalized });
67+
const params = {
68+
filter: this.filteringModel.normalized,
69+
};
70+
return buildUrl('/api/lhcFills', params);
6571
}
6672

6773
/**
@@ -83,7 +89,7 @@ export class LhcFillsOverviewModel extends OverviewPageModel {
8389
this._filteringModel.reset();
8490

8591
if (fetch) {
86-
this._applyFilters(true);
92+
this._applyFilters();
8793
}
8894
}
8995

lib/usecases/lhcFill/GetAllLhcFillsUseCase.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class GetAllLhcFillsUseCase {
4545
const queryBuilder = new QueryBuilder();
4646

4747
if (filter) {
48-
const { hasStableBeams, fillNumbers } = filter;
48+
const { hasStableBeams, fillNumbers, beamDuration } = filter;
4949
if (hasStableBeams) {
5050
// For now, if a stableBeamsStart is present, then a beam is stable
5151
queryBuilder.where('stableBeamsStart').not().is(null);
@@ -62,6 +62,10 @@ class GetAllLhcFillsUseCase {
6262
: queryBuilder.where('fillNumber').oneOf(...finalFillnumberList);
6363
}
6464
}
65+
// Beam duration filter, limit and corresponding operator.
66+
if (beamDuration?.limit !== undefined && beamDuration?.operator) {
67+
queryBuilder.where('stableBeamsDuration').applyOperator(beamDuration.operator, beamDuration.limit);
68+
}
6569
}
6670

6771
const { count, rows } = await TransactionHelper.provide(async () => {

lib/utilities/validateTime.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* @license
3+
* Copyright CERN and copyright holders of ALICE O2. This software is
4+
* distributed under the terms of the GNU General Public License v3 (GPL
5+
* Version 3), copied verbatim in the file "COPYING".
6+
*
7+
* See http://alice-o2.web.cern.ch/license for full licensing information.
8+
*
9+
* In applying this license CERN does not waive the privileges and immunities
10+
* granted to it by virtue of its status as an Intergovernmental Organization
11+
* or submit itself to any jurisdiction.
12+
*/
13+
import Joi from 'joi';
14+
import { NUMERICAL_COMPARISON_OPERATORS } from '../domain/dtos/filters/NumericalComparisonDto.js';
15+
16+
const joiTimeDurationErrorText = 'Invalid duration value';
17+
18+
/**
19+
* Transform digital time in string format
20+
*
21+
* @param {string} incomingValue The time to transform
22+
* @param {*} helpers The Joi helpers object
23+
* @returns {number|import("joi").ValidationError} The value if transformation passes, as seconds (Number)
24+
*/
25+
export const transformTime = (incomingValue, helpers) => {
26+
try {
27+
// Extract time to seconds...
28+
const [hoursStr, minutesStr, secondsStr] = incomingValue.split(':');
29+
30+
const hours = Number(hoursStr);
31+
const minutes = Number(minutesStr);
32+
const seconds = Number(secondsStr);
33+
34+
return hours * 3600 + minutes * 60 + seconds;
35+
} catch (error) {
36+
return helpers.error('any.invalid', { message: `Validation error: ${error?.message ?? 'failed to transform time'}` });
37+
}
38+
};
39+
40+
/**
41+
* Joi object that validates time duration filters.
42+
* This is for duration, not a point in time. 10000:59:59 is valid.
43+
* The operator is also validated.
44+
*/
45+
export const validateTimeDuration = Joi.object({
46+
limit: Joi.string().trim().pattern(/^\d+:[0-5]?\d:[0-5]?\d$/).custom(transformTime).messages({
47+
'string.pattern.base': joiTimeDurationErrorText,
48+
'string.base': joiTimeDurationErrorText,
49+
}),
50+
operator: Joi.string().valid(...NUMERICAL_COMPARISON_OPERATORS),
51+
});

0 commit comments

Comments
 (0)