Skip to content

Commit 2dfedd4

Browse files
authored
[O2B-1468] Exclude AOT and MUON QC flags created via gRPC from overview display (#1963)
* add * add other typeS * cleanup * allow multiple sources in obervable data and ref FilteringModel * rm error * add new columns to runs per lhc period * cleanup * rename migration * type * special handling of TST and ZDC * roll bakc * roll bakc * add test * fix tests * fix * fix * fix * fix * fix * fix * fixes * fix * fix * fixes * fix * fix * fix * fixes, grant more privileges in dev mode * rename var * rename * add fitering to service * add dto * d test * test * fix * fix * add filter on front * remove AOT and MUON flags from overview display * cleanup * fix * fix * add test * fix * fix
1 parent 5a2495d commit 2dfedd4

5 files changed

Lines changed: 183 additions & 6 deletions

File tree

lib/public/views/QcFlags/ForDataPass/QcFlagsForDataPassOverviewModel.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { buildUrl, RemoteData } from '/js/src/index.js';
1515
import { getRemoteData } from '../../../utilities/fetch/getRemoteData.js';
1616
import { QcFlagsOverviewModel } from '../Overview/QcFlagsOverviewModel.js';
1717
import { ObservableData } from '../../../utilities/ObservableData.js';
18+
import { DetectorType } from '../../../domain/enums/DetectorTypes.js';
1819

1920
/**
2021
* Quality Control Flags for data pass overview model
@@ -39,6 +40,12 @@ export class QcFlagsForDataPassOverviewModel extends QcFlagsOverviewModel {
3940
dataPassId: this._dataPassId,
4041
runNumber: this._runNumber,
4142
dplDetectorId: this._dplDetectorId,
43+
filter: this.dplDetector.match({
44+
Success: ({ type }) => [DetectorType.AOT_GLO, DetectorType.AOT_EVENT, DetectorType.MUON_GLO].includes(type)
45+
? { createdBy: { names: 'Anonymous', operator: 'none' } }
46+
: {},
47+
Other: () => {},
48+
}),
4249
});
4350
}
4451

lib/server/controllers/qcFlag.controller.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ const { qcFlagService } = require('../services/qualityControlFlag/QcFlagService.
2424
const { gaqService } = require('../services/qualityControlFlag/GaqService.js');
2525
const { qcFlagSummaryService } = require('../services/qualityControlFlag/QcFlagSummaryService.js');
2626

27+
const qcFlagFilterDTO = Joi.object({
28+
createdBy: Joi.object({
29+
operator: Joi.string().valid('or', 'none').required(),
30+
names: Joi.string().required().trim().custom((value) => value.split(',').map((s) => s.trim())),
31+
}),
32+
});
33+
2734
/**
2835
* Get one QC Flag by its id
2936
*/
@@ -53,6 +60,7 @@ const listQcFlagsPerDataPassHandler = async (req, res) => {
5360
runNumber: Joi.number(),
5461
dplDetectorId: Joi.number(),
5562
page: PaginationDto,
63+
filter: qcFlagFilterDTO,
5664
}),
5765
req,
5866
res,
@@ -64,11 +72,13 @@ const listQcFlagsPerDataPassHandler = async (req, res) => {
6472
runNumber,
6573
dplDetectorId,
6674
page: { limit = ApiConfig.pagination.limit, offset } = {},
75+
filter,
6776
} = validatedDTO.query;
6877

6978
const { count, rows: items } = await qcFlagService.getAllPerDataPassAndRunAndDetector(
7079
{ dataPassId, runNumber, detectorId: dplDetectorId },
7180
{ limit, offset },
81+
filter,
7282
);
7383
res.json(countedItemsToHttpView({ count, items }, limit));
7484
} catch (error) {
@@ -87,6 +97,7 @@ const listQcFlagsPerSimulationPassHandler = async (req, res) => {
8797
runNumber: Joi.number(),
8898
dplDetectorId: Joi.number(),
8999
page: PaginationDto,
100+
filter: qcFlagFilterDTO,
90101
}),
91102
req,
92103
res,
@@ -98,11 +109,13 @@ const listQcFlagsPerSimulationPassHandler = async (req, res) => {
98109
runNumber,
99110
dplDetectorId,
100111
page: { limit = ApiConfig.pagination.limit, offset } = {},
112+
filter,
101113
} = validatedDTO.query;
102114

