Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,9 @@ export const buildExpertRules = (
};
};

/** @deprecated The behavior of this local builder function differs from the backend one used by others (analysis results & spreadsheet).
* A new endpoint is now available to evaluate a global-filter on a network. To migrate to it.
*/
export const buildExpertFilter = (
equipmentType: EQUIPMENT_TYPES,
voltageLevelIds: string[] | undefined,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright © 2025, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import type { NonEmptyTuple } from 'type-fest';
import { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { useSnackMessage } from '@gridsuite/commons-ui';
import type { GlobalFilter, GlobalFilters } from './global-filter-types';
import { evaluateGlobalFilter } from '../../../../services/study/filter';
import type { AppState } from '../../../../redux/reducer';
import useGlobalFilters, { isGlobalFilterParameter } from './use-global-filters';
import type { FilterEquipmentType } from '../../../../types/filter-lib/filter';

/* Because of ESLint react-hooks/rules-of-hooks, nullable value must be managed inside the hook, because
* React hooks can't be called conditionally and/or different order. */
function useGlobalFiltersResults(
globalFilters: GlobalFilters | undefined,
equipmentTypes: NonEmptyTuple<FilterEquipmentType>
) {
const { snackError } = useSnackMessage();
const studyUuid = useSelector((state: AppState) => state.studyUuid);
const currentNodeUuid = useSelector((state: AppState) => state.currentTreeNode?.id);
const currentRootNetworkUuid = useSelector((state: AppState) => state.currentRootNetworkUuid);
const [filteredIds, setFilteredIds] = useState<string[]>();
useEffect(() => {
if (studyUuid && currentRootNetworkUuid && currentNodeUuid && isGlobalFilterParameter(globalFilters)) {
evaluateGlobalFilter(studyUuid, currentNodeUuid, currentRootNetworkUuid, equipmentTypes, globalFilters)
.then(setFilteredIds)
.catch((error) => {
console.error('Error while fetching GlobalFilter results', error);
snackError({ headerId: 'FilterEvaluationError', messageTxt: `${error}` });
});
}
}, [currentNodeUuid, currentRootNetworkUuid, equipmentTypes, globalFilters, snackError, studyUuid]);
return filteredIds;
}

export function useGlobalFilterResults(filters: GlobalFilter[], equipmentTypes: NonEmptyTuple<FilterEquipmentType>) {
const { globalFilters, handleGlobalFilterChange } = useGlobalFilters();
useEffect(() => handleGlobalFilterChange(filters), [filters, handleGlobalFilterChange]);
return useGlobalFiltersResults(globalFilters, equipmentTypes);
}
116 changes: 47 additions & 69 deletions src/components/results/common/global-filter/use-global-filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,88 +9,66 @@ import { useCallback, useState } from 'react';
import { GlobalFilter, GlobalFilters } from './global-filter-types';
import { FilterType } from '../utils';

interface UseGlobalFiltersParams {
onFilterChange?: (filters: GlobalFilters) => void;
export function isGlobalFilterParameter(globalFilters: GlobalFilters | undefined): globalFilters is GlobalFilters {
return (
globalFilters !== undefined &&
((globalFilters.countryCode && globalFilters.countryCode.length > 0) ||
(globalFilters.nominalV && globalFilters.nominalV.length > 0) ||
(globalFilters.genericFilter && globalFilters.genericFilter.length > 0) ||
!!globalFilters.substationProperty)
);
}

export default function useGlobalFilters({ onFilterChange }: UseGlobalFiltersParams) {
export default function useGlobalFilters() {
const [globalFilters, setGlobalFilters] = useState<GlobalFilters>();

const handleGlobalFilterChange = useCallback(
(value: GlobalFilter[]) => {
let newGlobalFilter: GlobalFilters = {};

const nominalVs = new Set(
value
.filter((filter: GlobalFilter) => filter.filterType === FilterType.VOLTAGE_LEVEL)
.map((filter: GlobalFilter) => filter.label)
);
// see <GlobalFilterSelector onChange={...} .../>
const handleGlobalFilterChange = useCallback((value: GlobalFilter[]) => {
let newGlobalFilter: GlobalFilters = {};

const genericFilters: Set<string> = new Set(
value
.filter((filter: GlobalFilter): boolean => filter.filterType === FilterType.GENERIC_FILTER)
.map((filter: GlobalFilter) => filter.uuid ?? '')
.filter((uuid: string): boolean => uuid !== '')
);
const nominalVs = new Set(
value
.filter((filter: GlobalFilter) => filter.filterType === FilterType.VOLTAGE_LEVEL)
.map((filter: GlobalFilter) => filter.label)
);

const countryCodes = new Set(
value
.filter((filter: GlobalFilter) => filter.filterType === FilterType.COUNTRY)
.map((filter: GlobalFilter) => filter.label)
);
const genericFilters: Set<string> = new Set(
value
.filter((filter: GlobalFilter): boolean => filter.filterType === FilterType.GENERIC_FILTER)
.map((filter: GlobalFilter) => filter.uuid ?? '')
.filter((uuid: string): boolean => uuid !== '')
);

const substationProperties: Map<string, string[]> = new Map();
const countryCodes = new Set(
value
.filter((filter: GlobalFilter) => filter.filterType === FilterType.SUBSTATION_PROPERTY)
.forEach((filter: GlobalFilter) => {
if (filter.filterSubtype) {
const subtypeSubstationProperties = substationProperties.get(filter.filterSubtype);
if (subtypeSubstationProperties) {
subtypeSubstationProperties.push(filter.label);
} else {
substationProperties.set(filter.filterSubtype, [filter.label]);
}
.filter((filter: GlobalFilter) => filter.filterType === FilterType.COUNTRY)
.map((filter: GlobalFilter) => filter.label)
);

const substationProperties: Map<string, string[]> = new Map();
value
.filter((filter: GlobalFilter) => filter.filterType === FilterType.SUBSTATION_PROPERTY)
.forEach((filter: GlobalFilter) => {
if (filter.filterSubtype) {
const subtypeSubstationProperties = substationProperties.get(filter.filterSubtype);
if (subtypeSubstationProperties) {
subtypeSubstationProperties.push(filter.label);
} else {
substationProperties.set(filter.filterSubtype, [filter.label]);
}
});

newGlobalFilter.nominalV = [...nominalVs];
newGlobalFilter.countryCode = [...countryCodes];
newGlobalFilter.genericFilter = [...genericFilters];
}
});

if (substationProperties.size > 0) {
newGlobalFilter.substationProperty = Object.fromEntries(substationProperties);
}

setGlobalFilters(newGlobalFilter);
onFilterChange?.(newGlobalFilter);
},
[onFilterChange]
);

const getGlobalFilterParameter = useCallback((globalFilters: GlobalFilters | undefined) => {
let shouldSentParameter = false;

if (globalFilters) {
if (
(globalFilters.countryCode && globalFilters.countryCode.length > 0) ||
(globalFilters.nominalV && globalFilters.nominalV.length > 0) ||
(globalFilters.genericFilter && globalFilters.genericFilter.length > 0) ||
globalFilters.substationProperty
) {
shouldSentParameter = true;
}
}
newGlobalFilter.nominalV = [...nominalVs];
newGlobalFilter.countryCode = [...countryCodes];
newGlobalFilter.genericFilter = [...genericFilters];

if (!shouldSentParameter) {
return undefined;
if (substationProperties.size > 0) {
newGlobalFilter.substationProperty = Object.fromEntries(substationProperties);
}

return globalFilters;
setGlobalFilters(newGlobalFilter);
}, []);

return {
globalFilters,
handleGlobalFilterChange,
getGlobalFilterParameter,
};
return { globalFilters, handleGlobalFilterChange };
}
24 changes: 14 additions & 10 deletions src/components/results/loadflow/load-flow-result-tab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import {
import { EQUIPMENT_TYPES } from '../../utils/equipment-types';
import type { UUID } from 'node:crypto';
import GlobalFilterSelector from '../common/global-filter/global-filter-selector';
import useGlobalFilters from '../common/global-filter/use-global-filters';
import useGlobalFilters, { isGlobalFilterParameter } from '../common/global-filter/use-global-filters';
import { useGlobalFilterOptions } from '../common/global-filter/use-global-filter-options';
import { ICellRendererParams } from 'ag-grid-community';
import { Button, Tooltip } from '@mui/material';
Expand Down Expand Up @@ -88,7 +88,7 @@ export const LoadFlowResultTab: FunctionComponent<LoadFlowTabProps> = ({
const { filters } = useFilterSelector(AgGridFilterType.Loadflow, mappingTabs(tabIndex));

const { countriesFilter, voltageLevelsFilter, propertiesFilter } = useGlobalFilterOptions();
const { globalFilters, handleGlobalFilterChange, getGlobalFilterParameter } = useGlobalFilters({});
const { globalFilters, handleGlobalFilterChange } = useGlobalFilters();
const { onLinkClick } = useLoadFlowResultColumnActions({
studyUuid,
nodeUuid,
Expand Down Expand Up @@ -129,16 +129,20 @@ export const LoadFlowResultTab: FunctionComponent<LoadFlowTabProps> = ({
colId: FROM_COLUMN_TO_FIELD_LIMIT_VIOLATION_RESULT[sort.colId],
})),
filters: mapFieldsToColumnsFilter(updatedFilters, mappingFields(tabIndex)),
...(getGlobalFilterParameter(globalFilters) !== undefined && {
globalFilters: {
...getGlobalFilterParameter(globalFilters),
limitViolationsTypes:
tabIndex === 0 ? [LimitTypes.CURRENT] : [LimitTypes.HIGH_VOLTAGE, LimitTypes.LOW_VOLTAGE],
},
}),
...(isGlobalFilterParameter(globalFilters)
? {
globalFilters: {
...globalFilters,
limitViolationsTypes:
tabIndex === 0
? [LimitTypes.CURRENT]
: [LimitTypes.HIGH_VOLTAGE, LimitTypes.LOW_VOLTAGE],
},
}
: {}),
});
},
[tabIndex, filters, intl, sortConfig, getGlobalFilterParameter, globalFilters]
[tabIndex, filters, intl, sortConfig, globalFilters]
);

const fetchloadflowResultWithParameters = useMemo(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import { securityAnalysisResultInvalidations } from '../../computing-status/use-
import { useParameterState } from 'components/dialogs/parameters/use-parameters-state';
import { useNodeData } from 'components/use-node-data';
import GlobalFilterSelector from '../common/global-filter/global-filter-selector';
import useGlobalFilters from '../common/global-filter/use-global-filters';
import useGlobalFilters, { isGlobalFilterParameter } from '../common/global-filter/use-global-filters';
import { useGlobalFilterOptions } from '../common/global-filter/use-global-filter-options';
import { EQUIPMENT_TYPES } from '../../utils/equipment-types';
import { usePaginationSelector } from 'hooks/use-pagination-selector';
Expand Down Expand Up @@ -124,7 +124,7 @@ export const SecurityAnalysisResultTab: FunctionComponent<SecurityAnalysisTabPro
getStoreFields(tabIndex) as SecurityAnalysisTab
);
const { page, rowsPerPage } = pagination;
const { globalFilters, handleGlobalFilterChange, getGlobalFilterParameter } = useGlobalFilters({});
const { globalFilters, handleGlobalFilterChange } = useGlobalFilters();
const { countriesFilter, voltageLevelsFilter, propertiesFilter } = useGlobalFilterOptions();

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

if (globalFilters !== undefined && getGlobalFilterParameter(globalFilters) !== undefined) {
if (isGlobalFilterParameter(globalFilters)) {
queryParams['globalFilters'] = globalFilters;
}

return fetchSecurityAnalysisResult(studyUuid, nodeUuid, currentRootNetworkUuid, queryParams);
},

[
tabIndex,
resultType,
sortConfig,
filters,
getGlobalFilterParameter,
globalFilters,
currentRootNetworkUuid,
page,
rowsPerPage,
intl,
]
[tabIndex, resultType, sortConfig, filters, globalFilters, currentRootNetworkUuid, page, rowsPerPage, intl]
);

const {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
SENSITIVITY_AT_NODE,
SENSITIVITY_IN_DELTA_MW,
} from './sensitivity-analysis-result.type';
import useGlobalFilters from '../common/global-filter/use-global-filters';
import useGlobalFilters, { isGlobalFilterParameter } from '../common/global-filter/use-global-filters';
import GlobalFilterSelector from '../common/global-filter/global-filter-selector';
import { EQUIPMENT_TYPES } from '../../utils/equipment-types';
import { useGlobalFilterOptions } from '../common/global-filter/use-global-filter-options';
Expand All @@ -47,7 +47,7 @@ function SensitivityAnalysisResultTab({
(state: AppState) => state.computingStatus[ComputingType.SENSITIVITY_ANALYSIS]
);

const { globalFilters, handleGlobalFilterChange, getGlobalFilterParameter } = useGlobalFilters({});
const { globalFilters, handleGlobalFilterChange } = useGlobalFilters();
const { countriesFilter, voltageLevelsFilter, propertiesFilter } = useGlobalFilterOptions();

const handleSensiNOrNkIndexChange = (event: SyntheticEvent, newNOrNKIndex: number) => {
Expand Down Expand Up @@ -104,7 +104,7 @@ function SensitivityAnalysisResultTab({
csvHeaders={csvHeaders}
nOrNkIndex={nOrNkIndex}
sensiKind={sensiTab}
globalFilters={getGlobalFilterParameter(globalFilters)}
globalFilters={isGlobalFilterParameter(globalFilters) ? globalFilters : undefined}
disabled={isCsvButtonDisabled}
/>
</Box>
Expand All @@ -116,7 +116,7 @@ function SensitivityAnalysisResultTab({
currentRootNetworkUuid={currentRootNetworkUuid}
setCsvHeaders={setCsvHeaders}
setIsCsvButtonDisabled={setIsCsvButtonDisabled}
globalFilters={getGlobalFilterParameter(globalFilters)}
globalFilters={isGlobalFilterParameter(globalFilters) ? globalFilters : undefined}
/>
</>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import type { UUID } from 'node:crypto';
import { ColDef, GridReadyEvent, RowDataUpdatedEvent } from 'ag-grid-community';
import GlobalFilterSelector from '../common/global-filter/global-filter-selector';
import { EQUIPMENT_TYPES } from '../../utils/equipment-types';
import useGlobalFilters from '../common/global-filter/use-global-filters';
import useGlobalFilters, { isGlobalFilterParameter } from '../common/global-filter/use-global-filters';
import { useGlobalFilterOptions } from '../common/global-filter/use-global-filter-options';

interface ShortCircuitAnalysisResultTabProps {
Expand Down Expand Up @@ -93,7 +93,7 @@ export const ShortCircuitAnalysisResultTab: FunctionComponent<ShortCircuitAnalys
const RESULTS_TAB_INDEX = 0;
const LOGS_TAB_INDEX = 1;

const { globalFilters, handleGlobalFilterChange, getGlobalFilterParameter } = useGlobalFilters({});
const { globalFilters, handleGlobalFilterChange } = useGlobalFilters();
const { countriesFilter, voltageLevelsFilter, propertiesFilter } = useGlobalFilterOptions();

const handleSubTabChange = useCallback(
Expand Down Expand Up @@ -195,7 +195,7 @@ export const ShortCircuitAnalysisResultTab: FunctionComponent<ShortCircuitAnalys
<ShortCircuitAnalysisAllBusesResult
onGridColumnsChanged={handleGridColumnsChanged}
onRowDataUpdated={handleRowDataUpdated}
globalFilters={getGlobalFilterParameter(globalFilters)}
globalFilters={isGlobalFilterParameter(globalFilters) ? globalFilters : undefined}
openVoltageLevelDiagram={openVoltageLevelDiagram}
/>
) : (
Expand Down
Loading
Loading