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: adapt import train schedules v2 #7172

Merged
merged 1 commit into from
Apr 23, 2024
Merged
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 @@ -31,8 +31,13 @@
"status": {
"calculatingTrainSchedule": "Calculating train schedules...",
"calculatingTrainScheduleComplete": "Running time calculation completed (path {{pathId}}) {{createdTrainsCount}}/{{trainsCount}}",
"calculatingTrainScheduleCompleteAll_one": "All running time calculations completed: {{successfulTrainsCount}}/{{trainsCount}} train created.",
"calculatingTrainScheduleCompleteAll_other": "All running time calculations completed: {{successfulTrainsCount}}/{{trainsCount}} trains created.",
"calculatingTrainScheduleCompleteAll_one": "All running time calculations completed: {{successfulTrainsCount}}/{{trainsCount}} train created. {{errorsNb}}/{{trainsCount}} train couldn't be created.",
"calculatingTrainScheduleCompleteAll_other": "All running time calculations completed: {{successfulTrainsCount}}/{{trainsCount}} trains created. {{errorsNb}}/{{trainsCount}} trains couldn't be created.",
"calculatingTrainScheduleCompleteAllSuccess_one": "All running time calculations completed: {{successfulTrainsCount}} train created.",
"calculatingTrainScheduleCompleteAllSuccess": "All running time calculations completed: {{successfulTrainsCount}} trains created.",
"calculatingTrainScheduleCompleteFailure_zero": "All running time calculations have failed.",
"calculatingTrainScheduleCompleteFailure_one": "All running time calculations completed: {{errorsNb}}/{{trainsCount}} train couldn't be created.",
"calculatingTrainScheduleCompleteFailure_other": "All running time calculations completed: {{errorsNb}}/{{trainsCount}} trains couldn't be created.",
"calculatingTrainScheduleCompleteAllFailure": "All running time calculations have failed.",
"complete": "{{uicNumber}}/{{uicTotalCount}} position {{uicName}}",
"missingRollingStock": "You must choose a default rolling stock",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,13 @@
"status": {
"calculatingTrainSchedule": "Calculs de marches en cours…",
"calculatingTrainScheduleComplete": "Calcul de marche terminé (path {{pathId}}) {{createdTrainsCount}}/{{trainsCount}}",
"calculatingTrainScheduleCompleteAll_one": "Tous les calculs de marches sont terminés: {{successfulTrainsCount}}/{{trainsCount}} train créé.",
"calculatingTrainScheduleCompleteAll_other": "Tous les calculs de marches sont terminés: {{successfulTrainsCount}}/{{trainsCount}} trains créés.",
"calculatingTrainScheduleCompleteAll_one": "Tous les calculs de marches sont terminés: {{successfulTrainsCount}}/{{trainsCount}} train créé. {{errorsNb}}/{{trainsCount}} train n'a pas été créé.",
"calculatingTrainScheduleCompleteAll_other": "Tous les calculs de marches sont terminés: {{successfulTrainsCount}}/{{trainsCount}} trains créés. {{errorsNb}}/{{trainsCount}} trains n'ont pas été créés.",
"calculatingTrainScheduleCompleteAllSuccess_one": "Tous les calculs de marches sont terminés: {{successfulTrainsCount}} train créé.",
"calculatingTrainScheduleCompleteAllSuccess": "Tous les calculs de marches sont terminés: {{successfulTrainsCount}} trains créés.",
"calculatingTrainScheduleCompleteFailure_zero": "Tous les calculs de marche ont échoué.",
"calculatingTrainScheduleCompleteFailure_one": "Des calculs de marche ont échoué. {{errorsNb}}/{{trainsCount}} train n'a pas été créé.",
"calculatingTrainScheduleCompleteFailure_other": "Des calculs de marche ont échoué. {{errorsNb}}/{{trainsCount}} trains n'ont pas été créés.",
"calculatingTrainScheduleCompleteAllFailure": "Tous les calculs de marche ont échoué.",
"complete": "{{uicNumber}}/{{uicTotalCount}} positionnez {{uicName}}",
"missingRollingStock": "Vous devez choisir un matériel roulant par défaut",
Expand Down
19 changes: 17 additions & 2 deletions front/src/applications/operationalStudies/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,32 @@ export interface Step extends Destination {
track: string;
}[];
}
export interface StepV2 extends Destination {
arrivalTime: string;
departureTime: string;
duration?: number;
}

export type TrainSchedule = {
trainNumber: string;
rollingStock: string;
rollingStock: string | null;
departureTime: string;
arrivalTime: string;
departure: string;
steps: Step[];
transilienName?: string;
};

export type TrainScheduleV2 = {
trainNumber: string;
rollingStock: string | null;
departureTime: string;
arrivalTime: string;
departure: string;
steps: StepV2[];
transilienName?: string;
};

export interface TrainScheduleWithPathRef extends TrainSchedule {
pathRef: string;
}
Expand All @@ -41,7 +56,7 @@ export interface TrainScheduleWithPath extends TrainScheduleWithPathRef {

export type ImportedTrainSchedule = {
trainNumber: string;
rollingStock: string;
rollingStock: string | null;
departureTime: string;
arrivalTime: string;
departure: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,18 @@ import React, { useEffect, useState } from 'react';

import { useTranslation } from 'react-i18next';

import type { TrainSchedule } from 'applications/operationalStudies/types';
import type { TrainScheduleV2 } from 'applications/operationalStudies/types';
import { enhancedEditoastApi } from 'common/api/enhancedEditoastApi';
import { Loader } from 'common/Loaders';
import {
ImportTrainScheduleConfigV2,
ImportTrainScheduleTrainsList,
} from 'modules/trainschedule/components/ImportTrainSchedule';
import { ImportTrainScheduleConfigV2 } from 'modules/trainschedule/components/ImportTrainSchedule';
import ImportTrainScheduleTrainsListV2 from 'modules/trainschedule/components/ImportTrainSchedule/ImportTrainScheduleTrainsListV2';
import { setFailure } from 'reducers/main';
import { useAppDispatch } from 'store';

const ImportTrainScheduleV2 = ({
infraId,
timetableId,
}: {
infraId: number;
timetableId: number;
}) => {
const ImportTrainScheduleV2 = ({ timetableId }: { timetableId: number }) => {
const dispatch = useAppDispatch();
const { t } = useTranslation(['rollingstock']);
const [trainsList, setTrainsList] = useState<TrainSchedule[]>([]);
const [trainsList, setTrainsList] = useState<TrainScheduleV2[]>([]);
const [isLoading, setIsLoading] = useState(false);

const { data: { results: rollingStocks } = { results: [] }, isError } =
Expand All @@ -42,13 +34,8 @@ const ImportTrainScheduleV2 = ({

return rollingStocks ? (
<main className="import-train-schedule">
<ImportTrainScheduleConfigV2
setIsLoading={setIsLoading}
setTrainsList={setTrainsList}
infraId={infraId}
/>
<ImportTrainScheduleTrainsList
infraId={infraId}
<ImportTrainScheduleConfigV2 setIsLoading={setIsLoading} setTrainsList={setTrainsList} />
<ImportTrainScheduleTrainsListV2
isLoading={isLoading}
rollingStocks={rollingStocks}
timetableId={timetableId}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { useParams } from 'react-router-dom';
import BreadCrumbs from 'applications/operationalStudies/components/BreadCrumbs';
import InfraLoadingState from 'applications/operationalStudies/components/Scenario/InfraLoadingState';
import { MANAGE_TRAIN_SCHEDULE_TYPES } from 'applications/operationalStudies/consts';
import ImportTrainSchedule from 'applications/operationalStudies/views/ImportTrainSchedule';
import infraLogo from 'assets/pictures/components/tracks.svg';
import { osrdEditoastApi } from 'common/api/osrdEditoastApi';
import { useModal } from 'common/BootstrapSNCF/ModalSNCF';
Expand All @@ -24,6 +23,7 @@ import { updateSelectedProjection, updateSimulation } from 'reducers/osrdsimulat
import { getPresentSimulation, getSelectedTrainId } from 'reducers/osrdsimulation/selectors';
import { useAppDispatch } from 'store';

import ImportTrainScheduleV2 from './ImportTrainScheduleV2';
import ManageTrainScheduleV2 from './ManageTrainScheduleV2';
import SimulationResultsV2 from './SimulationResultsV2';

Expand Down Expand Up @@ -331,7 +331,7 @@ const ScenarioV2 = () => {
)}
{displayTrainScheduleManagement === MANAGE_TRAIN_SCHEDULE_TYPES.import && (
<div className="scenario-managetrainschedule">
<ImportTrainSchedule infraId={infraId} timetableId={timetableId} />
<ImportTrainScheduleV2 timetableId={timetableId} />
</div>
)}
<div className="scenario-results">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,29 @@ import { useTranslation } from 'react-i18next';

import type {
ImportedTrainSchedule,
TrainSchedule,
TrainScheduleV2,
TrainScheduleImportConfig,
} from 'applications/operationalStudies/types';
import { getGraouTrainSchedules } from 'common/api/graouApi';
import {
type SearchResultItemOperationalPoint,
type PostSearchApiArg,
osrdEditoastApi,
} from 'common/api/osrdEditoastApi';
import InputSNCF from 'common/BootstrapSNCF/InputSNCF';
import { ModalContext } from 'common/BootstrapSNCF/ModalSNCF/ModalProvider';
import StationCard, { type ImportStation } from 'common/StationCard';
import UploadFileModal from 'common/uploadFileModal';
import { RollingStockSelector } from 'modules/rollingStock/components/RollingStockSelector';
import { useStoreDataForRollingStockSelector } from 'modules/rollingStock/components/RollingStockSelector/useStoreDataForRollingStockSelector';
import StationSelector from 'modules/trainschedule/components/ImportTrainSchedule/ImportTrainScheduleStationSelector';
import { setFailure } from 'reducers/main';
import { useAppDispatch } from 'store';
import { formatIsoDate } from 'utils/date';

interface ImportTrainScheduleConfigProps {
infraId: number;
setTrainsList: (trainsList: TrainSchedule[]) => void;
setTrainsList: (trainsList: TrainScheduleV2[]) => void;
setIsLoading: (isLoading: boolean) => void;
}

type SearchConstraintType = (string | number | string[])[];

const ImportTrainScheduleConfigV2 = ({
setTrainsList,
infraId,
setIsLoading,
}: ImportTrainScheduleConfigProps) => {
const { t } = useTranslation(['operationalStudies/importTrainSchedule']);
const [postSearch] = osrdEditoastApi.endpoints.postSearch.useMutation();
const [from, setFrom] = useState<ImportStation | undefined>();
const [fromSearchString, setFromSearchString] = useState('');
const [to, setTo] = useState<ImportStation | undefined>();
Expand All @@ -50,43 +38,6 @@ const ImportTrainScheduleConfigV2 = ({
const [endTime, setEndTime] = useState('23:59');
const dispatch = useAppDispatch();
const { openModal, closeModal } = useContext(ModalContext);
const { rollingStockComfort, rollingStock } = useStoreDataForRollingStockSelector();

async function importTrackSections(opIds: number[]): Promise<
Record<
number,
{
position: number;
track: string;
}[]
>
> {
const uniqueOpIds = Array.from(new Set(opIds));
const constraint = uniqueOpIds.reduce((res, uic) => [...res, ['=', ['uic'], Number(uic)]], [
'or',
] as (string | SearchConstraintType)[]);
const payload: PostSearchApiArg = {
searchPayload: {
object: 'operationalpoint',
query: ['and', constraint, ['=', ['infra_id'], infraId]],
},
pageSize: 1000,
};
const operationalPoints = (await postSearch(
payload
).unwrap()) as SearchResultItemOperationalPoint[];

return operationalPoints.reduce(
(res, operationalPoint) =>
operationalPoint.uic
? {
...res,
[operationalPoint.uic]: operationalPoint.track_sections,
}
: res,
{}
);
}

function validateImportedTrainSchedules(
importedTrainSchedules: Record<string, unknown>[]
Expand Down Expand Up @@ -119,12 +70,7 @@ const ImportTrainScheduleConfigV2 = ({
return importedTrainSchedules as ImportedTrainSchedule[];
}

async function updateTrainSchedules(importedTrainSchedules: ImportedTrainSchedule[]) {
const opIds = importedTrainSchedules.flatMap((trainSchedule) =>
trainSchedule.steps.map((step) => step.uic)
);
const trackSectionsByOp = await importTrackSections(opIds);

function updateTrainSchedules(importedTrainSchedules: ImportedTrainSchedule[]) {
// For each train schedule, we add the duration and tracks of each step
const trainsSchedules = importedTrainSchedules.map((trainSchedule) => {
const stepsWithDuration = trainSchedule.steps.map((step) => {
Expand All @@ -137,7 +83,6 @@ const ImportTrainScheduleConfigV2 = ({
return {
...step,
duration,
tracks: trackSectionsByOp[Number(step.uic)] || [],
};
});
return {
Expand All @@ -156,7 +101,7 @@ const ImportTrainScheduleConfigV2 = ({
const result = await getGraouTrainSchedules(config);
const importedTrainSchedules = validateImportedTrainSchedules(result);
if (importedTrainSchedules && !isEmpty(importedTrainSchedules)) {
await updateTrainSchedules(importedTrainSchedules);
updateTrainSchedules(importedTrainSchedules);
}

setIsLoading(false);
Expand Down Expand Up @@ -211,9 +156,9 @@ const ImportTrainScheduleConfigV2 = ({

return (
<>
<div className="row no-gutters">
<div className="container-fluid row no-gutters mb-2">
<div className="col-lg-6 station-selector sm-gutters">
<div className="osrd-config-item mb-2">
<div className="mb-2">
<div className="osrd-config-item-container osrd-config-item-from">
<h2>{t('from')}</h2>
{from ? (
Expand All @@ -238,7 +183,7 @@ const ImportTrainScheduleConfigV2 = ({
</div>
</div>
<div className="col-lg-6 station-selector sm-gutters">
<div className="osrd-config-item mb-2">
<div className="mb-2">
<div className="osrd-config-item-container osrd-config-item-to">
<h2>{t('to')}</h2>
{to ? (
Expand All @@ -264,15 +209,9 @@ const ImportTrainScheduleConfigV2 = ({
</div>
</div>

<div className="row no-gutters">
<div className="col-lg-6 sm-gutters">
<RollingStockSelector
rollingStockSelected={rollingStock}
rollingStockComfort={rollingStockComfort}
/>
</div>
<div className="col-lg-5 col-9 sm-gutters">
<div className="osrd-config-item">
<div className="container-fluid mb-2">
<div className="row no-gutters">
<div className="col-lg-10 col-10">
<div className="osrd-config-item-container osrd-config-item-datetime">
<h2>{t('datetime')}</h2>
<div className="mb-2">
Expand All @@ -288,7 +227,7 @@ const ImportTrainScheduleConfigV2 = ({
/>
</div>
<div className="row no-gutters">
<span className="col-6 sm-gutters">
<div className="col-6 sm-gutters">
<InputSNCF
id="startTime"
type="time"
Expand All @@ -301,8 +240,8 @@ const ImportTrainScheduleConfigV2 = ({
step={0}
unit={t('startTime')}
/>
</span>
<span className="col-6 sm-gutters">
</div>
<div className="col-6 sm-gutters">
<InputSNCF
id="endTime"
type="time"
Expand All @@ -315,30 +254,30 @@ const ImportTrainScheduleConfigV2 = ({
step={0}
unit={t('endTime')}
/>
</span>
</div>
</div>
</div>
</div>
</div>
<div className="col-lg-1 col-3 d-flex flex-column sm-gutters pb-lg-2">
<button
type="button"
className="btn btn-sm btn-primary btn-block h-100"
aria-label={t('searchTimetable')}
title={t('searchTimetable')}
onClick={defineConfig}
>
<Search />
</button>
<button
type="button"
className="btn btn-sm btn-secondary btn-block h-100"
aria-label={t('importTimetable')}
title={t('importTimetable')}
onClick={() => openModal(<UploadFileModal handleSubmit={importFile} />)}
>
<Download />
</button>
<div className="col-lg-2 col-2 d-flex flex-column no-gutters pl-1">
<button
type="button"
className="btn btn-sm btn-primary btn-block h-100"
aria-label={t('searchTimetable')}
title={t('searchTimetable')}
onClick={defineConfig}
>
<Search />
</button>
<button
type="button"
className="btn btn-sm btn-secondary btn-block h-100"
aria-label={t('importTimetable')}
title={t('importTimetable')}
onClick={() => openModal(<UploadFileModal handleSubmit={importFile} />)}
>
<Download />
</button>
</div>
</div>
</div>
</>
Expand Down
Loading
Loading