Skip to content

Commit

Permalink
[O2B-532] Create a reusable filtering system
Browse files Browse the repository at this point in the history
  • Loading branch information
martinboulais committed Dec 11, 2024
1 parent 62357ac commit 4fefe66
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 83 deletions.
3 changes: 2 additions & 1 deletion lib/public/components/Filters/common/FilterModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { Observable } from '/js/src/index.js';

/**
* Model storing the state of a given filter
*
* @abstract
*/
export class FilterModel extends Observable {
Expand Down Expand Up @@ -49,7 +50,7 @@ export class FilterModel extends Observable {
/**
* Returns the normalized value of the filter, that can be used as URL parameter
*
* @return {string|number|object|array|null} the normalized value
* @return {string|number|object|string[]|number[]|null} the normalized value
* @abstract
*/
get normalized() {
Expand Down
106 changes: 106 additions & 0 deletions lib/public/components/Filters/common/FilteringModel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/**
* @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 { Observable } from '/js/src/index.js';

/**
* @typedef FilteringItem
* @property {FilterModel} model the model of the filter
* @property {string} label the label of the filter
*/

/**
* Model representing a filtering system, including filter inputs visibility, filters values and so on
*/
export class FilteringModel extends Observable {
/**
* Constructor
*
* @param {Object<string, FilteringItem>} filters the filters with their label and model
*/
constructor(filters) {
super();

Check warning on line 32 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/components/Filters/common/FilteringModel.js#L31-L32

Added lines #L31 - L32 were not covered by tests

this._visualChange$ = new Observable();

Check warning on line 34 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/components/Filters/common/FilteringModel.js#L34

Added line #L34 was not covered by tests

this._filters = filters;
this._filterModels = Object.values(filters).map(({ model }) => model);
for (const model of this._filterModels) {
model.bubbleTo(this);
model.visualChange$?.bubbleTo(this._visualChange$);

Check warning on line 40 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/components/Filters/common/FilteringModel.js#L36-L40

Added lines #L36 - L40 were not covered by tests
}
}

/**
* Reset the filters
*
* @return {void}
*/
reset() {
for (const model of this._filterModels) {
model.reset();

Check warning on line 51 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/components/Filters/common/FilteringModel.js#L49-L51

Added lines #L49 - L51 were not covered by tests
}
}

/**
* Returns the normalized value of all the filters, without null values
*
* @return {object} the normalized values
*/
get normalized() {
const ret = {};
for (const [filterKey, { model: filter }] of Object.entries(this._filters)) {
if (filter && !filter.isEmpty) {
ret[filterKey] = filter.normalized;

Check warning on line 64 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/components/Filters/common/FilteringModel.js#L60-L64

Added lines #L60 - L64 were not covered by tests
}
}
return ret;

Check warning on line 67 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/components/Filters/common/FilteringModel.js#L67

Added line #L67 was not covered by tests
}

/**
* States if there is currently at least one filter active
*
* @return {boolean} true if at least one filter is active
*/
isAnyFilterActive() {
for (const model of this._filterModels) {
if (!model.isEmpty) {
return true;

Check warning on line 78 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/components/Filters/common/FilteringModel.js#L75-L78

Added lines #L75 - L78 were not covered by tests
}
}
return false;

Check warning on line 81 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/components/Filters/common/FilteringModel.js#L81

Added line #L81 was not covered by tests
}

/**
* Returns the observable notified any time there is a visual change which has no impact on the actual filtering
*
* @return {Observable} the filters visibility observable
*/
get visualChange$() {
return this._visualChange$;

Check warning on line 90 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/components/Filters/common/FilteringModel.js#L89-L90

Added lines #L89 - L90 were not covered by tests
}

/**
* Return the filter model for a given key
*
* @param {string} key the key of the filtering model
* @return {FilteringModel} the filtering model
*/
get(key) {
if (!(key in this._filters)) {
throw new Error(`No filter found with key ${key}`);

Check warning on line 101 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/components/Filters/common/FilteringModel.js#L99-L101

Added lines #L99 - L101 were not covered by tests
}

return this._filters[key].model;

Check warning on line 104 in lib/public/components/Filters/common/FilteringModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/components/Filters/common/FilteringModel.js#L104

Added line #L104 was not covered by tests
}
}
43 changes: 22 additions & 21 deletions lib/public/components/Filters/common/TagFilterModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@
* granted to it by virtue of its status as an Intergovernmental Organization
* or submit itself to any jurisdiction.
*/
import { Observable } from '/js/src/index.js';
import { CombinationOperatorChoiceModel } from './CombinationOperatorChoiceModel.js';
import { TagSelectionDropdownModel } from '../../tag/TagSelectionDropdownModel.js';
import { FilterModel } from './FilterModel.js';

/**
* Model to handle the state of a tags filter
*/
export class TagFilterModel extends Observable {
export class TagFilterModel extends FilterModel {
/**
* Constructor
*
Expand All @@ -28,28 +28,38 @@ export class TagFilterModel extends Observable {
super();
this._selectionModel = new TagSelectionDropdownModel({ includeArchived: true });
this._selectionModel.bubbleTo(this);
this._selectionModel.visualChange$.bubbleTo(this.visualChange$);

Check warning on line 31 in lib/public/components/Filters/common/TagFilterModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/components/Filters/common/TagFilterModel.js#L31

Added line #L31 was not covered by tests

this._combinationOperatorModel = new CombinationOperatorChoiceModel(operators);
this._combinationOperatorModel.bubbleTo(this);
}

// eslint-disable-next-line valid-jsdoc
/**
* States if the filter has no tags selected
*
* @return {boolean} true if no tags are selected
* @inheritDoc
*/
isEmpty() {
reset() {
this._selectionModel.reset();
this._combinationOperatorModel.reset();

Check warning on line 43 in lib/public/components/Filters/common/TagFilterModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/components/Filters/common/TagFilterModel.js#L41-L43

Added lines #L41 - L43 were not covered by tests
}

// eslint-disable-next-line valid-jsdoc
/**
* @inheritDoc
*/
get isEmpty() {

Check warning on line 50 in lib/public/components/Filters/common/TagFilterModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/components/Filters/common/TagFilterModel.js#L50

Added line #L50 was not covered by tests
return this.selected.length === 0;
}

// eslint-disable-next-line valid-jsdoc
/**
* Reset the model to its default state
*
* @return {void}
* @inheritDoc
*/
reset() {
this._selectionModel.reset();
this._combinationOperatorModel.reset();
get normalized() {
return {

Check warning on line 59 in lib/public/components/Filters/common/TagFilterModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/components/Filters/common/TagFilterModel.js#L58-L59

Added lines #L58 - L59 were not covered by tests
values: this.selected.join(),
operation: this.combinationOperator,
};
}

/**
Expand Down Expand Up @@ -87,13 +97,4 @@ export class TagFilterModel extends Observable {
get combinationOperator() {
return this._combinationOperatorModel.current;
}

/**
* Returns an observable notified any time a visual change occurs that has no impact on the actual selection
*
* @return {Observable} the visual change observable
*/
get visualChange$() {
return this._selectionModel.visualChange$;
}
}

This file was deleted.

5 changes: 5 additions & 0 deletions lib/public/views/Logs/ActiveColumns/logsActiveColumns.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ export const logsActiveColumns = {
sortable: true,
size: 'w-15',
format: (tags) => formatTagsList(tags),

/**

Check failure on line 138 in lib/public/views/Logs/ActiveColumns/logsActiveColumns.js

View workflow job for this annotation

GitHub Actions / linter

JSDoc description does not satisfy the regex pattern
* @param {LogsOverviewModel} logsModel the log model
* @return {Component} the filter component
*/
filter: (logsModel) => tagFilter(logsModel.listingTagsFilterModel),
balloon: true,
profiles: [profiles.none, 'embeded'],
Expand Down
2 changes: 1 addition & 1 deletion lib/public/views/Logs/Overview/LogsOverviewModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ export class LogsOverviewModel extends Observable {
'filter[created][to]':
new Date(`${this.createdFilterTo.replace(/\//g, '-')}T23:59:59.999`).getTime(),
},
...!this.listingTagsFilterModel.isEmpty() && {
...!this.listingTagsFilterModel.isEmpty && {

Check warning on line 398 in lib/public/views/Logs/Overview/LogsOverviewModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/views/Logs/Overview/LogsOverviewModel.js#L398

Added line #L398 was not covered by tests
'filter[tags][values]': this.listingTagsFilterModel.selected.join(),
'filter[tags][operation]': this.listingTagsFilterModel.combinationOperator,
},
Expand Down
7 changes: 6 additions & 1 deletion lib/public/views/Runs/ActiveColumns/runsActiveColumns.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,12 @@ export const runsActiveColumns = {
classes: 'w-5 f6',
format: (tags) => formatTagsList(tags),
exportFormat: (tags) => tags?.length ? tags.map(({ text }) => text).join('-') : '-',
filter: (runModel) => tagFilter(runModel.listingTagsFilterModel),

/**

Check failure on line 138 in lib/public/views/Runs/ActiveColumns/runsActiveColumns.js

View workflow job for this annotation

GitHub Actions / linter

JSDoc description does not satisfy the regex pattern
* @param {RunsOverviewModel} runModel the runs overview model
* @return {Component} the filter component
*/
filter: (runModel) => tagFilter(runModel.filteringModel.get('tags')),

Check warning on line 142 in lib/public/views/Runs/ActiveColumns/runsActiveColumns.js

View check run for this annotation

Codecov / codecov/patch

lib/public/views/Runs/ActiveColumns/runsActiveColumns.js#L142

Added line #L142 was not covered by tests
balloon: (tags) => tags && tags.length > 0,
},
fillNumber: {
Expand Down
53 changes: 28 additions & 25 deletions lib/public/views/Runs/Overview/RunsOverviewModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import { TimeRangeInputModel } from '../../../components/Filters/common/filters/
import { FloatComparisonFilterModel } from '../../../components/Filters/common/filters/FloatComparisonFilterModel.js';
import { detectorsProvider } from '../../../services/detectors/detectorsProvider.js';
import { AliceL3AndDipoleFilteringModel } from '../../../components/Filters/RunsFilter/AliceL3AndDipoleFilteringModel.js';
import { FilteringModel } from '../../../components/Filters/common/FilteringModel.js';
import { buildUrl } from '../../../utilities/fetch/buildUrl.js';

/**
* Model representing handlers for runs page
Expand All @@ -40,13 +42,16 @@ export class RunsOverviewModel extends OverviewPageModel {
constructor(model) {
super();

this._listingTagsFilterModel = new TagFilterModel([
CombinationOperator.AND,
CombinationOperator.OR,
CombinationOperator.NONE_OF,
]);
this._listingTagsFilterModel.observe(() => this._applyFilters(true));
this._listingTagsFilterModel.visualChange$.bubbleTo(this);
this._filteringModel = new FilteringModel({

Check warning on line 45 in lib/public/views/Runs/Overview/RunsOverviewModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/views/Runs/Overview/RunsOverviewModel.js#L45

Added line #L45 was not covered by tests
tags: {
model: new TagFilterModel([
CombinationOperator.AND,
CombinationOperator.OR,
CombinationOperator.NONE_OF,
]),
label: 'Tags',
},
});

this._detectorsFilterModel = new DetectorsFilterModel(detectorsProvider.dataTaking$);
this._detectorsFilterModel.observe(() => this._applyFilters(true));
Expand Down Expand Up @@ -92,6 +97,10 @@ export class RunsOverviewModel extends OverviewPageModel {
this._inelasticInteractionRateAtEndFilterModel.observe(() => this._applyFilters());
this._inelasticInteractionRateAtEndFilterModel.visualChange$.bubbleTo(this);

this._filteringModel.observe(() => this._applyFilters(true));
this._filteringModel.visualChange$.bubbleTo(this);

Check warning on line 101 in lib/public/views/Runs/Overview/RunsOverviewModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/views/Runs/Overview/RunsOverviewModel.js#L100-L101

Added lines #L100 - L101 were not covered by tests


Check failure on line 103 in lib/public/views/Runs/Overview/RunsOverviewModel.js

View workflow job for this annotation

GitHub Actions / linter

More than 1 blank line not allowed
// Export items
this._allRuns = RemoteData.NotAsked();

Expand All @@ -107,8 +116,7 @@ export class RunsOverviewModel extends OverviewPageModel {
* @inheritdoc
*/
getRootEndpoint() {
const paramsString = new URLSearchParams(this._getFilterQueryParams()).toString();
return `/api/runs?${paramsString}`;
return buildUrl('/api/runs', { ...this._getFilterQueryParams(), ...{ filter: this.filteringModel.normalized } });

Check warning on line 119 in lib/public/views/Runs/Overview/RunsOverviewModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/views/Runs/Overview/RunsOverviewModel.js#L119

Added line #L119 was not covered by tests
}

// eslint-disable-next-line valid-jsdoc
Expand Down Expand Up @@ -190,7 +198,6 @@ export class RunsOverviewModel extends OverviewPageModel {
this._runDefinitionFilter = [];

this._detectorsFilterModel.reset();
this._listingTagsFilterModel.reset();
this._listingRunTypesFilterModel.reset();
this._eorReasonsFilterModel.reset();
this._o2StartFilterModel.reset();
Expand Down Expand Up @@ -240,13 +247,13 @@ export class RunsOverviewModel extends OverviewPageModel {
* @return {Boolean} If any filter is active
*/
isAnyFilterActive() {
return this.runFilterValues !== ''
return this._filteringModel.isAnyFilterActive()
|| this.runFilterValues !== ''

Check warning on line 251 in lib/public/views/Runs/Overview/RunsOverviewModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/views/Runs/Overview/RunsOverviewModel.js#L250-L251

Added lines #L250 - L251 were not covered by tests
|| this._runDefinitionFilter.length > 0
|| !this._eorReasonsFilterModel.isEmpty()
|| !this._o2StartFilterModel.isEmpty
|| !this._o2StopFilterModel.isEmpty

Check warning on line 255 in lib/public/views/Runs/Overview/RunsOverviewModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/views/Runs/Overview/RunsOverviewModel.js#L254-L255

Added lines #L254 - L255 were not covered by tests
|| !this._detectorsFilterModel.isEmpty()
|| !this._listingTagsFilterModel.isEmpty()
|| this._listingRunTypesFilterModel.selected.length !== 0
|| this._aliceL3AndDipoleCurrentFilter.selected.length !== 0
|| this._fillNumbersFilter !== ''
Expand All @@ -269,6 +276,15 @@ export class RunsOverviewModel extends OverviewPageModel {
|| this._inelasticInteractionRateAtEndFilterModel.isEmpty;

Check warning on line 276 in lib/public/views/Runs/Overview/RunsOverviewModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/views/Runs/Overview/RunsOverviewModel.js#L276

Added line #L276 was not covered by tests
}

/**
* Return the filtering model
*
* @return {FilteringModel} the filtering model
*/
get filteringModel() {
return this._filteringModel;

Check warning on line 285 in lib/public/views/Runs/Overview/RunsOverviewModel.js

View check run for this annotation

Codecov / codecov/patch

lib/public/views/Runs/Overview/RunsOverviewModel.js#L284-L285

Added lines #L284 - L285 were not covered by tests
}

/**
* Set the export type parameter of the current export being created
* @param {string} selectedExportType Received string from the view
Expand Down Expand Up @@ -656,15 +672,6 @@ export class RunsOverviewModel extends OverviewPageModel {
this._applyFilters();
}

/**
* Return the model handling the filtering on tags
*
* @return {TagFilterModel} the filtering model
*/
get listingTagsFilterModel() {
return this._listingTagsFilterModel;
}

/**
* Returns the model handling the filtering on detectors
*
Expand Down Expand Up @@ -825,10 +832,6 @@ export class RunsOverviewModel extends OverviewPageModel {
...this._runDefinitionFilter.length > 0 && {
'filter[definitions]': this._runDefinitionFilter.join(','),
},
...!this._listingTagsFilterModel.isEmpty() && {
'filter[tags][values]': this._listingTagsFilterModel.selected.join(),
'filter[tags][operation]': this._listingTagsFilterModel.combinationOperator,
},
...this._fillNumbersFilter && {
'filter[fillNumbers]': this._fillNumbersFilter,
},
Expand Down

0 comments on commit 4fefe66

Please sign in to comment.