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
2 changes: 2 additions & 0 deletions lib/database/adapters/SimulationPassAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class SimulationPassAdapter {

const dataPassesCount = databaseObject.get('dataPassesCount');
const runsCount = databaseObject.get('runsCount');
const logsCount = databaseObject.get('logsCount');
const pdpBeamTypes = databaseObject.get('pdpBeamTypes')?.split(',') || [];

return {
Expand All @@ -56,6 +57,7 @@ class SimulationPassAdapter {
outputSize,
runsCount,
dataPassesCount,
logsCount,
pdpBeamTypes,
};
}
Expand Down
1 change: 1 addition & 0 deletions lib/database/models/typedefs/SequelizeSimulationPass.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@
* @property {number} outputSize
* @property {number} dataPassesCount
* @property {number} runsCount
* @property {number} logsCount
*/
1 change: 1 addition & 0 deletions lib/domain/dtos/GetAllLogsDto.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const FilterDto = Joi.object({
rootOnly: Joi.boolean(),
environmentIds: EnvironmentsFilterDto,
dataPasses: StringsFilterDto,
simulationPasses: StringsFilterDto,
lhcPeriods: StringsFilterDto,
});

Expand Down
1 change: 1 addition & 0 deletions lib/domain/entities/SimulationPass.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@
* @property {number} outputSize
* @property {number} dataPassesCount
* @property {number} runsCount
* @property {number} logsCount
*/
10 changes: 9 additions & 1 deletion lib/public/components/Log/logDisplayComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ const logFields = () => ({
parameters: { dataPassId: id },
})),
},
simulationPasses: {
name: 'Simulation passes',
format: (simulationPasses) => detailsFrontLinks(simulationPasses, ({ id, name }) => ({
content: name,
page: 'runs-per-simulation-pass',
parameters: { simulationPassId: id },
})),
},
attachments: {
name: 'Attachments',
visible: true,
Expand Down Expand Up @@ -121,7 +129,7 @@ const displayLogText = (log, options) => {
);
};

const expandedLogFields = ['runs', 'environments', 'lhcFills', 'dataPasses', 'attachments'];
const expandedLogFields = ['runs', 'environments', 'lhcFills', 'dataPasses', 'simulationPasses', 'attachments'];

