Skip to content
Merged
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
23 changes: 23 additions & 0 deletions lib/domain/dtos/common/BeamTypeDto.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* @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.
*/

const Joi = require('joi');
const { validateBeamTypes, BEAM_TYPE_INVALID } = require('../../../utilities/beamTypeUtils');

exports.BeamTypesDto = Joi.string()
.trim()
.custom(validateBeamTypes)
.messages({
[BEAM_TYPE_INVALID]: '{{#message}}',
'string.base': 'Beam type must be a string',
});
10 changes: 2 additions & 8 deletions lib/domain/dtos/filters/LhcFillsFilterDto.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*/
const Joi = require('joi');
const { validateRange, RANGE_INVALID } = require('../../../utilities/rangeUtils');
const { validateBeamTypes, BEAM_TYPE_INVALID } = require('../../../utilities/beamTypeUtils');
const { BeamTypesDto } = require('../common/BeamTypeDto.js');
const { validateTimeDuration } = require('../../../utilities/validateTime');
const { FromToFilterDto } = require('./FromToFilterDto.js');

Expand All @@ -27,11 +27,5 @@ exports.LhcFillsFilterDto = Joi.object({
stableBeamsStart: FromToFilterDto,
stableBeamsEnd: FromToFilterDto,
schemeName: Joi.string().trim().max(64),
beamTypes: Joi.string()
.trim()
.custom(validateBeamTypes)
.messages({
[BEAM_TYPE_INVALID]: '{{#message}}',
'string.base': 'Beam type must be a string',
}),
beamTypes: BeamTypesDto,
});
2 changes: 2 additions & 0 deletions lib/domain/dtos/filters/RunFilterDto.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/
const Joi = require('joi');
const { CustomJoi } = require('../CustomJoi.js');
const { BeamTypesDto } = require('../common/BeamTypeDto.js');
const { FromToFilterDto } = require('./FromToFilterDto.js');
const { RUN_QUALITIES } = require('../../enums/RunQualities.js');
const { IntegerComparisonDto, FloatComparisonDto } = require('./NumericalComparisonDto.js');
Expand Down Expand Up @@ -39,6 +40,7 @@ exports.RunFilterDto = Joi.object({
'string.pattern.base':
'Beam modes "{{#value}}" must contain only uppercase letters and single spaces between words.',
})),
beamTypes: BeamTypesDto,
runNumbers: Joi.string().trim().custom(validateRange).messages({
[RANGE_INVALID]: '{{#message}}',
'string.base': 'Run numbers must be comma-separated numbers or ranges (e.g. 12,15-18)',
Expand Down
6 changes: 6 additions & 0 deletions lib/public/views/Runs/ActiveColumns/runsActiveColumns.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import { numericalComparisonFilter } from '../../../components/Filters/common/fi
import { checkboxes } from '../../../components/Filters/common/filters/checkboxFilter.js';
import radioButtonFilter from '../../../components/Filters/common/filters/radioButtonFilter.js';
import { textInputFilter } from '../../../components/Filters/common/filters/textInputFilter.js';
import { beamTypeFilter } from '../../../components/Filters/LhcFillsFilter/beamTypeFilter.js';

/**
* List of active columns for a generic runs table
Expand Down Expand Up @@ -588,6 +589,11 @@ export const runsActiveColumns = {
name: 'PDP Beam Type',
visible: false,
},
beamType: {
name: 'Beam Type',
visible: false,
filter: (runsOverviewModel) => beamTypeFilter(runsOverviewModel.filteringModel.get('beamTypes')),
},
readoutCfgUri: {
name: 'Readout Config URI',
visible: false,
Expand Down
2 changes: 2 additions & 0 deletions lib/public/views/Runs/Overview/RunsOverviewModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { beamModesProvider } from '../../../services/beamModes/beamModesProvider
import { RadioButtonFilterModel } from '../../../components/Filters/common/RadioButtonFilterModel.js';
import { SelectionModel } from '../../../components/common/selection/SelectionModel.js';
import { TRIGGER_VALUES } from '../../../domain/enums/TriggerValue.js';
import { BeamTypeFilterModel } from '../../../components/Filters/LhcFillsFilter/BeamTypeFilterModel.js';

/**
* Model representing handlers for runs page
Expand Down Expand Up @@ -95,6 +96,7 @@ export class RunsOverviewModel extends FilterableOverviewPageModel {
dcs: new RadioButtonFilterModel([{ label: 'ANY' }, { label: 'ON', value: true }, { label: 'OFF', value: false }]),
epn: new RadioButtonFilterModel([{ label: 'ANY' }, { label: 'ON', value: true }, { label: 'OFF', value: false }]),
triggerValues: new SelectionModel({ availableOptions: TRIGGER_VALUES.map((value) => ({ label: value, value })) }),
beamTypes: new BeamTypeFilterModel(),
},
);

Expand Down
10 changes: 10 additions & 0 deletions lib/usecases/run/GetAllRunsUseCase.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class GetAllRunsUseCase {
gaq,
detectorsQcNotBadFraction,
beamModes,
beamTypes,
} = filter;

if (runNumbers) {
Expand Down Expand Up @@ -119,6 +120,15 @@ class GetAllRunsUseCase {
filteringQueryBuilder.where('lhcBeamMode').oneOf(...beamModes);
}

if (beamTypes) {
const beamTypesList = splitStringToStringsTrimmed(beamTypes, SEARCH_ITEMS_SEPARATOR);
filteringQueryBuilder.include({
association: 'lhcFill',
where: { beamType: { [Op.in]: beamTypesList } },
required: true,
});
}

if (eorReason) {
const eorReasonTypeWhere = {};
if (eorReason.category) {
Expand Down
36 changes: 36 additions & 0 deletions test/api/runs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,42 @@ module.exports = () => {
expect(runs).to.lengthOf(6);
});

it('should successfully filter with single beamType', async () => {
const beamType = 'p-p';
const response = await request(server).get(`/api/runs?filter[beamTypes]=${beamType}`);

expect(response.status).to.equal(200);
const { data: runs } = response.body;

expect(runs).to.be.an('array');
expect(runs).to.have.lengthOf.greaterThan(0);
expect(runs.every(({ lhcFill }) => lhcFill?.beamType === beamType)).to.be.true;
});

it('should successfully filter with multiple beamTypes', async () => {
const beamTypes = ['p-p', 'Pb-Pb'];
const response = await request(server).get(`/api/runs?filter[beamTypes]=${beamTypes.join(',')}`);

expect(response.status).to.equal(200);
const { data: runs } = response.body;

expect(runs).to.be.an('array');
expect(runs).to.have.lengthOf.greaterThan(0);
expect(runs.every(({ lhcFill }) => beamTypes.includes(lhcFill?.beamType))).to.be.true;
});

it('should return 400 if beamTypes filter has the incorrect format', async () => {
const beamTypeString = 'DOES NOT EXIST';
const response = await request(server).get(`/api/runs?filter[beamTypes]=${beamTypeString}`);

expect(response.status).to.equal(400);

const { errors: [error] } = response.body;

expect(error.title).to.equal('Invalid Attribute');
expect(error.detail).to.equal(`Invalid beam type format: ${beamTypeString}`);
});

it('should return 400 if beamModes filter has the incorrect format', async () => {
const beamModeString = '*THERE\'S NON LETTERS IN HERE';
const response = await request(server).get(`/api/runs?filter[beamModes]=${beamModeString}`);
Expand Down
27 changes: 27 additions & 0 deletions test/lib/usecases/run/GetAllRunsUseCase.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,33 @@ module.exports = () => {
}
});

it('should successfully filter on beamTypes', async () => {
const singleBeamType = 'p-p';
const multipleBeamTypes = 'p-p,Pb-Pb';
const nonExistentBeamType = 'DOES-NOT-EXIST';

getAllRunsDto.query = { filter: { beamTypes: singleBeamType }, page: { limit: 200 } };
{
const { runs } = await new GetAllRunsUseCase().execute(getAllRunsDto);
expect(runs).to.have.lengthOf.greaterThan(0);
expect(runs.every(({ lhcFill }) => lhcFill?.beamType === singleBeamType)).to.be.true;
}

getAllRunsDto.query = { filter: { beamTypes: multipleBeamTypes }, page: { limit: 200 } };
{
const acceptedBeamTypes = multipleBeamTypes.split(',');
const { runs } = await new GetAllRunsUseCase().execute(getAllRunsDto);
expect(runs).to.have.lengthOf.greaterThan(0);
expect(runs.every(({ lhcFill }) => acceptedBeamTypes.includes(lhcFill?.beamType))).to.be.true;
}

getAllRunsDto.query = { filter: { beamTypes: nonExistentBeamType } };
{
const { runs } = await new GetAllRunsUseCase().execute(getAllRunsDto);
expect(runs).to.have.lengthOf(0);
}
});

it('should successfully filter on run definition', async () => {
const PHYSICS_COUNT = 7;
const COSMICS_COUNT = 2;
Expand Down