Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

front: match PathStep and SuggestedOP with pathStepId #10006

Draft
wants to merge 11 commits into
base: dev
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { describe, it, expect } from 'vitest';

import type { PathfindingResult } from 'common/api/osrdEditoastApi';
import { populatePathStepIdInSuggestedOPs } from 'modules/pathfinding/utils';
import { Duration } from 'utils/duration';

import { updatePathStepsFromOperationalPoints } from '../useSetupItineraryForTrainUpdate';
Expand Down Expand Up @@ -178,7 +179,7 @@ describe('updatePathStepsFrom', () => {
];
const result = updatePathStepsFromOperationalPoints(
pathSteps,
suggestedOpPoints,
populatePathStepIdInSuggestedOPs(suggestedOpPoints, pathSteps),
pathFindingResult as Extract<PathfindingResult, { status: 'success' }>,
stepsCoordinates
);
Expand Down Expand Up @@ -254,7 +255,7 @@ describe('updatePathStepsFrom', () => {
];
const result = updatePathStepsFromOperationalPoints(
pathSteps,
suggestedOpPoints,
populatePathStepIdInSuggestedOPs(suggestedOpPoints, pathSteps),
pathFindingResult as Extract<PathfindingResult, { status: 'success' }>,
stepsCoordinates
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
import buildOpSearchQuery from 'modules/operationalPoint/helpers/buildOpSearchQuery';
import getPointOnPathCoordinates from 'modules/pathfinding/helpers/getPointOnPathCoordinates';
import getTrackLengthCumulativeSums from 'modules/pathfinding/helpers/getTrackLengthCumulativeSums';
import { formatSuggestedOperationalPoints, matchPathStepAndOp } from 'modules/pathfinding/utils';
import { formatSuggestedOperationalPoints } from 'modules/pathfinding/utils';
import { getSupportedElectrification, isThermal } from 'modules/rollingStock/helpers/electric';
import type { SuggestedOP } from 'modules/trainschedule/components/ManageTrainSchedule/types';
import computeBasePathStep from 'modules/trainschedule/helpers/computeBasePathStep';
Expand All @@ -42,8 +42,8 @@ export function updatePathStepsFromOperationalPoints(
stepsCoordinates: Position[]
) {
const updatedPathSteps: PathStep[] = pathSteps.map((step, i) => {
const correspondingOp = suggestedOperationalPoints.find((suggestedOp) =>
matchPathStepAndOp(step, suggestedOp)
const correspondingOp = suggestedOperationalPoints.find(
(suggestedOp) => step.id === suggestedOp.pathStepId
);

const { kp, name } = correspondingOp || step;
Expand Down Expand Up @@ -204,6 +204,7 @@ const useSetupItineraryForTrainUpdate = (trainIdToEdit: number) => {

const suggestedOperationalPoints: SuggestedOP[] = formatSuggestedOperationalPoints(
operational_points,
trainSchedule.path,
geometry,
pathfindingResult.length
);
Expand Down
1 change: 1 addition & 0 deletions front/src/applications/stdcm/hooks/useStdcmResults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ const useStdcmResults = (

const suggestedOperationalPoints: SuggestedOP[] = formatSuggestedOperationalPoints(
operationalPointsWithMetadata, // Pass the operational points with metadata
stdcmTrainResult?.path ?? [],
geometry,
path.length
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { SimulationResponse } from 'common/api/osrdEditoastApi';
import { matchPathStepAndOp } from 'modules/pathfinding/utils';
import { interpolateValue } from 'modules/simulationResult/SimulationResultExport/utils';
import type { SuggestedOP } from 'modules/trainschedule/components/ManageTrainSchedule/types';
import type { StdcmPathStep } from 'reducers/osrdconf/types';
Expand Down Expand Up @@ -137,9 +136,7 @@ export function getOperationalPointsWithTimes(
const stopEndTime = computeStopDepartureTime(formattedTime, durationToString);

// Find the corresponding stopType from pathSteps
const correspondingStep = simulationPathSteps.find(
(step) => step.location && matchPathStepAndOp(step.location, op)
);
const correspondingStep = simulationPathSteps.find((step) => step.id === op.pathStepId);
let stopType;
if (correspondingStep) {
stopType = !correspondingStep.isVia ? 'serviceStop' : correspondingStep.stopType;
Expand Down
1 change: 1 addition & 0 deletions front/src/applications/stdcm/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const extractMarkersInfo = (pathSteps: StdcmPathStep[]): MarkerInformatio

acc.push({
pointType,
id: step.id,
uic: step.location.uic,
secondary_code: step.location.secondary_code,
coordinates: step.location.coordinates,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { useSelector } from 'react-redux';
import ModalBodySNCF from 'common/BootstrapSNCF/ModalSNCF/ModalBodySNCF';
import ModalFooterSNCF from 'common/BootstrapSNCF/ModalSNCF/ModalFooterSNCF';
import ModalHeaderSNCF from 'common/BootstrapSNCF/ModalSNCF/ModalHeaderSNCF';
import { isVia, matchPathStepAndOp } from 'modules/pathfinding/utils';
import type { SuggestedOP } from 'modules/trainschedule/components/ManageTrainSchedule/types';
import { upsertViaFromSuggestedOP } from 'reducers/osrdconf/operationalStudiesConf';
import {
Expand Down Expand Up @@ -39,12 +38,12 @@ const ModalSuggestedVias = ({ suggestedVias, launchPathfinding }: ModalSuggested
);

const removeViaFromPath = (op: SuggestedOP) => {
const newPathSteps = pathSteps.filter((step) => !matchPathStepAndOp(step!, op));
const newPathSteps = pathSteps.filter((step) => step!.id !== op.pathStepId);
launchPathfinding(newPathSteps);
};

const formatOP = (op: SuggestedOP, idx: number, idxTrueVia: number) => {
const isInVias = isVia(vias, op);
const isInVias = vias.find((step) => step.id === op.pathStepId);
return (
<div
key={`suggested-via-modal-${op.opId}-${idx}`}
Expand Down Expand Up @@ -107,7 +106,7 @@ const ModalSuggestedVias = ({ suggestedVias, launchPathfinding }: ModalSuggested
{suggestedVias.map((via, idx) => {
if (!isOriginOrDestination(via)) {
// If name is undefined, we know the op/via has been added by clicking on map
if (isVia(vias, via)) idxTrueVia += 1;
if (vias.find((step) => step.id === via.pathStepId)) idxTrueVia += 1;
return formatOP(via, idx, idxTrueVia);
}
return null;
Expand Down
11 changes: 4 additions & 7 deletions front/src/modules/pathfinding/hooks/usePathfinding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,7 @@ import type {
PostInfraByInfraIdPathPropertiesApiArg,
} from 'common/api/osrdEditoastApi';
import { osrdEditoastApi } from 'common/api/osrdEditoastApi';
import {
formatSuggestedOperationalPoints,
getPathfindingQuery,
matchPathStepAndOp,
} from 'modules/pathfinding/utils';
import { formatSuggestedOperationalPoints, getPathfindingQuery } from 'modules/pathfinding/utils';
import { useStoreDataForRollingStockSelector } from 'modules/rollingStock/components/RollingStockSelector/useStoreDataForRollingStockSelector';
import type { SuggestedOP } from 'modules/trainschedule/components/ManageTrainSchedule/types';
import { setFailure, setWarning } from 'reducers/main';
Expand Down Expand Up @@ -117,15 +113,16 @@ const usePathfinding = (

const suggestedOperationalPoints: SuggestedOP[] = formatSuggestedOperationalPoints(
operational_points,
pathStepsInput,
geometry,
pathResult.length
);

// We update existing pathsteps with coordinates, positionOnPath and kp corresponding to the new pathfinding result
const updatedPathSteps: (PathStep | null)[] = pathStepsInput.map((step, i) => {
if (!step) return step;
const correspondingOp = suggestedOperationalPoints.find((suggestedOp) =>
matchPathStepAndOp(step, suggestedOp)
const correspondingOp = suggestedOperationalPoints.find(
(suggestedOp) => step.id === suggestedOp.pathStepId
);

const theoreticalMargin = i === 0 ? step.theoreticalMargin || '0%' : step.theoreticalMargin;
Expand Down
86 changes: 29 additions & 57 deletions front/src/modules/pathfinding/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,41 @@ import { getPointOnTrackCoordinates } from 'utils/geometry';

import getStepLocation from './helpers/getStepLocation';

const matchPathStepAndOp = (step: PathItemLocation, op: SuggestedOP) => {
if ('operational_point' in step) {
return step.operational_point === op.opId;
}
if ('uic' in step) {
return step.uic === op.uic && step.secondary_code === op.ch;
}
if ('trigram' in step) {
return step.trigram === op.trigram && step.secondary_code === op.ch;
}
return step.track === op.track && step.offset === op.offsetOnTrack;
};

export const populatePathStepIdInSuggestedOPs = (
suggestedOPs: SuggestedOP[],
pathSteps: PathStep[]
): SuggestedOP[] =>
suggestedOPs.map((op) => ({
...op,
pathStepId: pathSteps.find(
(pathStep) => matchPathStepAndOp(pathStep, op) // TODO: && op.kp === pathStep.kp && step.positionOnPath === op.positionOnPath
)?.id,
}));

export const formatSuggestedOperationalPoints = (
operationalPoints: Array<
NonNullable<Required<PathProperties['operational_points']>>[number] & {
metadata?: NonNullable<SuggestedOP['metadata']>;
}
>,
pathSteps: PathStep[],
geometry: GeoJsonLineString,
pathLength: number
): SuggestedOP[] =>
operationalPoints.map((op) => ({
): SuggestedOP[] => {
const suggestedOPs = operationalPoints.map((op) => ({
opId: op.id,
name: op.extensions?.identifier?.name,
uic: op.extensions?.identifier?.uic,
Expand All @@ -38,21 +63,7 @@ export const formatSuggestedOperationalPoints = (
coordinates: getPointOnTrackCoordinates(geometry, pathLength, op.position),
metadata: op?.metadata,
}));

export const matchPathStepAndOp = (
step: PathItemLocation,
op: Pick<SuggestedOP, 'opId' | 'uic' | 'ch' | 'trigram' | 'track' | 'offsetOnTrack'>
) => {
if ('operational_point' in step) {
return step.operational_point === op.opId;
}
if ('uic' in step) {
return step.uic === op.uic && step.secondary_code === op.ch;
}
if ('trigram' in step) {
return step.trigram === op.trigram && step.secondary_code === op.ch;
}
return step.track === op.track && step.offset === op.offsetOnTrack;
return populatePathStepIdInSuggestedOPs(suggestedOPs, pathSteps);
};

export const getPathfindingQuery = ({
Expand Down Expand Up @@ -124,14 +135,9 @@ export const upsertPathStepsInOPs = (ops: SuggestedOP[], pathSteps: PathStep[]):
}
} else {
updatedOPs = updatedOPs.map((op) => {
if (
matchPathStepAndOp(step, op) &&
op.kp === step.kp &&
step.positionOnPath === op.positionOnPath
) {
if (step.id === op.pathStepId) {
return {
...op,
pathStepId: step.id,
stopFor,
arrival,
receptionSignal,
Expand All @@ -145,40 +151,6 @@ export const upsertPathStepsInOPs = (ops: SuggestedOP[], pathSteps: PathStep[]):
return updatedOPs;
};

export const pathStepMatchesOp = (
pathStep: PathStep,
op: Pick<
SuggestedOP,
'pathStepId' | 'opId' | 'uic' | 'ch' | 'trigram' | 'track' | 'offsetOnTrack' | 'name' | 'kp'
>,
withKP = false
) => {
if (!matchPathStepAndOp(pathStep, op)) {
return pathStep.id === op.pathStepId;
}
if ('uic' in pathStep) {
return withKP ? pathStep.kp === op.kp : pathStep.name === op.name;
}
return true;
};

/**
* Check if a suggested operational point is a via.
* Some OPs have same uic so we need to check also the ch (can be still not enough
* probably because of imports problem).
* If the vias has no uic, it has been added via map click and we know it has an id.
* @param withKP - If true, we check the kp compatibility instead of the name.
* It is used in the times and stops table to check if an operational point is a via.
*/
export const isVia = (
vias: PathStep[],
op: Pick<
SuggestedOP,
'pathStepId' | 'opId' | 'uic' | 'ch' | 'trigram' | 'track' | 'offsetOnTrack' | 'name' | 'kp'
>,
{ withKP = false } = {}
) => vias.some((via) => pathStepMatchesOp(via, op, withKP));

export const isStation = (chCode: string): boolean =>
chCode === 'BV' || chCode === '00' || chCode === '';

Expand Down
7 changes: 2 additions & 5 deletions front/src/modules/timesStops/TimesStopsInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import type { Operation } from 'react-datasheet-grid/dist/types';
import { useTranslation } from 'react-i18next';

import { useScenarioContext } from 'applications/operationalStudies/hooks/useScenarioContext';
import { isVia, matchPathStepAndOp } from 'modules/pathfinding/utils';
import type { SuggestedOP } from 'modules/trainschedule/components/ManageTrainSchedule/types';
import {
updatePathSteps,
Expand Down Expand Up @@ -48,7 +47,7 @@ const createClearViaButton = ({
pathStepsAndSuggestedOPs &&
rowIndex > 0 &&
rowIndex < pathStepsAndSuggestedOPs.length - 1 &&
isVia(pathSteps || [], rowData, { withKP: true }) &&
pathSteps.find((step) => step.id === rowData.pathStepId) &&
(!isNil(rowData.stopFor) ||
rowData.theoreticalMargin !== undefined ||
rowData.arrival !== undefined ||
Expand Down Expand Up @@ -81,9 +80,7 @@ const TimesStopsInput = ({
const { getTrackSectionsByIds, trackSectionsLoading } = useScenarioContext();

const clearPathStep = (rowData: TimesStopsInputRow) => {
const index = pathSteps.findIndex(
(step) => matchPathStepAndOp(step, rowData) && step.positionOnPath === rowData.positionOnPath
);
const index = pathSteps.findIndex((step) => step.id === rowData.pathStepId);

const updatedPathSteps = pathSteps.map((step, i) => {
if (i === index) {
Expand Down
21 changes: 3 additions & 18 deletions front/src/modules/timesStops/helpers/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { keyColumn, createTextColumn } from 'react-datasheet-grid';

import type { ReceptionSignal } from 'common/api/osrdEditoastApi';
import type { TimeString } from 'common/types';
import { matchPathStepAndOp } from 'modules/pathfinding/utils';
import type { SuggestedOP } from 'modules/trainschedule/components/ManageTrainSchedule/types';
import type { PathStep } from 'reducers/osrdconf/types';
import { Duration } from 'utils/duration';
Expand All @@ -25,18 +24,6 @@ import {
import { marginRegExValidation, MarginUnit } from '../consts';
import { TableType, type TimeExtraDays, type TimesStopsInputRow } from '../types';

const matchPathStepAndOpWithKP = (step: PathStep, op: SuggestedOP) => {
if (!matchPathStepAndOp(step, op)) {
return step.id === op.pathStepId;
}
// We match the kp in case two OPs have the same uic+ch (can happen when the
// infra is imported)
if ('uic' in step || 'trigram' in step) {
return step.kp === op.kp;
}
return true;
};

export const formatSuggestedViasToRowVias = (
operationalPoints: SuggestedOP[],
pathSteps: PathStep[],
Expand All @@ -50,7 +37,7 @@ export const formatSuggestedViasToRowVias = (
// to move it to the first position
const origin = pathSteps[0];
const originIndexInOps = origin
? operationalPoints.findIndex((op) => matchPathStepAndOpWithKP(origin, op))
? operationalPoints.findIndex((op) => origin.id === op.pathStepId)
: -1;
if (originIndexInOps !== -1) {
[formattedOps[0], formattedOps[originIndexInOps]] = [
Expand All @@ -61,9 +48,7 @@ export const formatSuggestedViasToRowVias = (

// Ditto: destination should be last
const dest = pathSteps[pathSteps.length - 1];
const destIndexInOps = dest
? operationalPoints.findIndex((op) => matchPathStepAndOpWithKP(dest, op))
: -1;
const destIndexInOps = dest ? operationalPoints.findIndex((op) => dest.id === op.pathStepId) : -1;
if (destIndexInOps !== -1) {
const lastOpIndex = formattedOps.length - 1;
[formattedOps[lastOpIndex], formattedOps[destIndexInOps]] = [
Expand All @@ -73,7 +58,7 @@ export const formatSuggestedViasToRowVias = (
}

return formattedOps.map((op, i) => {
const pathStep = pathSteps.find((step) => matchPathStepAndOpWithKP(step, op));
const pathStep = pathSteps.find((step) => step.id === op.pathStepId);
const { name } = pathStep || op;
const objectToUse = tableType === TableType.Input ? pathStep : op;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@ import stdcmOrigin from 'assets/pictures/mapMarkers/start.svg';
import originSVG from 'assets/pictures/origin.svg';
import viaSVG from 'assets/pictures/via.svg';
import type { PathItemLocation, TrackSection } from 'common/api/osrdEditoastApi';
import { matchPathStepAndOp } from 'modules/pathfinding/utils';
import type { PathStep } from 'reducers/osrdconf/types';

import type { SuggestedOP } from '../types';

export type MarkerInformation = Pick<PathStep, 'name' | 'coordinates' | 'metadata'> &
export type MarkerInformation = Pick<PathStep, 'id' | 'name' | 'coordinates' | 'metadata'> &
PathItemLocation & {
pointType: MARKER_TYPE;
};
Expand Down Expand Up @@ -73,7 +72,7 @@ const extractMarkerInformation = (
pathSteps
.map((pathStep, index): MarkerProperties | null => {
const matchingOp = suggestedOP
? suggestedOP.find((op) => matchPathStepAndOp(pathStep, op))
? suggestedOP.find((op) => pathStep.id === op.pathStepId)
: undefined;

if (pathStep.coordinates) {
Expand Down
3 changes: 1 addition & 2 deletions front/src/reducers/osrdconf/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { v4 as uuidV4 } from 'uuid';

import { calculateDistanceAlongTrack } from 'applications/editor/tools/utils';
import type { ManageTrainSchedulePathProperties } from 'applications/operationalStudies/types';
import { pathStepMatchesOp } from 'modules/pathfinding/utils';
import type { SuggestedOP } from 'modules/trainschedule/components/ManageTrainSchedule/types';
import { addElementAtIndex } from 'utils/array';

Expand Down Expand Up @@ -86,7 +85,7 @@ export function upsertPathStep(statePathSteps: (PathStep | null)[], op: Suggeste
}),
};

const stepIndex = cleanPathSteps.findIndex((step) => pathStepMatchesOp(step, op));
const stepIndex = cleanPathSteps.findIndex((step) => step.id === op.pathStepId);
if (stepIndex >= 0) {
// Because of import issues, there can be multiple ops with same position on path
// To avoid updating the wrong one, we need to find the one that matches the payload
Expand Down
Loading