/**
* Returns a card for the given log
Expand Down
21 changes: 20 additions & 1 deletion lib/public/views/Logs/ActiveColumns/logsActiveColumns.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { formatRunsList } from '../../Runs/format/formatRunsList.js';
import { profiles } from '../../../components/common/table/profiles.js';
import { formatLhcFillsList } from '../../LhcFills/format/formatLhcFillsList.js';
import { formatDataPassesList } from '../../DataPasses/format/formatDataPassesList.js';
import { formatSimulationPassesList } from '../../SimulationPasses/format/formatSimulationPassesList.js';
import { formatTagsList } from '../../Tags/format/formatTagsList.js';
import { timeRangeFilter } from '../../../components/Filters/common/filters/timeRangeFilter.js';
import { textInputFilter } from '../../../components/Filters/common/filters/textInputFilter.js';
Expand Down Expand Up @@ -68,7 +69,7 @@ export const logsActiveColumns = {
name: 'Title',
visible: true,
sortable: true,
size: 'w-30',
size: 'w-20',

/**
* Title filter component
Expand Down Expand Up @@ -243,6 +244,24 @@ export const logsActiveColumns = {
balloon: true,
profiles: [profiles.none, 'embeded'],
},
simulationPasses: {
name: 'Simulation passes',
visible: true,
sortable: false,
size: 'w-10',
format: formatSimulationPassesList,

/**
* Simulation passes filter component
*
* @param {object} logOverviewModel the overview model
* @param {FilteringModel} logOverviewModel.filteringModel filtering model
* @return {Component} the filter component
*/
filter: ({ filteringModel }) => textInputFilter(filteringModel, 'simulationPasses', 'e.g. LHC23k6c, LHC23k6b'),
balloon: true,
profiles: [profiles.none, 'embeded'],
},
lhcPeriods: {
name: 'LHC Periods',
visible: false,
Expand Down
1 change: 1 addition & 0 deletions lib/public/views/Logs/Overview/LogsOverviewModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export class LogsOverviewModel extends FilterableOverviewPageModel {
environmentIds: new RawTextFilterModel(),
fillNumbers: new RawTextFilterModel(),
dataPasses: new RawTextFilterModel(),
simulationPasses: new RawTextFilterModel(),
lhcPeriods: new RawTextFilterModel(),
created: new TimeRangeInputModel(),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,20 @@ export const simulationPassesActiveColumns = {
classes: 'w-10 f6',
},

associatedLogs: {
name: 'Logs',
visible: true,
format: (_, { name, logsCount }) =>
logsCount === 0
? 'No logs'
: frontLink(
badge(logsCount),
'log-overview',
{ 'filter[simulationPasses]': name },
),
classes: 'w-10 f6',
},

description: {
name: 'Description',
visible: true,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* @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.
*/

import { frontLinks } from '../../../components/common/navigation/frontLinks.js';

/**
* Format a given simulation passes list as a list of hyperlink to their runs page
*
* @param {{id: number, name: string}[]} simulationPasses the list of simulation passes to display
* @return {Component} the list of simulation passes
*/
export const formatSimulationPassesList = (simulationPasses) => frontLinks(simulationPasses, ({ id, name }) => ({
content: name,
page: 'runs-per-simulation-pass',
parameters: { simulationPassId: id },
}));
1 change: 1 addition & 0 deletions lib/server/services/log/LogService.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ class LogService {
.include({ association: 'runs', attributes: ['id', 'runNumber'] })
.include({ association: 'lhcFills', attributes: ['fillNumber'] })
.include({ association: 'dataPasses', attributes: ['id', 'name'] })
.include({ association: 'simulationPasses', attributes: ['id', 'name'] })
.include({ association: 'environments', attributes: ['id'] })
.include({ association: 'user', attributes: ['name'] })
.include('tags')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ class SimulationPassService {
query: (sequelize) => sequelize.fn('count', sequelize.fn('distinct', sequelize.col('dataPasses.id'))),
alias: 'dataPassesCount',
})
// Use a subquery to not multiply the joined rows by the number of logs
.includeAttribute({
query: '(SELECT COUNT(*) FROM log_simulation_passes WHERE log_simulation_passes.simulation_pass_id = `SimulationPass`.id)',
alias: 'logsCount',
})
.includeAttribute({
query: (sequelize) => sequelize.fn('GROUP_CONCAT', sequelize.fn('DISTINCT', sequelize.col('runs.pdp_beam_type'))),
alias: 'pdpBeamTypes',
Expand Down
11 changes: 11 additions & 0 deletions lib/usecases/log/GetAllLogsUseCase.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const {
LogLhcFillsRepository,
LogEnvironmentsRepository,
LogDataPassesRepository,
LogSimulationPassesRepository,
},
} = require('../../database');
const { getTagsByText } = require('../../server/services/tag/getTagsByText.js');
Expand Down Expand Up @@ -54,6 +55,7 @@ const applyFilter = async (dataSource, queryBuilder, filter) => {
environmentIds,
fillNumbers,
dataPasses,
simulationPasses,
lhcPeriods,
} = filter;

Expand Down Expand Up @@ -164,6 +166,14 @@ const applyFilter = async (dataSource, queryBuilder, filter) => {
queryBuilder.where('id').oneOf(...new Set(logDataPasses.map(({ logId }) => logId)));
}

if (simulationPasses) {
// The log must be linked to at least one of the given simulation passes
const logSimulationPasses = await LogSimulationPassesRepository.findAll(dataSource.createQueryBuilder()
.include({ association: 'simulationPass', attributes: [], required: true, where: { name: { [Op.in]: simulationPasses } } }));

queryBuilder.where('id').oneOf(...new Set(logSimulationPasses.map(({ logId }) => logId)));
}

if (lhcPeriods) {
// Logs are not directly linked to LHC periods, use the period of their linked data passes. At least one period must match
const logDataPasses = await LogDataPassesRepository.findAll(dataSource.createQueryBuilder()
Expand Down Expand Up @@ -253,6 +263,7 @@ class GetAllLogsUseCase {
.include({ association: 'runs', attributes: ['id', 'runNumber'] })
.include({ association: 'lhcFills', attributes: ['fillNumber'] })
.include({ association: 'dataPasses', attributes: ['id', 'name'] })
.include({ association: 'simulationPasses', attributes: ['id', 'name'] })
.include('attachments')
.include({ association: 'environments', attributes: ['id'] });

Expand Down
1 change: 1 addition & 0 deletions test/api/environments.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ module.exports = () => {
expect(log.lhcFills.map(({ fillNumber }) => fillNumber)).to.have.members([5, 6]);
expect(log.environments.map(({ id }) => id)).to.have.members(['8E4aZTjY', 'eZF99lH6']);
expect(log.dataPasses.map(({ name }) => name)).to.have.members(['LHC22b_apass1', 'LHC22b_skimming']);
expect(log.simulationPasses.map(({ name }) => name)).to.have.members(['LHC23k6c', 'LHC23k6b']);
expect(log.attachments).to.be.an('array');
});
});
Expand Down
1 change: 1 addition & 0 deletions test/api/lhcFills.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,7 @@ module.exports = () => {
expect(log.lhcFills.map(({ fillNumber }) => fillNumber)).to.have.members([5, 6]);
expect(log.environments.map(({ id }) => id)).to.have.members(['8E4aZTjY', 'eZF99lH6']);
expect(log.dataPasses.map(({ name }) => name)).to.have.members(['LHC22b_apass1', 'LHC22b_skimming']);
expect(log.simulationPasses.map(({ name }) => name)).to.have.members(['LHC23k6c', 'LHC23k6b']);
expect(log.attachments).to.be.an('array');
});
});
Expand Down
26 changes: 24 additions & 2 deletions test/api/logs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,17 @@ module.exports = () => {
}
});

