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
91 changes: 91 additions & 0 deletions src/services/study/sensitivity-analysis-utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/**
* Copyright (c) 2026, 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 { extractUuidsFromVariationId, resolveForVariationId } from './sensitivity-analysis-utils';

describe('extractUuidsFromVarId', () => {
it('extracts a single UUID from a standard varId', () => {
const varId = '[1e34a601-fb40-4df1-8dc6-09aac5ef717e] (REGULAR)';
expect(extractUuidsFromVariationId(varId)).toEqual(['1e34a601-fb40-4df1-8dc6-09aac5ef717e']);
});

it('extracts multiple comma-separated UUIDs', () => {
const varId = '[1e34a601-fb40-4df1-8dc6-09aac5ef717e, 2b45c702-ac51-4e20-9ed7-10bbd6af828f] (REGULAR)';
expect(extractUuidsFromVariationId(varId)).toEqual([
'1e34a601-fb40-4df1-8dc6-09aac5ef717e',
'2b45c702-ac51-4e20-9ed7-10bbd6af828f',
]);
});

it('returns an empty array when no UUIDs are present', () => {
expect(extractUuidsFromVariationId('SomePlainText (REGULAR)')).toEqual([]);
});

it('returns an empty array for an empty string', () => {
expect(extractUuidsFromVariationId('')).toEqual([]);
});

it('extracts UUIDs regardless of surrounding text', () => {
const varId = 'prefix-1e34a601-fb40-4df1-8dc6-09aac5ef717e-suffix';
expect(extractUuidsFromVariationId(varId)).toEqual(['1e34a601-fb40-4df1-8dc6-09aac5ef717e']);
});

it('handles three UUIDs', () => {
const varId =
'[aaaaaaaa-0000-0000-0000-000000000001, aaaaaaaa-0000-0000-0000-000000000002, aaaaaaaa-0000-0000-0000-000000000003] (CONTINGENCY)';
expect(extractUuidsFromVariationId(varId)).toEqual([
'aaaaaaaa-0000-0000-0000-000000000001',
'aaaaaaaa-0000-0000-0000-000000000002',
'aaaaaaaa-0000-0000-0000-000000000003',
]);
});
});

describe('resolveVarId', () => {
it('replaces a single UUID with its name', () => {
const varId = '[1e34a601-fb40-4df1-8dc6-09aac5ef717e] (REGULAR)';
const map = new Map([['1e34a601-fb40-4df1-8dc6-09aac5ef717e', 'FilterA']]);
expect(resolveForVariationId(varId, map)).toBe('[FilterA] (REGULAR)');
});

it('replaces multiple comma-separated UUIDs with their names', () => {
const varId = '[1e34a601-fb40-4df1-8dc6-09aac5ef717e, 2b45c702-ac51-4e20-9ed7-10bbd6af828f] (REGULAR)';
const map = new Map([
['1e34a601-fb40-4df1-8dc6-09aac5ef717e', 'FilterA'],
['2b45c702-ac51-4e20-9ed7-10bbd6af828f', 'FilterB'],
]);
expect(resolveForVariationId(varId, map)).toBe('[FilterA, FilterB] (REGULAR)');
});

it('leaves unknown UUIDs as-is when not found in the map', () => {
const varId = '[1e34a601-fb40-4df1-8dc6-09aac5ef717e] (REGULAR)';
const map = new Map<string, string>();
expect(resolveForVariationId(varId, map)).toBe('[1e34a601-fb40-4df1-8dc6-09aac5ef717e] (REGULAR)');
});

it('replaces only known UUIDs and leaves unknown ones as-is', () => {
const varId = '[1e34a601-fb40-4df1-8dc6-09aac5ef717e, 2b45c702-ac51-4e20-9ed7-10bbd6af828f] (REGULAR)';
const map = new Map([['1e34a601-fb40-4df1-8dc6-09aac5ef717e', 'FilterA']]);
expect(resolveForVariationId(varId, map)).toBe('[FilterA, 2b45c702-ac51-4e20-9ed7-10bbd6af828f] (REGULAR)');
});

it('returns the original string unchanged when no UUIDs are present', () => {
const varId = 'SomePlainText (REGULAR)';
const map = new Map([['1e34a601-fb40-4df1-8dc6-09aac5ef717e', 'FilterA']]);
expect(resolveForVariationId(varId, map)).toBe('SomePlainText (REGULAR)');
});

it('handles an empty string', () => {
expect(resolveForVariationId('', new Map())).toBe('');
});

it('preserves the suffix type (CONTINGENCY)', () => {
const varId = '[1e34a601-fb40-4df1-8dc6-09aac5ef717e] (CONTINGENCY)';
const map = new Map([['1e34a601-fb40-4df1-8dc6-09aac5ef717e', 'MyFilter']]);
expect(resolveForVariationId(varId, map)).toBe('[MyFilter] (CONTINGENCY)');
});
});
23 changes: 23 additions & 0 deletions src/services/study/sensitivity-analysis-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* Copyright (c) 2026, 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/.
*/
const UUID_REGEX = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi;

