Skip to content

Commit 58563b2

Browse files
[O2B-1552] Move ad hoc filters inside runs overviews model to filtering model (#2106)
Notable changes for users: - The monte carlo reproducable as not bad filters from the runs per .... overview pages has been changed to also be cleared when the 'reset all filters' button is pressed to be in line with all other filters. Notable changes for developers: **Filter restructuring** - `ddflpFilter`, `dcsFilter`, and `epnFilter` have been moved to the `filteringModel`. - `detectorsQc[_id][notBadFraction]` filters have been moved to a new model: `MultiCompositeFilterModel`. - `gaq[notBadFraction]` now uses its own filter model with unique normalization logic: `GaqFilterModel`. **New filter models** - **`GaqFilterModel`** - Dedicated model for `gaq[notBadFraction]`. - Implements custom normalization logic. - **`ToggleFilterModel`** - Introduced for toggle-based filters. - `mcReproducibleAsNotBad` has been converted to use this model. - **`MultiCompositeFilterModel`** - New flexible model: - Can be instantiated with filters. - Supports adding filters dynamically after instantiation. - Computes normalized values based on: - Insertion keys - Normalized child filters **Shared filter behavior** - `mcReproducibleAsNotBad`: - Converted into a `ToggleFilterModel`. - Integrated into both: - `GaqFilterModel` - `MultiCompositeFilterModel` - **Important:** both models must use the same instance for correct behavior. **New components** - `RadioButtonFilter` - Replaces filters associated with the restructured filter models. - `ToggleFilter` (component + model) - Introduced alongside `ToggleFilterModel`. **Renaming** - `detectorsQc[_id][notBadFraction]` → `detectorsQcNotBadFraction[_id]` - Reduces nesting within the `filterModel`. **Implementation note** - This approach avoids: - Creating a custom wrapper around `NumericalComparisonFilterModel` solely for normalization, or - Nesting `MultiCompositeFilterModel` within another `MultiCompositeFilterModel` to achieve automatic normalization.
1 parent 9e26f06 commit 58563b2

34 files changed

Lines changed: 465 additions & 635 deletions

lib/domain/dtos/filters/RunFilterDto.js

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -88,26 +88,27 @@ exports.RunFilterDto = Joi.object({
8888
inelasticInteractionRateAtEnd: FloatComparisonDto,
8989

9090
gaq: Joi.object({
91-
notBadFraction: FloatComparisonDto.when(
92-
'dataPassIds',
93-
{
94-
is: Joi.array().length(1),
95-
then: FloatComparisonDto,
96-
otherwise: Joi.forbidden().error(new Error('Filtering by GAQ is enabled only when filtering with one dataPassId')),
97-
},
98-
),
91+
notBadFraction: FloatComparisonDto.custom((value, helpers) => {
92+
const [, { dataPassIds }] = helpers.state.ancestors;
93+
94+
if (!dataPassIds || dataPassIds.length !== 1) {
95+
return helpers.message('Filtering by GAQ is enabled only when filtering with one dataPassId');
96+
}
97+
98+
return value;
99+
}),
99100
mcReproducibleAsNotBad: Joi.boolean().optional(),
100101
}),
101102

102-
detectorsQc: Joi.object()
103+
detectorsQcNotBadFraction: Joi.object()
103104
.pattern(
104105
Joi.string().regex(/^_\d+$/), // Detector id with '_' prefix
105-
Joi.object({ notBadFraction: FloatComparisonDto }),
106+
FloatComparisonDto,
106107
)
107108
.keys({
108109
mcReproducibleAsNotBad: Joi.boolean().optional(),
109110
})
110-
.custom((detectorsQcObj, helpers) => {
111+
.custom((detectorsQcNotBadFractionObj, helpers) => {
111112
const [{ dataPassIds, simulationPassIds, lhcPeriodIds }] = helpers.state.ancestors;
112113

113114
singleRunsCollectionCustomCheck(
@@ -117,6 +118,6 @@ exports.RunFilterDto = Joi.object({
117118
'the dataPassIds, simulationPassIds and lhcPeriodIds filters collectively contain exactly one ID',
118119
);
119120

120-
return detectorsQcObj;
121+
return detectorsQcNotBadFractionObj;
121122
}),
122123
});

lib/public/components/Filters/LhcFillsFilter/StableBeamFilterModel.js

Lines changed: 0 additions & 76 deletions
This file was deleted.

lib/public/components/Filters/LhcFillsFilter/stableBeamFilter.js

Lines changed: 0 additions & 49 deletions
This file was deleted.
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/**
2+
* @license
3+
* Copyright CERN and copyright holders of ALICE Trg. 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-Trg.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+
14+
import { FilterModel } from '../common/FilterModel.js';
15+
import { NumericalComparisonFilterModel } from '../common/filters/NumericalComparisonFilterModel.js';
16+
17+
/**
18+
* FilterModel that filters by the fraction of gaq that was not bad
19+
*/
20+
export class GaqFilterModel extends FilterModel {
21+
/**
22+
* Constructor
23+
* @param {ToggleFilterModel} mcReproducibleAsNotBad model that determines if a 'not bad' status was reproduceable for a Monte Carlo.
24+
* This param is required as multiple other filters models need to make use of the same ToggleFilterModel instance
25+
*/
26+
constructor(mcReproducibleAsNotBad) {
27+
super();
28+
29+
this._notBadFraction = new NumericalComparisonFilterModel({ scale: 0.01, integer: false });
30+
this._addSubmodel(this._notBadFraction);
31+
this._mcReproducibleAsNotBad = mcReproducibleAsNotBad;
32+
33+
/**
34+
* _mcReproducableAsNotBad will only be added to the normalize call notBadFraction is not empty
35+
* So, notifying when it is empty will just send an unneeded request.
36+
*/
37+
this._mcReproducibleAsNotBad.visualChange$.bubbleTo(this._visualChange$);
38+
this._mcReproducibleAsNotBad.observe(() => {
39+
if (!this.notBadFraction.isEmpty) {
40+
this.notify();
41+
}
42+
});
43+
}
44+
45+
/**
46+
* @inheritDoc
47+
*/
48+
reset() {
49+
this._notBadFraction.reset();
50+
}
51+
52+
/**
53+
* @inheritDoc
54+
*/
55+
get isEmpty() {
56+
return this._notBadFraction.isEmpty;
57+
}
58+
59+
/**
60+
* @inheritDoc
61+
*/
62+
get normalized() {
63+
const normalized = { notBadFraction: this._notBadFraction.normalized };
64+
65+
if (!this.isEmpty) {
66+
normalized.mcReproducibleAsNotBad = this._mcReproducibleAsNotBad.isToggled;
67+
}
68+
69+
return normalized;
70+
}
71+
72+
/**
73+
* Return the underlying notBadFraction model
74+
*
75+
* @return {NumericalComparisonFilterModel} the filter model
76+
*/
77+
get notBadFraction() {
78+
return this._notBadFraction;
79+
}
80+
81+
/**
82+
* Return the underlying mcReproducibleAsNotBad model
83+
*
84+
* @return {ToggleFilterModel} the filter model
85+
*/
86+
get mcReproducibleAsNotBad() {
87+
return this._mcReproducibleAsNotBad;
88+
}
89+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/**
2+
* @license
3+
* Copyright CERN and copyright holders of ALICE Trg. 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-Trg.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+
14+
import { FilterModel } from '../common/FilterModel.js';
15+
16+
/**
17+
* FilterModel that allows devs to create custom filters from multiple other filters during instantiation, or using putFilter
18+
*/
19+
export class MultiCompositionFilterModel extends FilterModel {
20+
/**
21+
* Constructor
22+
* @param {Object<string|number, FilterModel|SelectionModel>} filters the filters that will make up the composite filter
23+
*/
24+
constructor(filters = {}) {
25+
super();
26+
27+
/**
28+
* @type {Object<string|number, FilterModel|SelectionModel>}
29+
*/
30+
this._filters = {};
31+
32+
Object.entries(filters).forEach(([key, filter]) => this.putFilter(key, filter));
33+
}
34+
35+
/**
36+
* Return a subfilter by key
37+
*
38+
* @param {string} key the key of the subfilter
39+
* @return {FilterModel} the subfilter
40+
*/
41+
putFilter(key, filterModel) {
42+
if (key in this._filters) {
43+
return;
44+
}
45+
46+
this._filters[key] = filterModel;
47+
this._addSubmodel(filterModel);
48+
}
49+
50+
/**
51+
* Add new subfilter
52+
*
53+
* @param {string} key key of the subfilter
54+
* @param {FilterModel} filter the the subfilter
55+
*/
56+
getFilter(key) {
57+
if (!(key in this._filters)) {
58+
throw new Error(`No filter found with key ${key}`);
59+
}
60+
61+
return this._filters[key];
62+
}
63+
64+
/**
65+
* @inheritDoc
66+
*/
67+
reset() {
68+
Object.values(this._filters).forEach((filter) => filter.reset());
69+
}
70+
71+
/**
72+
* @inheritDoc
73+
*/
74+
get isEmpty() {
75+
return Object.values(this._filters).every((filter) => filter.isEmpty);
76+
}
77+
78+
/**
79+
* @inheritDoc
80+
*/
81+
get normalized() {
82+
const normalized = {};
83+
84+
for (const [id, filter] of Object.entries(this._filters)) {
85+
if (!filter.isEmpty) {
86+
normalized[id] = filter.normalized;
87+
}
88+
}
89+
90+
return normalized;
91+
}
92+
}

0 commit comments

Comments
 (0)