it('should return the simulation passes linked to each log', async () => {
const response = await request(server).get('/api/logs?sort[id]=asc');
expect(response.status).to.equal(200);

const log1 = response.body.data.find(({ id }) => id === 1);
expect(log1.simulationPasses.map(({ name }) => name).sort()).to.deep.equal(['LHC23k6b', 'LHC23k6c']);
for (const { simulationPasses } of response.body.data) {
expect(simulationPasses).to.be.an('array');
}
});

it('should return the data passes linked to each log', async () => {
const response = await request(server).get('/api/logs?sort[id]=asc');
expect(response.status).to.equal(200);
Expand All @@ -278,6 +289,17 @@ module.exports = () => {
expect(singleResponse.body.data.map(({ id }) => id).sort((a, b) => a - b)).to.deep.equal([1, 119]);
});

it('should successfully filter by simulation pass names', async () => {
// At least one of the simulation passes must be linked to the log
const response = await request(server).get('/api/logs?filter[simulationPasses]=LHC23k6b,LHC23k6a');
expect(response.status).to.equal(200);
expect(response.body.data.map(({ id }) => id).sort((a, b) => a - b)).to.deep.equal([1, 2]);

const singleResponse = await request(server).get('/api/logs?filter[simulationPasses]=LHC23k6c');
expect(singleResponse.status).to.equal(200);
expect(singleResponse.body.data.map(({ id }) => id).sort((a, b) => a - b)).to.deep.equal([1, 119]);
});

it('should successfully filter by LHC period names', async () => {
const response = await request(server).get('/api/logs?filter[lhcPeriods]=LHC22a');
expect(response.status).to.equal(200);
Expand All @@ -288,8 +310,8 @@ module.exports = () => {
expect(multipleResponse.body.data.map(({ id }) => id).sort((a, b) => a - b)).to.deep.equal([1, 2, 119]);
});

it('should return an empty list when filtering by unknown data pass or LHC period', async () => {
for (const [filterKey, value] of [['dataPasses', 'NOT_A_DATA_PASS'], ['lhcPeriods', 'NOT_A_PERIOD']]) {
it('should return an empty list when filtering by unknown data pass, simulation pass or LHC period', async () => {
for (const [filterKey, value] of [['dataPasses', 'NOT_A_DATA_PASS'], ['simulationPasses', 'NOT_A_SIMULATION_PASS'], ['lhcPeriods', 'NOT_A_PERIOD']]) {
const response = await request(server).get(`/api/logs?filter[${filterKey}]=${value}`);
expect(response.status).to.equal(200);
expect(response.body.data).to.lengthOf(0);
Expand Down
1 change: 1 addition & 0 deletions test/api/runs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1071,6 +1071,7 @@ module.exports = () => {
expect(log.lhcFills.map(({ fillNumber }) => fillNumber)).to.have.members([5, 6]);
expect(log.environments.map(({ id }) => id)).to.have.members(['8E4aZTjY', 'eZF99lH6']);
expect(log.dataPasses.map(({ name }) => name)).to.have.members(['LHC22b_apass1', 'LHC22b_skimming']);
expect(log.simulationPasses.map(({ name }) => name)).to.have.members(['LHC23k6c', 'LHC23k6b']);
expect(log.attachments).to.be.an('array');
// Tags must be complete to be displayed with their color and archived state
expect(log.tags).to.not.be.empty;
Expand Down
3 changes: 3 additions & 0 deletions test/api/simulationPasses.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const LHC23k6c = {
outputSize: 14013600611699,
dataPassesCount: 2,
runsCount: 3,
logsCount: 2,
};

const LHC23k6b = {
Expand All @@ -42,6 +43,7 @@ const LHC23k6b = {
outputSize: 157000310748,
dataPassesCount: 1,
runsCount: 3,
logsCount: 1,
};

const LHC23k6a = {
Expand All @@ -56,6 +58,7 @@ const LHC23k6a = {
outputSize: 147000310748,
dataPassesCount: 1,
runsCount: 1,
logsCount: 1,
};

module.exports = () => {
Expand Down
1 change: 1 addition & 0 deletions test/api/tags.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,7 @@ module.exports = () => {
expect(log.lhcFills.map(({ fillNumber }) => fillNumber)).to.have.members([5, 6]);
expect(log.environments.map(({ id }) => id)).to.have.members(['8E4aZTjY', 'eZF99lH6']);
expect(log.dataPasses.map(({ name }) => name)).to.have.members(['LHC22b_apass1', 'LHC22b_skimming']);
expect(log.simulationPasses.map(({ name }) => name)).to.have.members(['LHC23k6c', 'LHC23k6b']);
expect(log.attachments).to.be.an('array');
});
});
Expand Down
1 change: 1 addition & 0 deletions test/lib/server/services/log/LogService.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ module.exports = () => {
expect(log.lhcFills.map(({ fillNumber }) => fillNumber)).to.have.members([5, 6]);
expect(log.environments.map(({ id }) => id)).to.have.members(['8E4aZTjY', 'eZF99lH6']);
expect(log.dataPasses.map(({ name }) => name)).to.have.members(['LHC22b_apass1', 'LHC22b_skimming']);
expect(log.simulationPasses.map(({ name }) => name)).to.have.members(['LHC23k6c', 'LHC23k6b']);
// Complete tags (with their color and archived state) are required to display the logs, the author is only exposed by name
expect(log.tags).to.not.be.empty;
expect(log.tags.every(({ archived, updatedAt }) => typeof archived === 'boolean' && typeof updatedAt === 'number')).to.be.true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const LHC23k6c = {
outputSize: 14013600611699,
dataPassesCount: 2,
runsCount: 3,
logsCount: 2,
};

const LHC23k6b = {
Expand All @@ -43,6 +44,7 @@ const LHC23k6b = {
outputSize: 157000310748,
dataPassesCount: 1,
runsCount: 3,
logsCount: 1,
};

const LHC23k6a = {
Expand All @@ -57,6 +59,7 @@ const LHC23k6a = {
outputSize: 147000310748,
dataPassesCount: 1,
runsCount: 1,
logsCount: 1,
};

module.exports = () => {
Expand Down
Loading
Loading