103115
const { count, rows: items } = await qcFlagService.getAllPerSimulationPassAndRunAndDetector(
104116
{ simulationPassId, runNumber, detectorId: dplDetectorId },
105117
{ limit, offset },
118+
filter,
106119
);
107120
res.json(countedItemsToHttpView({ count, items }, limit));
108121
} catch (error) {
@@ -120,6 +133,7 @@ const listSynchronousQcFlagsHandler = async (req, res) => {
120133
runNumber: Joi.number(),
121134
detectorId: Joi.number(),
122135
page: PaginationDto,
136+
filter: qcFlagFilterDTO,
123137
}),
124138
req,
125139
res,
@@ -130,11 +144,13 @@ const listSynchronousQcFlagsHandler = async (req, res) => {
130144
runNumber,
131145
detectorId,
132146
page: { limit = ApiConfig.pagination.limit, offset } = {},
147+
filter,
133148
} = validatedDTO.query;
134149

135150
const { count, rows: items } = await qcFlagService.getAllSynchronousPerRunAndDetector(
136151
{ runNumber, detectorId },
137152
{ limit, offset },
153+
filter,
138154
);
139155
res.json(countedItemsToHttpView({ count, items }, limit));
140156
} catch (error) {

lib/server/services/qualityControlFlag/QcFlagService.js

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,17 @@ const validateUserDetectorAccess = (userRoles, detectorName) => {
6464
}
6565
};
6666

67+
/**
68+
* @typedef CreatedByFilter
69+
* @property {string} names
70+
* @property {'or'|'none'} operator
71+
*/
72+
73+
/**
74+
* @typedef QcFlagFilter
75+
* @property {CreatedByFilter} createdBy
76+
*/
77+
6778
/**
6879
* Quality control flags service
6980
*/
@@ -383,6 +394,27 @@ class QcFlagService {
383394
});
384395
}
385396

