Skip to content

Commit 9cab1e7

Browse files
committed
Extract dedicated services for GAQ and QC summaries
1 parent d927011 commit 9cab1e7

6 files changed

Lines changed: 413 additions & 346 deletions

File tree

lib/server/controllers/qcFlag.controller.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ const { PaginationDto } = require('../../domain/dtos');
1919
const { ApiConfig } = require('../../config');
2020
const { countedItemsToHttpView } = require('../utilities/countedItemsToHttpView');
2121
const { qcFlagService } = require('../services/qualityControlFlag/QcFlagService.js');
22+
const { gaqService } = require('../services/qualityControlFlag/GaqService.js');
23+
const { qcFlagSummaryService } = require('../services/qualityControlFlag/QcFlagSummaryService.js');
2224

2325
// eslint-disable-next-line valid-jsdoc
2426
/**
@@ -324,7 +326,7 @@ const getQcFlagsSummaryHandler = async (req, res) => {
324326
mcReproducibleAsNotBad = false,
325327
} = validatedDTO.query;
326328

327-
const data = await qcFlagService.getQcFlagsSummary({ dataPassId, simulationPassId, lhcPeriodId }, { mcReproducibleAsNotBad });
329+
const data = await qcFlagSummaryService.getQcFlagsSummary({ dataPassId, simulationPassId, lhcPeriodId }, { mcReproducibleAsNotBad });
328330
res.json({ data });
329331
} catch (error) {
330332
updateExpressResponseFromNativeError(res, error);
@@ -349,7 +351,7 @@ const getGaqQcFlagsHandler = async (request, response) => {
349351
try {
350352
const { dataPassId, runNumber } = validatedDTO.query;
351353

352-
const data = await qcFlagService.getGaqFlags(dataPassId, runNumber);
354+
const data = await gaqService.getFlagsForDataPassAndRun(dataPassId, runNumber);
353355
response.json({ data });
354356
} catch (error) {
355357
updateExpressResponseFromNativeError(response, error);
@@ -374,7 +376,7 @@ const getGaqSummaryHandler = async (request, response) => {
374376
try {
375377
const { dataPassId, mcReproducibleAsNotBad = false } = validatedDTO.query;
376378

377-
const data = await qcFlagService.getGaqSummary(dataPassId, { mcReproducibleAsNotBad });
379+
const data = await gaqService.getSummary(dataPassId, { mcReproducibleAsNotBad });
378380
response.json({ data });
379381
} catch (error) {
380382
updateExpressResponseFromNativeError(response, error);
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
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 { getOneDataPassOrFail } from '../dataPasses/getOneDataPassOrFail.js';
14+
import { Op } from 'sequelize';
15+
import { qcFlagAdapter } from '../../../database/adapters/index.js';
16+
import { QcFlagRepository } from '../../../database/repositories/index.js';
17+
import { QcFlagSummaryService } from './QcFlagSummaryService.js';
18+
19+
const QC_SUMMARY_PROPERTIES = {
20+
badEffectiveRunCoverage: 'badEffectiveRunCoverage',
21+
explicitlyNotBadEffectiveRunCoverage: 'explicitlyNotBadEffectiveRunCoverage',
22+
missingVerificationsCount: 'missingVerificationsCount',
23+
mcReproducible: 'mcReproducible',
24+
};
25+
26+
/**
27+
* Globally aggregated quality (QC flags aggregated for a predefined list of detectors per runs) service
28+
*/
29+
export class GaqService {
30+
/**
31+
* Get GAQ summary
32+
*
33+
* @param {number} dataPassId id of data pass id
34+
* @param {object} [options] additional options
35+
* @param {boolean} [options.mcReproducibleAsNotBad = false] if set to true,
36+
* `Limited Acceptance MC Reproducible` flag type is treated as good one
37+
* @return {Promise<GaqSummary[]>} Resolves with the GAQ Summary
38+
*/
39+
async getSummary(dataPassId, { mcReproducibleAsNotBad = false } = {}) {
40+
await getOneDataPassOrFail({ id: dataPassId });
41+
const runGaqSubSummaries = await QcFlagRepository.getRunGaqSubSummaries(dataPassId, { mcReproducibleAsNotBad });
42+
43+
const summary = {};
44+
const flagsAndVerifications = {};
45+
46+
// Fold list of subSummaries into one summary
47+
for (const subSummary of runGaqSubSummaries) {
48+
const {
49+
runNumber,
50+
flagsIds,
51+
verifiedFlagsIds,
52+
} = subSummary;
53+
54+
if (!summary[runNumber]) {
55+
summary[runNumber] = { [QC_SUMMARY_PROPERTIES.mcReproducible]: false };
56+
}
57+
if (!flagsAndVerifications[runNumber]) {
58+
flagsAndVerifications[runNumber] = {};
59+
}
60+
61+
const runSummary = summary[runNumber];
62+
63+
const distinctRunFlagsIds = flagsAndVerifications[runNumber]?.distinctFlagsIds ?? [];
64+
const distinctRunVerifiedFlagsIds = flagsAndVerifications[runNumber]?.distinctVerifiedFlagsIds ?? [];
65+
66+
flagsAndVerifications[runNumber] = {
67+
distinctFlagsIds: new Set([...distinctRunFlagsIds, ...flagsIds]),
68+
distinctVerifiedFlagsIds: new Set([...distinctRunVerifiedFlagsIds, ...verifiedFlagsIds]),
69+
};
70+
71+
QcFlagSummaryService.mergeIntoSummaryUnit(runSummary, subSummary);
72+
}
73+
74+
for (const [runNumber, { distinctFlagsIds, distinctVerifiedFlagsIds }] of Object.entries(flagsAndVerifications)) {
75+
summary[runNumber][QC_SUMMARY_PROPERTIES.missingVerificationsCount] = distinctFlagsIds.size - distinctVerifiedFlagsIds.size;
76+
}
77+
78+
return summary;
79+
}
80+
81+
/**
82+
* Find QC flags in GAQ effective periods for given data pass and run
83+
*
84+
* @param {number} dataPassId id od data pass
85+
* @param {number} runNumber run number
86+
* @return {Promise<GaqFlags[]>} promise of aggregated QC flags
87+
*/
88+
async getFlagsForDataPassAndRun(dataPassId, runNumber) {
89+
const gaqPeriods = await QcFlagRepository.findGaqPeriods(dataPassId, runNumber);
90+
const qcFlags = (await QcFlagRepository.findAll({
91+
where: { id: { [Op.in]: gaqPeriods.flatMap(({ contributingFlagIds }) => contributingFlagIds) } },
92+
include: [
93+
{ association: 'flagType' },
94+
{ association: 'createdBy' },
95+
{ association: 'verifications', include: [{ association: 'createdBy' }] },
96+
],
97+
})).map(qcFlagAdapter.toEntity);
98+
99+
const idToFlag = Object.fromEntries(qcFlags.map((flag) => [flag.id, flag]));
100+
101+
return gaqPeriods.map(({
102+
contributingFlagIds,
103+
from,
104+
to,
105+
}) => ({
106+
from,
107+
to,
108+
contributingFlags: contributingFlagIds.map((id) => idToFlag[id]),
109+
}));
110+
}
111+
}
112+
113+
export const gaqService = new GaqService();

0 commit comments

Comments
 (0)