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 3 commits into
base: dev
Choose a base branch
from
Draft
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
@@ -1,6 +1,7 @@
import { describe, it, expect } from 'vitest';

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

import { updatePathStepsFromOperationalPoints } from '../useSetupItineraryForTrainUpdate';

Expand Down Expand Up @@ -177,7 +178,7 @@ describe('updatePathStepsFrom', () => {
];
const result = updatePathStepsFromOperationalPoints(
pathSteps,
suggestedOpPoints,
populatePathStepIdInSuggestedOPs(suggestedOpPoints, pathSteps),
pathFindingResult as Extract<PathfindingResult, { status: 'success' }>,
stepsCoordinates
);
Expand Down Expand Up @@ -253,7 +254,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 @@ -13,7 +13,7 @@ import {
} from 'common/api/osrdEditoastApi';
import { useOsrdConfActions } from 'common/osrdContext';
import buildOpSearchQuery from 'modules/operationalPoint/helpers/buildOpSearchQuery';
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 @@ -39,8 +39,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 @@ -173,6 +173,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
[],
geometry,
path.length
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import ModalBodySNCF from 'common/BootstrapSNCF/ModalSNCF/ModalBodySNCF';
import ModalFooterSNCF from 'common/BootstrapSNCF/ModalSNCF/ModalFooterSNCF';
import ModalHeaderSNCF from 'common/BootstrapSNCF/ModalSNCF/ModalHeaderSNCF';
import { useOsrdConfActions, useOsrdConfSelectors } from 'common/osrdContext';
import { isVia, matchPathStepAndOp } from 'modules/pathfinding/utils';
import type { SuggestedOP } from 'modules/trainschedule/components/ManageTrainSchedule/types';
import type { OperationalStudiesConfSliceActions } from 'reducers/osrdconf/operationalStudiesConf';
import type { PathStep } from 'reducers/osrdconf/types';
Expand Down Expand Up @@ -37,12 +36,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 @@ -105,7 +104,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 @@ -14,11 +14,7 @@ import type {
} from 'common/api/osrdEditoastApi';
import { osrdEditoastApi } from 'common/api/osrdEditoastApi';
import { useOsrdConfActions, useOsrdConfSelectors } from 'common/osrdContext';
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 @@ -118,15 +114,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
85 changes: 32 additions & 53 deletions front/src/modules/pathfinding/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,44 @@ import { getPointCoordinates } from 'utils/geometry';

import getStepLocation from './helpers/getStepLocation';

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;
};

export const populatePathStepIdInSuggestedOPs = (
suggestedOPs: SuggestedOP[],
pathSteps: PathStep[]
): SuggestedOP[] =>
suggestedOPs.map((op) => ({
...op,
pathStepId: pathSteps.find(
(pathStep) => matchPathStepAndOp(pathStep, op) // TODO: && op.kp === pathStep.kp
)?.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 +66,7 @@ export const formatSuggestedOperationalPoints = (
coordinates: getPointCoordinates(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,10 +138,9 @@ export const upsertPathStepsInOPs = (ops: SuggestedOP[], pathSteps: PathStep[]):
}
} else {
updatedOPs = updatedOPs.map((op) => {
if (matchPathStepAndOp(step, op) && op.kp === step.kp) {
if (step.id === op.pathStepId) {
return {
...op,
pathStepId: step.id,
stopFor,
arrival,
receptionSignal,
Expand All @@ -141,40 +154,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 { useTranslation } from 'react-i18next';

import { useScenarioContext } from 'applications/operationalStudies/hooks/useScenarioContext';
import { useOsrdConfActions } from 'common/osrdContext';
import { isVia, matchPathStepAndOp } from 'modules/pathfinding/utils';
import type { SuggestedOP } from 'modules/trainschedule/components/ManageTrainSchedule/types';
import type { OperationalStudiesConfSliceActions } from 'reducers/osrdconf/operationalStudiesConf';
import type { PathStep } from 'reducers/osrdconf/types';
Expand Down Expand Up @@ -43,7 +42,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 @@ -78,9 +77,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 { IsoDurationString, 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 { NO_BREAK_SPACE } from 'utils/strings';
Expand All @@ -24,18 +23,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 @@ -49,7 +36,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 @@ -60,9 +47,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 @@ -72,7 +57,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
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 nextId from 'react-id-generator';

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 @@ -76,7 +75,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