Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/domain/dtos/filters/LhcFillsFilterDto.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,8 @@ exports.LhcFillsFilterDto = Joi.object({
'any.invalid': '{{#message}}',
}),
beamDurationOperator: Joi.string().trim().min(1).max(2),
runDuration: Joi.string().trim().min(8).max(8).custom(validateTime).messages({
'any.invalid': '{{#message}}',
}),
runDurationOperator: Joi.string().trim().min(1).max(2),
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* @license
* Copyright CERN and copyright holders of ALICE Trg. This software is
* distributed under the terms of the GNU General Public License v3 (GPL
* Version 3), copied verbatim in the file "COPYING".
*
* See http://alice-Trg.web.cern.ch/license for full licensing information.
*
* In applying this license CERN does not waive the privileges and immunities
* granted to it by virtue of its status as an Intergovernmental Organization
* or submit itself to any jurisdiction.
*/

import { comparisonOperatorFilter } from '../common/filters/comparisonOperatorFilter.js';
import { rawTextFilter } from '../common/filters/rawTextFilter.js';

/**
* Component to filter LHC-fills by run duration
*
* @param {rawTextFilter} runDurationFilterModel runDurationFilterModel
* @param {string} runDurationOperator run duration operator value
* @param {(string) => undefined} runDurationOperatorUpdate run duration operator setter function
* @returns {Component} the text field
*/
export const runDurationFilter = (runDurationFilterModel, runDurationOperator, runDurationOperatorUpdate) => {
const amountFilter = rawTextFilter(
runDurationFilterModel,
{ classes: ['w-100', 'run-duration-filter'], placeholder: 'e.g 16:14:15 (HH:MM:SS)' },
);

return comparisonOperatorFilter(amountFilter, runDurationOperator, (value) => runDurationOperatorUpdate(value));
};
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { frontLink } from '../../../components/common/navigation/frontLink.js';
import { toggleStableBeamOnlyFilter } from '../../../components/Filters/LhcFillsFilter/stableBeamFilter.js';
import { fillNumberFilter } from '../../../components/Filters/LhcFillsFilter/fillNumberFilter.js';
import { beamDurationFilter } from '../../../components/Filters/LhcFillsFilter/beamDurationFilter.js';
import { runDurationFilter } from '../../../components/Filters/LhcFillsFilter/runDurationFilter.js';

/**
* List of active columns for a lhc fills table
Expand Down Expand Up @@ -145,6 +146,11 @@ export const lhcFillsActiveColumns = {
visible: true,
size: 'w-8',
format: (duration) => formatDuration(duration),
filter: (lhcFillModel) => runDurationFilter(
lhcFillModel.filteringModel.get('runDuration'),
lhcFillModel.getRunDurationOperator(),
(value) => lhcFillModel.setRunDurationOperator(value),
),
},
efficiency: {
name: 'Fill Efficiency',
Expand Down
26 changes: 25 additions & 1 deletion lib/public/views/LhcFills/Overview/LhcFillsOverviewModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { OverviewPageModel } from '../../../models/OverviewModel.js';
import { addStatisticsToLhcFill } from '../../../services/lhcFill/addStatisticsToLhcFill.js';

const defaultBeamDurationOperator = '=';
const defaultRunDurationOperator = '=';

/**
* Model for the LHC fills overview page
Expand All @@ -37,10 +38,12 @@ export class LhcFillsOverviewModel extends OverviewPageModel {
this._filteringModel = new FilteringModel({
fillNumbers: new RawTextFilterModel(),
beamDuration: new RawTextFilterModel(),
runDuration: new RawTextFilterModel(),
hasStableBeams: new StableBeamFilterModel(),
});

this._beamDurationOperator = defaultBeamDurationOperator;
this._runDurationOperator = defaultRunDurationOperator;

this._filteringModel.observe(() => this._applyFilters(true));
this._filteringModel.visualChange$.bubbleTo(this);
Expand Down Expand Up @@ -71,10 +74,29 @@ export class LhcFillsOverviewModel extends OverviewPageModel {
...this._filteringModel.get('beamDuration').isEmpty === false && {
'filter[beamDurationOperator]': this._beamDurationOperator,
},
...this._filteringModel.get('runDuration').isEmpty === false && {
'filter[runDurationOperator]': this._runDurationOperator,
},
};
return buildUrl('/api/lhcFills', params);
}

/**
* Setter function for runDurationOperator
*/
setRunDurationOperator(runDurationOperator) {
this._runDurationOperator = runDurationOperator;
this._applyFilters();
this.notify();
}

