Skip to content

Commit 6ae1475

Browse files
Fix spreadsheet global filter (#3336)
fix-spreadsheet-global-filter Signed-off-by: Franck LECUYER <[email protected]>
1 parent f1dbb9b commit 6ae1475

File tree

12 files changed

+223
-256
lines changed

12 files changed

+223
-256
lines changed

src/components/dialogs/parameters/dynamicsimulation/curve/dialog/curve-selector-utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,9 @@ export const buildExpertRules = (
313313
};
314314
};
315315

316+
/** @deprecated The behavior of this local builder function differs from the backend one used by others (analysis results & spreadsheet).
317+
* A new endpoint is now available to evaluate a global-filter on a network. To migrate to it.
318+
*/
316319
export const buildExpertFilter = (
317320
equipmentType: EQUIPMENT_TYPES,
318321
voltageLevelIds: string[] | undefined,
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright © 2025, RTE (http://www.rte-france.com)
3+
* This Source Code Form is subject to the terms of the Mozilla Public
4+
* License, v. 2.0. If a copy of the MPL was not distributed with this
5+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6+
*/
7+
8+
import type { NonEmptyTuple } from 'type-fest';
9+
import { useEffect, useState } from 'react';
10+
import { useSelector } from 'react-redux';
11+
import { useSnackMessage } from '@gridsuite/commons-ui';
12+
import type { GlobalFilter, GlobalFilters } from './global-filter-types';
13+
import { evaluateGlobalFilter } from '../../../../services/study/filter';
14+
import type { AppState } from '../../../../redux/reducer';
15+
import useGlobalFilters, { isGlobalFilterParameter } from './use-global-filters';
16+
import type { FilterEquipmentType } from '../../../../types/filter-lib/filter';
17+
18+
/* Because of ESLint react-hooks/rules-of-hooks, nullable value must be managed inside the hook, because
19+
* React hooks can't be called conditionally and/or different order. */
20+
function useGlobalFiltersResults(
21+
globalFilters: GlobalFilters | undefined,
22+
equipmentTypes: NonEmptyTuple<FilterEquipmentType>
23+
) {
24+
const { snackError } = useSnackMessage();
25+
const studyUuid = useSelector((state: AppState) => state.studyUuid);
26+
const currentNodeUuid = useSelector((state: AppState) => state.currentTreeNode?.id);
27+
const currentRootNetworkUuid = useSelector((state: AppState) => state.currentRootNetworkUuid);
28+
const [filteredIds, setFilteredIds] = useState<string[]>();
29+
useEffect(() => {
30+
if (studyUuid && currentRootNetworkUuid && currentNodeUuid && isGlobalFilterParameter(globalFilters)) {
31+
evaluateGlobalFilter(studyUuid, currentNodeUuid, currentRootNetworkUuid, equipmentTypes, globalFilters)
32+
.then(setFilteredIds)
33+
.catch((error) => {
34+
console.error('Error while fetching GlobalFilter results', error);
35+
snackError({ headerId: 'FilterEvaluationError', messageTxt: `${error}` });
36+
});
37+
}
38+
}, [currentNodeUuid, currentRootNetworkUuid, equipmentTypes, globalFilters, snackError, studyUuid]);
39+
return filteredIds;
40+
}
41+
42+
export function useGlobalFilterResults(filters: GlobalFilter[], equipmentTypes: NonEmptyTuple<FilterEquipmentType>) {
43+
const { globalFilters, handleGlobalFilterChange } = useGlobalFilters();
44+
useEffect(() => handleGlobalFilterChange(filters), [filters, handleGlobalFilterChange]);
45+
return useGlobalFiltersResults(globalFilters, equipmentTypes);
46+
}

src/components/results/common/global-filter/use-global-filters.ts

Lines changed: 47 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -9,88 +9,66 @@ import { useCallback, useState } from 'react';
99
import { GlobalFilter, GlobalFilters } from './global-filter-types';
1010
import { FilterType } from '../utils';
1111

12-
interface UseGlobalFiltersParams {
13-
onFilterChange?: (filters: GlobalFilters) => void;
12+
export function isGlobalFilterParameter(globalFilters: GlobalFilters | undefined): globalFilters is GlobalFilters {
13+
return (
14+
globalFilters !== undefined &&
15+
((globalFilters.countryCode && globalFilters.countryCode.length > 0) ||
16+
(globalFilters.nominalV && globalFilters.nominalV.length > 0) ||
17+
(globalFilters.genericFilter && globalFilters.genericFilter.length > 0) ||
18+
!!globalFilters.substationProperty)
19+
);
1420
}
1521

16-
export default function useGlobalFilters({ onFilterChange }: UseGlobalFiltersParams) {
22+
export default function useGlobalFilters() {
1723
const [globalFilters, setGlobalFilters] = useState<GlobalFilters>();
1824

19-
const handleGlobalFilterChange = useCallback(
20-
(value: GlobalFilter[]) => {
21-
let newGlobalFilter: GlobalFilters = {};
22-
23-
const nominalVs = new Set(
24-
value
25-
.filter((filter: GlobalFilter) => filter.filterType === FilterType.VOLTAGE_LEVEL)
26-
.map((filter: GlobalFilter) => filter.label)
27-
);
25+
// see <GlobalFilterSelector onChange={...} .../>
26+
const handleGlobalFilterChange = useCallback((value: GlobalFilter[]) => {
27+
let newGlobalFilter: GlobalFilters = {};
2828

29-
const genericFilters: Set<string> = new Set(
30-
value
31-
.filter((filter: GlobalFilter): boolean => filter.filterType === FilterType.GENERIC_FILTER)
32-
.map((filter: GlobalFilter) => filter.uuid ?? '')
33-
.filter((uuid: string): boolean => uuid !== '')
34-
);
29+
const nominalVs = new Set(
30+
value
31+
.filter((filter: GlobalFilter) => filter.filterType === FilterType.VOLTAGE_LEVEL)
32+
.map((filter: GlobalFilter) => filter.label)
33+
);
3534

36-
const countryCodes = new Set(
37-
value
38-
.filter((filter: GlobalFilter) => filter.filterType === FilterType.COUNTRY)
39-
.map((filter: GlobalFilter) => filter.label)
40-
);
35+
const genericFilters: Set<string> = new Set(
36+
value
37+
.filter((filter: GlobalFilter): boolean => filter.filterType === FilterType.GENERIC_FILTER)
38+
.map((filter: GlobalFilter) => filter.uuid ?? '')
39+
.filter((uuid: string): boolean => uuid !== '')
40+
);
4141

42-
const substationProperties: Map<string, string[]> = new Map();
42+
const countryCodes = new Set(
4343
value
44-
.filter((filter: GlobalFilter) => filter.filterType === FilterType.SUBSTATION_PROPERTY)
45-
.forEach((filter: GlobalFilter) => {
46-
if (filter.filterSubtype) {
47-
const subtypeSubstationProperties = substationProperties.get(filter.filterSubtype);
48-
if (subtypeSubstationProperties) {
49-
subtypeSubstationProperties.push(filter.label);
50-
} else {
51-
substationProperties.set(filter.filterSubtype, [filter.label]);
52-
}
44+
.filter((filter: GlobalFilter) => filter.filterType === FilterType.COUNTRY)
45+
.map((filter: GlobalFilter) => filter.label)
46+
);
47+
48+
const substationProperties: Map<string, string[]> = new Map();
49+
value
50+
.filter((filter: GlobalFilter) => filter.filterType === FilterType.SUBSTATION_PROPERTY)
51+
.forEach((filter: GlobalFilter) => {
52+
if (filter.filterSubtype) {
53+
const subtypeSubstationProperties = substationProperties.get(filter.filterSubtype);
54+
if (subtypeSubstationProperties) {
55+
subtypeSubstationProperties.push(filter.label);
56+
} else {
57+
substationProperties.set(filter.filterSubtype, [filter.label]);
5358
}
54-
});
55-
56-
newGlobalFilter.nominalV = [...nominalVs];
57-
newGlobalFilter.countryCode = [...countryCodes];
58-
newGlobalFilter.genericFilter = [...genericFilters];
59+
}
60+
});
5961

60-
if (substationProperties.size > 0) {
61-
newGlobalFilter.substationProperty = Object.fromEntries(substationProperties);
62-
}
63-
64-
setGlobalFilters(newGlobalFilter);
65-
onFilterChange?.(newGlobalFilter);
66-
},
67-
[onFilterChange]
68-
);
69-
70-
const getGlobalFilterParameter = useCallback((globalFilters: GlobalFilters | undefined) => {
71-
let shouldSentParameter = false;
72-
73-
if (globalFilters) {
74-
if (
75-
(globalFilters.countryCode && globalFilters.countryCode.length > 0) ||
76-
(globalFilters.nominalV && globalFilters.nominalV.length > 0) ||
77-
(globalFilters.genericFilter && globalFilters.genericFilter.length > 0) ||
78-
globalFilters.substationProperty
79-
) {
80-
shouldSentParameter = true;
81-
}
82-
}
62+
newGlobalFilter.nominalV = [...nominalVs];
63+
newGlobalFilter.countryCode = [...countryCodes];
64+
newGlobalFilter.genericFilter = [...genericFilters];
8365

84-
if (!shouldSentParameter) {
85-
return undefined;
66+
if (substationProperties.size > 0) {
67+
newGlobalFilter.substationProperty = Object.fromEntries(substationProperties);
8668
}
8769

88-
return globalFilters;
70+
setGlobalFilters(newGlobalFilter);
8971
}, []);
9072

91-
return {
92-
globalFilters,
93-
handleGlobalFilterChange,
94-
getGlobalFilterParameter,
95-
};
73+
return { globalFilters, handleGlobalFilterChange };
9674
}

src/components/results/loadflow/load-flow-result-tab.tsx

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ import {
4545
import { EQUIPMENT_TYPES } from '../../utils/equipment-types';
4646
import type { UUID } from 'node:crypto';
4747
import GlobalFilterSelector from '../common/global-filter/global-filter-selector';
48-
import useGlobalFilters from '../common/global-filter/use-global-filters';
48+
import useGlobalFilters, { isGlobalFilterParameter } from '../common/global-filter/use-global-filters';
4949
import { useGlobalFilterOptions } from '../common/global-filter/use-global-filter-options';
5050
import { ICellRendererParams } from 'ag-grid-community';
5151
import { Button, Tooltip } from '@mui/material';
@@ -88,7 +88,7 @@ export const LoadFlowResultTab: FunctionComponent<LoadFlowTabProps> = ({
8888
const { filters } = useFilterSelector(AgGridFilterType.Loadflow, mappingTabs(tabIndex));
8989

9090
const { countriesFilter, voltageLevelsFilter, propertiesFilter } = useGlobalFilterOptions();
91-
const { globalFilters, handleGlobalFilterChange, getGlobalFilterParameter } = useGlobalFilters({});
91+
const { globalFilters, handleGlobalFilterChange } = useGlobalFilters();
9292
const { onLinkClick } = useLoadFlowResultColumnActions({
9393
studyUuid,
9494
nodeUuid,
@@ -129,16 +129,20 @@ export const LoadFlowResultTab: FunctionComponent<LoadFlowTabProps> = ({
129129
colId: FROM_COLUMN_TO_FIELD_LIMIT_VIOLATION_RESULT[sort.colId],
130130
})),
131131
filters: mapFieldsToColumnsFilter(updatedFilters, mappingFields(tabIndex)),
132-
...(getGlobalFilterParameter(globalFilters) !== undefined && {
133-
globalFilters: {
134-
...getGlobalFilterParameter(globalFilters),
135-
limitViolationsTypes:
136-
tabIndex === 0 ? [LimitTypes.CURRENT] : [LimitTypes.HIGH_VOLTAGE, LimitTypes.LOW_VOLTAGE],
137-
},
138-
}),
132+
...(isGlobalFilterParameter(globalFilters)
133+
? {
134+
globalFilters: {
135+
...globalFilters,
136+
limitViolationsTypes:
137+
tabIndex === 0
138+
? [LimitTypes.CURRENT]
139+
: [LimitTypes.HIGH_VOLTAGE, LimitTypes.LOW_VOLTAGE],
140+
},
141+
}
142+
: {}),
139143
});
140144
},
141-
[tabIndex, filters, intl, sortConfig, getGlobalFilterParameter, globalFilters]
145+
[tabIndex, filters, intl, sortConfig, globalFilters]
142146
);
143147

144148
const fetchloadflowResultWithParameters = useMemo(() => {

src/components/results/securityanalysis/security-analysis-result-tab.tsx

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ import { securityAnalysisResultInvalidations } from '../../computing-status/use-
4444
import { useParameterState } from 'components/dialogs/parameters/use-parameters-state';
4545
import { useNodeData } from 'components/use-node-data';
4646
import GlobalFilterSelector from '../common/global-filter/global-filter-selector';
47-
import useGlobalFilters from '../common/global-filter/use-global-filters';
47+
import useGlobalFilters, { isGlobalFilterParameter } from '../common/global-filter/use-global-filters';
4848
import { useGlobalFilterOptions } from '../common/global-filter/use-global-filter-options';
4949
import { EQUIPMENT_TYPES } from '../../utils/equipment-types';
5050
import { usePaginationSelector } from 'hooks/use-pagination-selector';
@@ -124,7 +124,7 @@ export const SecurityAnalysisResultTab: FunctionComponent<SecurityAnalysisTabPro
124124
getStoreFields(tabIndex) as SecurityAnalysisTab
125125
);
126126
const { page, rowsPerPage } = pagination;
127-
const { globalFilters, handleGlobalFilterChange, getGlobalFilterParameter } = useGlobalFilters({});
127+
const { globalFilters, handleGlobalFilterChange } = useGlobalFilters();
128128
const { countriesFilter, voltageLevelsFilter, propertiesFilter } = useGlobalFilterOptions();
129129

130130
const globalFilterOptions = useMemo(
@@ -165,25 +165,14 @@ export const SecurityAnalysisResultTab: FunctionComponent<SecurityAnalysisTabPro
165165
queryParams['filters'] = mapFieldsToColumnsFilter(updatedFilters, columnToFieldMapping);
166166
}
167167

168-
if (globalFilters !== undefined && getGlobalFilterParameter(globalFilters) !== undefined) {
168+
if (isGlobalFilterParameter(globalFilters)) {
169169
queryParams['globalFilters'] = globalFilters;
170170
}
171171

172172
return fetchSecurityAnalysisResult(studyUuid, nodeUuid, currentRootNetworkUuid, queryParams);
173173
},
174174

175-
[
176-
tabIndex,
177-
resultType,
178-
sortConfig,
179-
filters,
180-
getGlobalFilterParameter,
181-
globalFilters,
182-
currentRootNetworkUuid,
183-
page,
184-
rowsPerPage,
185-
intl,
186-
]
175+
[tabIndex, resultType, sortConfig, filters, globalFilters, currentRootNetworkUuid, page, rowsPerPage, intl]
187176
);
188177

189178
const {

src/components/results/sensitivity-analysis/sensitivity-analysis-result-tab.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {
2323
SENSITIVITY_AT_NODE,
2424
SENSITIVITY_IN_DELTA_MW,
2525
} from './sensitivity-analysis-result.type';
26-
import useGlobalFilters from '../common/global-filter/use-global-filters';
26+
import useGlobalFilters, { isGlobalFilterParameter } from '../common/global-filter/use-global-filters';
2727
import GlobalFilterSelector from '../common/global-filter/global-filter-selector';
2828
import { EQUIPMENT_TYPES } from '../../utils/equipment-types';
2929
import { useGlobalFilterOptions } from '../common/global-filter/use-global-filter-options';
@@ -47,7 +47,7 @@ function SensitivityAnalysisResultTab({
4747
(state: AppState) => state.computingStatus[ComputingType.SENSITIVITY_ANALYSIS]
4848
);
4949

50-
const { globalFilters, handleGlobalFilterChange, getGlobalFilterParameter } = useGlobalFilters({});
50+
const { globalFilters, handleGlobalFilterChange } = useGlobalFilters();
5151
const { countriesFilter, voltageLevelsFilter, propertiesFilter } = useGlobalFilterOptions();
5252

5353
const handleSensiNOrNkIndexChange = (event: SyntheticEvent, newNOrNKIndex: number) => {
@@ -104,7 +104,7 @@ function SensitivityAnalysisResultTab({
104104
csvHeaders={csvHeaders}
105105
nOrNkIndex={nOrNkIndex}
106106
sensiKind={sensiTab}
107-
globalFilters={getGlobalFilterParameter(globalFilters)}
107+
globalFilters={isGlobalFilterParameter(globalFilters) ? globalFilters : undefined}
108108
disabled={isCsvButtonDisabled}
109109
/>
110110
</Box>
@@ -116,7 +116,7 @@ function SensitivityAnalysisResultTab({
116116
currentRootNetworkUuid={currentRootNetworkUuid}
117117
setCsvHeaders={setCsvHeaders}
118118
setIsCsvButtonDisabled={setIsCsvButtonDisabled}
119-
globalFilters={getGlobalFilterParameter(globalFilters)}
119+
globalFilters={isGlobalFilterParameter(globalFilters) ? globalFilters : undefined}
120120
/>
121121
</>
122122
)}

src/components/results/shortcircuit/shortcircuit-analysis-result-tab.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import type { UUID } from 'node:crypto';
2929
import { ColDef, GridReadyEvent, RowDataUpdatedEvent } from 'ag-grid-community';
3030
import GlobalFilterSelector from '../common/global-filter/global-filter-selector';
3131
import { EQUIPMENT_TYPES } from '../../utils/equipment-types';
32-
import useGlobalFilters from '../common/global-filter/use-global-filters';
32+
import useGlobalFilters, { isGlobalFilterParameter } from '../common/global-filter/use-global-filters';
3333
import { useGlobalFilterOptions } from '../common/global-filter/use-global-filter-options';
3434

3535
interface ShortCircuitAnalysisResultTabProps {
@@ -93,7 +93,7 @@ export const ShortCircuitAnalysisResultTab: FunctionComponent<ShortCircuitAnalys
9393
const RESULTS_TAB_INDEX = 0;
9494
const LOGS_TAB_INDEX = 1;
9595

96-
const { globalFilters, handleGlobalFilterChange, getGlobalFilterParameter } = useGlobalFilters({});
96+
const { globalFilters, handleGlobalFilterChange } = useGlobalFilters();
9797
const { countriesFilter, voltageLevelsFilter, propertiesFilter } = useGlobalFilterOptions();
9898

9999
const handleSubTabChange = useCallback(
@@ -195,7 +195,7 @@ export const ShortCircuitAnalysisResultTab: FunctionComponent<ShortCircuitAnalys
195195
<ShortCircuitAnalysisAllBusesResult
196196
onGridColumnsChanged={handleGridColumnsChanged}
197197
onRowDataUpdated={handleRowDataUpdated}
198-
globalFilters={getGlobalFilterParameter(globalFilters)}
198+
globalFilters={isGlobalFilterParameter(globalFilters) ? globalFilters : undefined}
199199
openVoltageLevelDiagram={openVoltageLevelDiagram}
200200
/>
201201
) : (

0 commit comments

Comments
 (0)