Skip to content

Commit 7f6dbfd

Browse files
authored
[O2B-1506] LHCfills run duration filter (#2038)
- LHCfills page now has a total run duration filter. - GetAllLhcFillsUseCase.js will now execute a JOIN on statistics (fill_statistics) when runDuration and runDurationOperator are defined in the filter.
1 parent 5fd8dba commit 7f6dbfd

10 files changed

Lines changed: 565 additions & 22 deletions

File tree

lib/database/utilities/QueryBuilder.js

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,55 @@ class WhereQueryBuilder {
145145
}
146146

147147
/**
148-
* Set the next filter condition according to a comparison operator ("<", "<=", ">=", ">")
148+
* Set a min range limit using the provided value OR null (useful when 0 is stored as null)
149+
*
150+
* @param {*} min the lower limit criteria
151+
* @param {boolean} strict if true, the comparison is exclusive, else it will be inclusive (equality match)
152+
*
153+
* @return {void}
154+
*/
155+
greaterThanOrNull(min, strict) {
156+
if (this.notFlag) {
157+
this.notFlag = !this.notFlag;
158+
this.lowerThan(min, !strict);
159+
this.notFlag = !this.notFlag;
160+
return;
161+
}
162+
const operation = {
163+
[Op.or]: [
164+
{ [Op.is]: null },
165+
{ [strict ? Op.gt : Op.gte]: min },
166+
],
167+
};
168+
this._op(operation);
169+
}
170+
171+
/**
172+
* Set a max range limit using the provided value OR null (useful when 0 is stored as null)
173+
*
174+
* @param {*} max the upper limit criteria
175+
* @param {boolean} strict if true, the comparison is exclusive, else it will be inclusive (equality match)
176+
*
177+
* @return {void}
178+
*/
179+
lowerThanOrNull(max, strict) {
180+
if (this.notFlag) {
181+
this.notFlag = !this.notFlag;
182+
this.greaterThan(max, !strict);
183+
this.notFlag = !this.notFlag;
184+
return;
185+
}
186+
const operation = {
187+
[Op.or]: [
188+
{ [Op.is]: null },
189+
{ [strict ? Op.lt : Op.lte]: max },
190+
],
191+
};
192+
this._op(operation);
193+
}
194+
195+
/**
196+
* Set the next filter condition according to a comparison operator ("<", "<=", ">=", ">", "=")
149197
*
150198
* @param {"<"|"<="|"="|">="|">"} operator the operator to apply
151199
* @param {*} limit the limit to compare to

lib/domain/dtos/filters/LhcFillsFilterDto.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ exports.LhcFillsFilterDto = Joi.object({
1919
fillNumbers: Joi.string().trim().custom(validateRange).messages({
2020
'any.invalid': '{{#message}}',
2121
}),
22+
runDuration: validateTimeDuration,
2223
beamDuration: validateTimeDuration,
2324
});

lib/public/components/Filters/LhcFillsFilter/beamDurationFilter.js renamed to lib/public/components/Filters/LhcFillsFilter/durationFilter.js

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,18 @@ import { comparisonOperatorFilter } from '../common/filters/comparisonOperatorFi
1515
import { rawTextFilter } from '../common/filters/rawTextFilter.js';
1616

1717
/**
18-
* Component to filter LHC-fills by beam duration
18+
* Component to filter LHC-fills by duration
1919
*
20-
* @param {TextComparisonFilterModel} beamDurationFilterModel beamDurationFilterModel
20+
* @param {TextComparisonFilterModel} durationFilterModel durationFilterModel
21+
* @param {string} id id used for the operand and operator elements, becomes: `${id}-operator` OR `${id}-operand`.
2122
* @returns {Component} the text field
2223
*/
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)' },
24+
export const durationFilter = (durationFilterModel, id) => {
25+
const amountFilter = rawTextFilter(
26+
durationFilterModel.operandInputModel,
27+
{ id: `${id}-operand`, classes: ['w-100'], placeholder: 'e.g 16:14:15 (HH:MM:SS)' },
2728
);
2829

29-
return comparisonOperatorFilter(durationFilter, beamDurationFilterModel.operatorSelectionModel.current, (value) =>
30-
beamDurationFilterModel.operatorSelectionModel.select(value), { id: 'beam-duration-filter-operator' });
30+
return comparisonOperatorFilter(amountFilter, durationFilterModel.operatorSelectionModel.current, (value) =>
31+
durationFilterModel.operatorSelectionModel.select(value), { id: `${id}-operator` });
3132
};

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +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';
28+
import { durationFilter } from '../../../components/Filters/LhcFillsFilter/durationFilter.js';
2929