/**
* Extracts all unique UUIDs found inside a varId string in SensitivityOfTo like
* "[uuid1, uuid2] (REGULAR)" — finds all UUIDs inside brackets.
*/
export function extractUuidsFromVariationId(variationId: string): string[] {
return [...variationId.matchAll(UUID_REGEX)].map((match) => match[0]);
}

/**
* Replaces all UUIDs in a varId string in SensitivityOfTo with the corresponding
* directory's element name from the provided map. UUIDs not found in the map are left as-is.
*/
export function resolveForVariationId(variationId: string, nameByUuid: Map<string, string>): string {
return variationId.replace(UUID_REGEX, (uuid) => nameByUuid.get(uuid) ?? uuid);
}
33 changes: 30 additions & 3 deletions src/services/study/sensitivity-analysis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@
*/

import { getStudyUrlWithNodeUuidAndRootNetworkUuid } from './index';
import { backendFetch, backendFetchJson, backendFetchText } from '@gridsuite/commons-ui';
import { backendFetch, backendFetchJson, backendFetchText, fetchElementNames } from '@gridsuite/commons-ui';
import type { UUID } from 'node:crypto';
import {
CsvConfig,
SelectorFilterOptions,
Sensitivity,
SensitivityResult,
SensitivityResultFilterOptions,
} from './sensitivity-analysis.type';
import { FilterConfig } from '../../types/custom-aggrid-types';
import { GlobalFilters } from 'components/results/common/global-filter/global-filter-types';
import { extractUuidsFromVariationId, resolveForVariationId } from './sensitivity-analysis-utils';

export function startSensitivityAnalysis(
studyUuid: UUID,
Expand Down Expand Up @@ -58,7 +60,7 @@ export function fetchSensitivityAnalysisStatus(studyUuid: UUID, currentNodeUuid:
return backendFetchText(url);
}

export function fetchSensitivityAnalysisResult(
export async function fetchSensitivityAnalysisResult(
studyUuid: UUID,
currentNodeUuid: UUID,
currentRootNetworkUuid: UUID,
Expand Down Expand Up @@ -87,7 +89,32 @@ export function fetchSensitivityAnalysisResult(
currentRootNetworkUuid
)}/sensitivity-analysis/result?${urlSearchParams}`;
console.debug(url);
return backendFetchJson(url);
const fetchedDto: SensitivityResult | null = await backendFetchJson(url);

if (!fetchedDto) {
return null;
}

// Collect all unique UUIDs across all sensitivities
const allUuids = new Set(
fetchedDto.sensitivities.flatMap((sensitivity: Sensitivity) => extractUuidsFromVariationId(sensitivity.varId))
);

if (allUuids.size === 0) {
return fetchedDto;
}

// Fetch names for all UUIDs in a single fetch
const nameByUuid = await fetchElementNames(allUuids);

// Replace UUIDs with names in each sensitivity
return {
...fetchedDto,
sensitivities: fetchedDto.sensitivities.map((sensitivity) => ({
...sensitivity,
varId: resolveForVariationId(sensitivity.varId, nameByUuid),
})),
};
}

export function fetchSensitivityAnalysisFilterOptions(
Expand Down
Loading