397+
/**
398+
* Get sequelize clause for CreateByFilter
399+
*
400+
* @param {QcFlagFilter} filter filter
401+
* @returns {object} sequelize clause
402+
* @throws {Error} if bad operator provided
403+
*/
404+
getCreatedByFilterClause(filter = {}) {
405+
const { createdBy: { names: createdByNames, operator: createdByOperator } = {} } = filter;
406+
407+
if (createdByNames && createdByOperator) {
408+
if (createdByOperator === 'or') {
409+
return { name: { [Op.or]: createdByNames } };
410+
} else if (createdByOperator === 'none') {
411+
return { [Op.not]: { name: { [Op.or]: createdByNames } } };
412+
} else {
413+
throw Error(`Operator ${createdByOperator} is not suppoerted`);
414+
}
415+
}
416+
}
417+
386418
/**
387419
* Return a paginated list of QC flags related to a given data pass, run and dpl detector
388420
*
@@ -393,9 +425,10 @@ class QcFlagService {
393425
* @param {object} [pagination] the pagination to apply
394426
* @param {number} [pagination.offset] amount of items to skip
395427
* @param {number} [pagination.limit] amount of items to fetch
428+
* @param {QcFlagFilter} [filter] filter
396429
* @return {Promise<{count, rows: DataPassQcFlag[]}>} paginated list of data pass QC flags
397430
*/
398-
async getAllPerDataPassAndRunAndDetector({ dataPassId, runNumber, detectorId }, pagination) {
431+
async getAllPerDataPassAndRunAndDetector({ dataPassId, runNumber, detectorId }, pagination, filter) {
399432
const { limit, offset } = pagination || {};
400433

401434
const queryBuilder = dataSource.createQueryBuilder()
@@ -404,7 +437,10 @@ class QcFlagService {
404437
association: 'qcFlag',
405438
include: [
406439
{ association: 'flagType' },
407-
{ association: 'createdBy' },
440+
{
441+
association: 'createdBy',
442+
where: this.getCreatedByFilterClause(filter),
443+
},
408444
{ association: 'verifications', include: [{ association: 'createdBy' }] },
409445
{ association: 'effectivePeriods' },
410446
],
@@ -446,9 +482,10 @@ class QcFlagService {
446482
* @param {object} [pagination] the pagination to apply
447483
* @param {number} [pagination.offset] amount of items to skip
448484
* @param {number} [pagination.limit] amount of items to fetch
485+
* @param {QcFlagFilter} [filter] filter
449486
* @return {Promise<{count, rows: SimulationPassQcFlag[]}>} paginated list of simulation pass QC flags
450487
*/
451-
async getAllPerSimulationPassAndRunAndDetector({ simulationPassId, runNumber, detectorId }, pagination) {
488+
async getAllPerSimulationPassAndRunAndDetector({ simulationPassId, runNumber, detectorId }, pagination, filter) {
452489
const { limit, offset } = pagination || {};
453490

454491
const queryBuilder = dataSource.createQueryBuilder()
@@ -457,7 +494,10 @@ class QcFlagService {
457494
association: 'qcFlag',
458495
include: [
459496
{ association: 'flagType' },
460-
{ association: 'createdBy' },
497+
{
498+
association: 'createdBy',
499+
where: this.getCreatedByFilterClause(filter),
500+
},
461501
{ association: 'verifications', include: [{ association: 'createdBy' }] },
462502
{ association: 'effectivePeriods' },
463503
],
@@ -498,9 +538,10 @@ class QcFlagService {
498538
* @param {object} [pagination] the pagination to apply
499539
* @param {number} [pagination.offset] amount of items to skip
500540
* @param {number} [pagination.limit] amount of items to fetch
541+
* @param {QcFlagFilter} [filter] filter
501542
* @return {Promise<{count, rows: SynchronousQcFlag[]}>} paginated list of synchronous QC flags
502543
*/
503-
async getAllSynchronousPerRunAndDetector({ runNumber, detectorId }, pagination) {
544+
async getAllSynchronousPerRunAndDetector({ runNumber, detectorId }, pagination, filter) {
504545
const { limit, offset } = pagination || {};
505546

506547
const queryParameters = {
@@ -513,7 +554,10 @@ class QcFlagService {
513554
},
514555
include: [
515556
{ association: 'flagType' },
516-
{ association: 'createdBy' },
557+
{
558+
association: 'createdBy',
559+
where: this.getCreatedByFilterClause(filter),
560+
},
517561
{ association: 'verifications', include: [{ association: 'createdBy' }] },
518562
{ association: 'dataPasses', required: false },
519563
{ association: 'simulationPasses', required: false },

test/api/qcFlags.test.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,64 @@ module.exports = () => {
294294
expect(titleError.detail).to.equal('"query.page.limit" must be greater than or equal to 1');
295295
}
296296
});
297+
298+
299+
it('should successfuly fiter data pass flags by created by name', async () => {
300+
const dataPassId = 1;
301+
const runNumber = 107;
302+
const detectorId = 1;
303+
{
304+
const response = await request(server)
305+
.get(`/api/qcFlags/perDataPass?dataPassId=${dataPassId}&runNumber=${runNumber}&dplDetectorId=${detectorId}&filter[createdBy][names]=John%20Doe&filter[createdBy][operator]=or`);
306+
expect(response.body.data).to.be.lengthOf(2);
307+
}
308+
309+
{
310+
const response = await request(server)
311+
.get(`/api/qcFlags/perDataPass?dataPassId=${dataPassId}&runNumber=${runNumber}&dplDetectorId=${detectorId}&filter[createdBy][names]=John%20Doe&filter[createdBy][operator]=none`);
312+
expect(response.body.data).to.be.lengthOf(0);
313+
}
314+
});
315+
316+
it('should get error when not supported operator is provided when filtering data pass QC flags by createdBy name ', async () => {
317+
const dataPassId = 1;
318+
const runNumber = 106;
319+
const detectorId = 1;
320+
{
321+
const response = await request(server)
322+
.get(`/api/qcFlags/perDataPass?dataPassId=${dataPassId}&runNumber=${runNumber}&dplDetectorId=${detectorId}&filter[createdBy][names]=Jan%20Jansen&filter[createdBy][operator]=SOME_OPERATOR`);
323+
expect(response.body.errors[0].detail).eql('\"query.filter.createdBy.operator\" must be one of [or, none]');
324+
}
325+
});
326+
327+
it('should successfuly fiter simulation pass flags by created by name', async () => {
328+
const simulationPassId = 1;
329+
const runNumber = 106;
330+
const detectorId = 1;
331+
{
332+
const response = await request(server)
333+
.get(`/api/qcFlags/perSimulationPass?simulationPassId=${simulationPassId}&runNumber=${runNumber}&dplDetectorId=${detectorId}&filter[createdBy][names]=Jan%20Jansen&filter[createdBy][operator]=or`);
334+
expect(response.body.data).to.be.lengthOf(2);
335+
}
336+
337+
{
338+
const response = await request(server)
339+
.get(`/api/qcFlags/perSimulationPass?simulationPassId=${simulationPassId}&runNumber=${runNumber}&dplDetectorId=${detectorId}&filter[createdBy][names]=Jan%20Jansen&filter[createdBy][operator]=none`);
340+
expect(response.body.data).to.be.lengthOf(0);
341+
}
342+
});
343+
344+
it('should get error when not supported operator is provided when filtering simulation pass QC flags by createdBy name ', async () => {
345+
const simulationPassId = 1;
346+
const runNumber = 106;
347+
const detectorId = 1;
348+
{
349+
const response = await request(server)
350+
.get(`/api/qcFlags/perSimulationPass?simulationPassId=${simulationPassId}&runNumber=${runNumber}&dplDetectorId=${detectorId}&filter[createdBy][names]=Jan%20Jansen&filter[createdBy][operator]=SOME_OPERATOR`);
351+
expect(response.body.errors[0].detail).eql('\"query.filter.createdBy.operator\" must be one of [or, none]');
352+
}
353+
});
354+
297355
});
298356

299357
describe('GET /api/qcFlags/synchronous', () => {
@@ -323,6 +381,22 @@ module.exports = () => {
323381
expect(flag.verifications[0].comment).to.be.equal('good');
324382
}
325383
});
384+
385+
it('should successfuly fiter sync flags by created by name', async () => {
386+
const runNumber = 56;
387+
const detectorId = 7;
388+
{
389+
const response = await request(server)
390+
.get(`/api/qcFlags/synchronous?runNumber=${runNumber}&detectorId=${detectorId}&filter[createdBy][names]=Jan%20Jansen&filter[createdBy][operator]=or`);
391+
expect(response.body.data).to.be.lengthOf(2);
392+
}
393+
394+
{
395+
const response = await request(server)
396+
.get(`/api/qcFlags/synchronous?runNumber=${runNumber}&detectorId=${detectorId}&filter[createdBy][names]=Jan%20Jansen&filter[createdBy][operator]=none`);
397+
expect(response.body.data).to.be.lengthOf(0);
398+
}
399+
});
326400
});
327401

328402
describe('POST /api/qcFlags', () => {

test/lib/server/services/qualityControlFlag/QcFlagService.test.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1748,4 +1748,40 @@ module.exports = () => {
17481748
});
17491749
});
17501750
});
1751+
1752+
it('should successfult fiter sync flags by created by name', async () => {
1753+
{
1754+
const { rows } = await qcFlagService.getAllSynchronousPerRunAndDetector({ runNumber: 56, detectorId: 7 }, {}, { createdBy: { names: ['Jan Jansen'], operator: 'or' }});
1755+
expect(rows).to.be.lengthOf(2);
1756+
}
1757+
1758+
{
1759+
const { rows } = await qcFlagService.getAllSynchronousPerRunAndDetector({ runNumber: 56, detectorId: 7 }, {}, { createdBy: { names: ['Jan Jansen'], operator: 'none' }});
1760+
expect(rows).to.be.lengthOf(0);
1761+
}
1762+
});
1763+
1764+
it('should successfult fiter data pass flags by created by name', async () => {
1765+
{
1766+
const { rows } = await qcFlagService.getAllPerDataPassAndRunAndDetector({ dataPassId: 1, runNumber: 107, detectorId: 1 }, {}, { createdBy: { names: ['John Doe'], operator: 'or' }});
1767+
expect(rows).to.be.lengthOf(2);
1768+
}
1769+
1770+
{
1771+
const { rows } = await qcFlagService.getAllPerDataPassAndRunAndDetector({ dataPassId: 1, runNumber: 107, detectorId: 1 }, {}, { createdBy: { names: ['John Doe'], operator: 'none' }});
1772+
expect(rows).to.be.lengthOf(0);
1773+
}
1774+
});
1775+
1776+
it('should successfult fiter simulation pass flags by created by name', async () => {
1777+
{
1778+
const { rows } = await qcFlagService.getAllPerSimulationPassAndRunAndDetector({ simulationPassId: 1, runNumber: 106, detectorId: 1 }, {}, { createdBy: { names: ['Jan Jansen'], operator: 'or' }});
1779+
expect(rows).to.be.lengthOf(2);
1780+
}
1781+
1782+
{
1783+
const { rows } = await qcFlagService.getAllPerSimulationPassAndRunAndDetector({ simulationPassId: 1, runNumber: 106, detectorId: 1 }, {}, { createdBy: { names: ['Jan Jansen'], operator: 'none' }});
1784+
expect(rows).to.be.lengthOf(0);
1785+
}
1786+
});
17511787
};

0 commit comments

Comments
 (0)