3030
/**
3131
* List of active columns for a lhc fills table
@@ -109,7 +109,7 @@ export const lhcFillsActiveColumns = {
109109

110110
return '-';
111111
},
112-
filter: (lhcFillModel) => beamDurationFilter(lhcFillModel.filteringModel.get('beamDuration')),
112+
filter: (lhcFillModel) => durationFilter(lhcFillModel.filteringModel.get('beamDuration'), 'beam-duration-filter'),
113113
profiles: {
114114
lhcFill: true,
115115
environment: true,
@@ -141,6 +141,7 @@ export const lhcFillsActiveColumns = {
141141
visible: true,
142142
size: 'w-8',
143143
format: (duration) => formatDuration(duration),
144+
filter: (lhcFillModel) => durationFilter(lhcFillModel.filteringModel.get('runDuration'), 'run-duration-filter'),
144145
},
145146
efficiency: {
146147
name: 'Fill Efficiency',

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ export class LhcFillsOverviewModel extends OverviewPageModel {
2828
/**
2929
* Constructor
3030
*
31-
* @param {model} model global model
3231
* @param {boolean} [stableBeamsOnly=false] if true, overview will load stable beam only
3332
*/
3433
constructor(stableBeamsOnly = false) {
@@ -37,6 +36,7 @@ export class LhcFillsOverviewModel extends OverviewPageModel {
3736
this._filteringModel = new FilteringModel({
3837
fillNumbers: new RawTextFilterModel(),
3938
beamDuration: new TextComparisonFilterModel(),
39+
runDuration: new TextComparisonFilterModel(),
4040
hasStableBeams: new StableBeamFilterModel(),
4141
});
4242

lib/usecases/lhcFill/GetAllLhcFillsUseCase.js

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ class GetAllLhcFillsUseCase {
4444

4545
const queryBuilder = new QueryBuilder();
4646

47+
let associatedStatisticsRequired = false;
48+
4749
if (filter) {
48-
const { hasStableBeams, fillNumbers, beamDuration } = filter;
50+
const { hasStableBeams, fillNumbers, beamDuration, runDuration } = filter;
4951
if (hasStableBeams) {
5052
// For now, if a stableBeamsStart is present, then a beam is stable
5153
queryBuilder.where('stableBeamsStart').not().is(null);
@@ -62,6 +64,23 @@ class GetAllLhcFillsUseCase {
6264
: queryBuilder.where('fillNumber').oneOf(...finalFillnumberList);
6365
}
6466
}
67+
68+
if (runDuration?.limit !== undefined && runDuration?.operator) {
69+
associatedStatisticsRequired = true;
70+
// 00:00:00 aka 0 value is saved in the DB as null (bookkeeping.fill_statistics.runs_coverage)
71+
if (runDuration.operator === '<=' && Number(runDuration.limit) === 0) {
72+
queryBuilder.whereAssociation('statistics', 'runsCoverage').lowerThanOrNull(0, false);
73+
} else if (runDuration.operator === '>=' && Number(runDuration.limit) === 0) {
74+
queryBuilder.whereAssociation('statistics', 'runsCoverage').greaterThanOrNull(0, false);
75+
} else if ((runDuration.operator === '>' || runDuration.operator === '<') && Number(runDuration.limit) === 0) {
76+
queryBuilder.whereAssociation('statistics', 'runsCoverage').applyOperator(runDuration.operator, 0);
77+
} else if (Number(runDuration.limit) === 0) {
78+
queryBuilder.whereAssociation('statistics', 'runsCoverage').applyOperator(runDuration.operator, null);
79+
} else {
80+
queryBuilder.whereAssociation('statistics', 'runsCoverage').applyOperator(runDuration.operator, runDuration.limit);
81+
}
82+
}
83+
6584
// Beam duration filter, limit and corresponding operator.
6685
if (beamDuration?.limit !== undefined && beamDuration?.operator) {
6786
queryBuilder.where('stableBeamsDuration').applyOperator(beamDuration.operator, beamDuration.limit);
@@ -74,6 +93,14 @@ class GetAllLhcFillsUseCase {
7493
where: { definition: RunDefinition.PHYSICS },
7594
required: false,
7695
});
96+
97+
if (associatedStatisticsRequired) {
98+
queryBuilder.include({
99+
association: 'statistics',
100+
required: true,
101+
});
102+
}
103+
77104
queryBuilder.orderBy('fillNumber', 'desc');
78105
queryBuilder.limit(limit);
79106
queryBuilder.offset(offset);

0 commit comments

Comments
 (0)