/**
* Run duration operator getter
*/
getRunDurationOperator() {
return this._runDurationOperator;
}

/**
* Beam duration operator setter
*/
Expand Down Expand Up @@ -109,6 +131,7 @@ export class LhcFillsOverviewModel extends OverviewPageModel {
resetFiltering(fetch = true) {
this._filteringModel.reset();
this._beamDurationOperator = defaultBeamDurationOperator;
this._runDurationOperator = defaultRunDurationOperator;

if (fetch) {
this._applyFilters(true);
Expand All @@ -121,7 +144,8 @@ export class LhcFillsOverviewModel extends OverviewPageModel {
*/
isAnyFilterActive() {
return this._filteringModel.isAnyFilterActive()
|| this._beamDurationOperator !== defaultBeamDurationOperator;
|| this._beamDurationOperator !== defaultBeamDurationOperator
|| this._runDurationOperator !== defaultRunDurationOperator;
}

/**
Expand Down
18 changes: 17 additions & 1 deletion lib/usecases/lhcFill/GetAllLhcFillsUseCase.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@ class GetAllLhcFillsUseCase {

const queryBuilder = new QueryBuilder();

let associatedStatisticsRequired = false;

if (filter) {
const { hasStableBeams, fillNumbers, beamDurationOperator, beamDuration } = filter;
const { hasStableBeams, fillNumbers, beamDurationOperator, beamDuration, runDurationOperator, runDuration } = filter;
if (hasStableBeams) {
// For now, if a stableBeamsStart is present, then a beam is stable
queryBuilder.where('stableBeamsStart').not().is(null);
Expand All @@ -61,6 +63,15 @@ class GetAllLhcFillsUseCase {
queryBuilder.where('fillNumber').oneOf(...finalFillnumberList);
}
}

// Run duration filter and corresponding operator.
if (runDuration !== null && runDuration !== undefined && runDurationOperator) {
associatedStatisticsRequired = true;
// 00:00:00 aka 0 value is saved in the DB as null
runDuration === 0 ? queryBuilder.whereAssociation('statistics', 'runsCoverage').applyOperator(runDurationOperator, null)
: queryBuilder.whereAssociation('statistics', 'runsCoverage').applyOperator(runDurationOperator, runDuration);
}

// Beam duration filter and corresponding operator.
if (beamDuration !== null && beamDuration !== undefined && beamDurationOperator) {
beamDuration === 0 ? queryBuilder.where('stableBeamsDuration').applyOperator(beamDurationOperator, null)
Expand All @@ -74,6 +85,11 @@ class GetAllLhcFillsUseCase {
where: { definition: RunDefinition.PHYSICS },
required: false,
});
queryBuilder.include({
association: 'statistics',
required: associatedStatisticsRequired,
});

queryBuilder.orderBy('fillNumber', 'desc');
queryBuilder.limit(limit);
queryBuilder.offset(offset);
Expand Down
62 changes: 61 additions & 1 deletion test/lib/usecases/lhcFill/GetAllLhcFillsUseCase.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ module.exports = () => {
})

// Beam duration filter tests

it('should only contain specified stable beam durations, < 12:00:00', async () => {
getAllLhcFillsDto.query = { filter: { beamDuration: '43200', beamDurationOperator: '<' } };
const { lhcFills } = await new GetAllLhcFillsUseCase().execute(getAllLhcFillsDto);
Expand Down Expand Up @@ -160,4 +159,65 @@ module.exports = () => {
expect(lhcFill.stableBeamsDuration).equals(null)
});
})

it('should only contain specified total run duration, > 04:00:00', async () => {
getAllLhcFillsDto.query = { filter: { runDuration: '14400', runDurationOperator: '>' } };
const { lhcFills } = await new GetAllLhcFillsUseCase().execute(getAllLhcFillsDto)

expect(lhcFills).to.be.an('array').and.lengthOf(1)
lhcFills.forEach((lhcFill) => {
expect(lhcFill.statistics.runsCoverage).greaterThan(14400)
});
})

it('should only contain specified total run duration, >= 05:00:00', async () => {
getAllLhcFillsDto.query = { filter: { runDuration: '18000', runDurationOperator: '>=' } };
const { lhcFills } = await new GetAllLhcFillsUseCase().execute(getAllLhcFillsDto)

expect(lhcFills).to.be.an('array').and.lengthOf(1)
lhcFills.forEach((lhcFill) => {
expect(lhcFill.statistics.runsCoverage).greaterThan(18000)
});
})

it('should only contain specified total run duration, = 05:00:00', async () => {
getAllLhcFillsDto.query = { filter: { runDuration: '18000', runDurationOperator: '=' } };
const { lhcFills } = await new GetAllLhcFillsUseCase().execute(getAllLhcFillsDto)

expect(lhcFills).to.be.an('array').and.lengthOf(1)
lhcFills.forEach((lhcFill) => {
expect(lhcFill.statistics.runsCoverage).greaterThan(18000)
});
})

it('should only contain specified total run duration, = 00:00:00', async () => {
// Tests the usecase's ability to replace the request for 0 to a request for null.
getAllLhcFillsDto.query = { filter: { runDuration: 0, runDurationOperator: '=' } };
const { lhcFills } = await new GetAllLhcFillsUseCase().execute(getAllLhcFillsDto)

expect(lhcFills).to.be.an('array').and.lengthOf(4)
lhcFills.forEach((lhcFill) => {
expect(lhcFill.statistics.runsCoverage).equals(0)
});
})

it('should only contain specified total run duration, <= 05:00:00', async () => {
getAllLhcFillsDto.query = { filter: { runDuration: '18000', runDurationOperator: '<=' } };
const { lhcFills } = await new GetAllLhcFillsUseCase().execute(getAllLhcFillsDto)

expect(lhcFills).to.be.an('array').and.lengthOf(1)
lhcFills.forEach((lhcFill) => {
expect(lhcFill.statistics.runsCoverage).greaterThan(18000)
});
})

it('should only contain specified total run duration, < 06:30:59', async () => {
getAllLhcFillsDto.query = { filter: { runDuration: '23459', runDurationOperator: '<' } };
const { lhcFills } = await new GetAllLhcFillsUseCase().execute(getAllLhcFillsDto)

expect(lhcFills).to.be.an('array').and.lengthOf(1)
lhcFills.forEach((lhcFill) => {
expect(lhcFill.statistics.runsCoverage).greaterThan(23459)
});
})
};
6 changes: 5 additions & 1 deletion test/public/lhcFills/overview.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,9 @@ module.exports = () => {
const filterSBExpect = { selector: '.stableBeams-filter .w-30', value: 'Stable Beams Only' };
const filterFillNRExpect = {selector: 'div.items-baseline:nth-child(1) > div:nth-child(1)', value: 'Fill #'}
const filterSBDurationExpect = {selector: 'div.items-baseline:nth-child(3) > div:nth-child(1)', value: 'SB Duration'}
const filterSBDurationPlaceholderExpect = {selector: 'input.w-100:nth-child(2)', value: 'e.g 16:14:15 (HH:MM:SS)'}
const filterSBDurationPlaceholderExpect = {selector: '.beam-duration-filter', value: 'e.g 16:14:15 (HH:MM:SS)'}
const filterRunDurationExpect = {selector: 'div.flex-row:nth-child(4) > div:nth-child(1)', value: 'Total runs duration'}
const filterRunDurationPlaceholderExpect = {selector: '.run-duration-filter', value: 'e.g 16:14:15 (HH:MM:SS)'}


await goToPage(page, 'lhc-fill-overview');
Expand All @@ -280,6 +282,8 @@ module.exports = () => {
await expectInnerText(page, filterFillNRExpect.selector, filterFillNRExpect.value);
await expectInnerText(page, filterSBDurationExpect.selector, filterSBDurationExpect.value);
await expectAttributeValue(page, filterSBDurationPlaceholderExpect.selector, 'placeholder', filterSBDurationPlaceholderExpect.value);
await expectInnerText(page, filterRunDurationExpect.selector, filterRunDurationExpect.value);
await expectAttributeValue(page, filterRunDurationPlaceholderExpect.selector, 'placeholder', filterRunDurationPlaceholderExpect.value);
});

it('should successfully un-apply Stable Beam filter menu', async () => {
Expand Down
Loading