Skip to content

Commit 2050f05

Browse files
Wadjetztheocrsb
authored andcommitted
editost: adapt track_occupancy endpoint with new exceptions (#15793)
Part of #15202 This PR adapt the `/train_schedules/track_occupancy`endpoint to use the new exceptions table It also requires the timetable_id as a parameter, as exceptions are now related to a timetable and not just to a train schedule Fix tests and frontend > [!NOTE] > This PR targets a feature branch and may contain all its commits, as it has not been rebased yet. Only the last commit need to be reviewed. Signed-off-by: Egor <egor@berezify.fr>
1 parent d41bd23 commit 2050f05

File tree

5 files changed

+84
-28
lines changed

5 files changed

+84
-28
lines changed

editoast/openapi.yaml

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

editoast/src/views/timetable/paced_train.rs

Lines changed: 75 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,6 +1205,7 @@ pub(in crate::views) struct TrackOccupancyForm {
12051205
train_schedule_ids: Vec<i64>,
12061206
operational_point_reference: OperationalPointReference,
12071207
infra_id: i64,
1208+
timetable_id: i64,
12081209
electrical_profile_set_id: Option<i64>,
12091210
use_simulation: bool,
12101211
}
@@ -1253,6 +1254,7 @@ pub(in crate::views) async fn track_occupancy(
12531254
train_schedule_ids,
12541255
operational_point_reference,
12551256
infra_id,
1257+
timetable_id,
12561258
electrical_profile_set_id,
12571259
use_simulation,
12581260
}): Json<TrackOccupancyForm>,
@@ -1281,17 +1283,32 @@ pub(in crate::views) async fn track_occupancy(
12811283
let conn = &mut db_pool.get().await?;
12821284

12831285
let train_schedules: Vec<models::TrainSchedule> =
1284-
models::TrainSchedule::retrieve_batch_or_fail(conn, train_schedule_ids, |missing| {
1285-
TrainScheduleError::BatchNotFound {
1286+
models::TrainSchedule::retrieve_batch_or_fail(
1287+
conn,
1288+
train_schedule_ids.clone(),
1289+
|missing| TrainScheduleError::BatchNotFound {
12861290
count: missing.len(),
1287-
}
1288-
})
1291+
},
1292+
)
12891293
.await?;
12901294

1291-
// Collect all occurrences from all paced trains using iter_occurrences()
1295+
let mut exceptions = TrainScheduleException::retrieve_exceptions_by_train_schedules(
1296+
conn,
1297+
timetable_id,
1298+
train_schedule_ids,
1299+
)
1300+
.await?
1301+
.into_iter()
1302+
.map_into::<schemas::TrainScheduleException>()
1303+
.into_group_map_by(|e| e.timetable_id);
1304+
12921305
let (train_ids, trains): (Vec<_>, Vec<_>) = train_schedules
12931306
.iter()
1294-
.flat_map(|train_schedule| train_schedule.iter_occurrences())
1307+
.flat_map(|train_schedule| {
1308+
train_schedule
1309+
.iter_occurrences_v2(&exceptions.remove(&train_schedule.id).unwrap_or_default())
1310+
.collect::<Vec<_>>()
1311+
})
12951312
.unzip();
12961313

12971314
let op_location =
@@ -1649,7 +1666,6 @@ mod tests {
16491666
use schemas::fixtures::simple_created_exception_with_change_groups;
16501667
use schemas::fixtures::simple_modified_exception_with_change_groups;
16511668
use schemas::infra::Direction;
1652-
use schemas::paced_train::ExceptionType;
16531669
use schemas::paced_train::InitialSpeedChangeGroup;
16541670
use schemas::paced_train::Paced;
16551671
use schemas::paced_train::PacedTrainException;
@@ -2751,7 +2767,7 @@ mod tests {
27512767
}
27522768

27532769
async fn init_paced_train_test(
2754-
exceptions: Vec<PacedTrainException>,
2770+
with_exception: bool,
27552771
path: Vec<PathItem>,
27562772
schedule: Vec<ScheduleItem>,
27572773
operational_point_reference: OperationalPointReference,
@@ -2771,46 +2787,77 @@ mod tests {
27712787
let small_infra = create_small_infra(&mut db_pool.get_ok()).await;
27722788
let rolling_stock =
27732789
create_fast_rolling_stock(&mut db_pool.get_ok(), "simulation_rolling_stock").await;
2774-
let train_schedule_set = create_train_schedule_set(&mut db_pool.get_ok()).await;
2775-
let paced_train = models::TrainSchedule::default()
2790+
let (timetable, train_schedule_set) =
2791+
create_timetable_with_train_schedule_set(&mut db_pool.get_ok()).await;
2792+
let train_schedule = models::TrainSchedule::default()
27762793
.into_changeset()
27772794
.train_schedule_set_id(train_schedule_set.id)
27782795
.rolling_stock_name(rolling_stock.name)
27792796
.path(path)
27802797
.schedule(schedule)
27812798
.interval(TimeDelta::try_minutes(15))
27822799
.time_window(TimeDelta::try_hours(1))
2783-
.exceptions(exceptions)
27842800
.create(&mut db_pool.get_ok())
27852801
.await
27862802
.expect("Failed to create paced train");
27872803

2804+
if with_exception {
2805+
TrainScheduleException::changeset()
2806+
.timetable_id(timetable.id)
2807+
.train_schedule_id(train_schedule.id)
2808+
.change_groups(TrainScheduleExceptionChangeGroups::default())
2809+
.create(&mut db_pool.get_ok())
2810+
.await
2811+
.expect("Failed to create exception");
2812+
}
2813+
27882814
let request = app
27892815
.post("/train_schedules/track_occupancy")
27902816
.json(&TrackOccupancyForm {
2791-
train_schedule_ids: vec![paced_train.id],
2817+
train_schedule_ids: vec![train_schedule.id],
27922818
operational_point_reference,
27932819
infra_id: small_infra.id,
2820+
timetable_id: timetable.id,
27942821
electrical_profile_set_id: None,
27952822
use_simulation,
27962823
});
27972824

27982825
app.fetch(request).await
27992826
}
28002827

2801-
#[rstest]
28022828
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
2803-
#[case(vec![], 4)]
2829+
async fn paced_train_track_occupancy_without_exceptions() {
2830+
let response = init_paced_train_test(
2831+
false,
2832+
vec![
2833+
PathItem::new_operational_point("Mid_West_station"),
2834+
PathItem::new_operational_point("Mid_East_station"),
2835+
],
2836+
vec![ScheduleItem::new_with_stop(
2837+
"Mid_East_station",
2838+
Duration::new(0, 0).expect("Failed to parse duration"),
2839+
)],
2840+
OperationalPointReference::Id {
2841+
operational_point: "Mid_West_station".into(),
2842+
},
2843+
true,
2844+
);
2845+
let track_occupancies: Vec<TrackSectionOccupancy> =
2846+
response.await.assert_status(StatusCode::OK).json_into();
2847+
2848+
assert_eq!(track_occupancies.len(), 1);
2849+
let item = &track_occupancies[0];
2850+
assert_eq!(
2851+
item.local_track_name,
2852+
Some(NonBlankString("V2".to_string()))
2853+
);
2854+
assert_eq!(item.trains.len(), 4);
2855+
}
2856+
28042857
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
2805-
#[case(vec![
2806-
PacedTrainException { exception_type: ExceptionType::Created {}, ..Default::default()}], 5)
2807-
]
2808-
async fn paced_train_track_occupancy(
2809-
#[case] exceptions: Vec<PacedTrainException>,
2810-
#[case] paced_trains: usize,
2811-
) {
2858+
async fn paced_train_track_occupancy_with_exceptions() {
28122859
let response = init_paced_train_test(
2813-
exceptions,
2860+
true,
28142861
vec![
28152862
PathItem::new_operational_point("Mid_West_station"),
28162863
PathItem::new_operational_point("Mid_East_station"),
@@ -2833,13 +2880,13 @@ mod tests {
28332880
item.local_track_name,
28342881
Some(NonBlankString("V2".to_string()))
28352882
);
2836-
assert_eq!(item.trains.len(), paced_trains);
2883+
assert_eq!(item.trains.len(), 5);
28372884
}
28382885

28392886
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
28402887
async fn paced_train_returns_empty_track_occupancies() {
28412888
let response = init_paced_train_test(
2842-
Vec::new(),
2889+
false,
28432890
vec![
28442891
PathItem::new_operational_point("Mid_West_station"),
28452892
PathItem::new_operational_point("Mid_East_station"),
@@ -2897,7 +2944,7 @@ mod tests {
28972944
#[case] use_simulation: bool,
28982945
) {
28992946
let response = init_paced_train_test(
2900-
Vec::new(),
2947+
false,
29012948
vec![
29022949
new_op_with_trigram_and_local_track_name("Mid_West_station", "MWS", None, None),
29032950
new_op_with_trigram_and_local_track_name(
@@ -2959,7 +3006,7 @@ mod tests {
29593006
),
29603007
};
29613008
let response = init_paced_train_test(
2962-
Vec::new(),
3009+
false,
29633010
vec![
29643011
first_path_item,
29653012
PathItem::new_operational_point("Mid_East_station"),
@@ -2984,7 +3031,7 @@ mod tests {
29843031
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
29853032
async fn track_occupancy_no_sim_no_arrival_returns_empty() {
29863033
let response = init_paced_train_test(
2987-
Vec::new(),
3034+
false,
29883035
vec![
29893036
PathItem::new_operational_point("Mid_West_station"),
29903037
PathItem::new_operational_point("Mid_East_station"),
@@ -3131,7 +3178,7 @@ mod tests {
31313178
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
31323179
async fn track_occupancy_op_in_db_but_not_in_path_items() {
31333180
let response = init_paced_train_test(
3134-
Vec::new(),
3181+
false,
31353182
vec![
31363183
PathItem::new_operational_point("South_West_station"),
31373184
PathItem::new_operational_point("Mid_East_station"),

front/src/applications/operationalStudies/views/Scenario/components/SimulationResults/SimulationResults.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ const SimulationResults = ({
123123
updateTrackOccupanciesOnDrag: handleTrainDragInTrackOccupancy,
124124
} = useTrackOccupancy({
125125
infraId,
126+
timetableId,
126127
pathOperationalPoints: filteredOperationalPoints,
127128
timetableItemProjections,
128129
});

front/src/common/api/generatedEditoastApi.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2597,6 +2597,7 @@ export type PostTrainSchedulesTrackOccupancyApiArg = {
25972597
electrical_profile_set_id?: number | null;
25982598
infra_id: number;
25992599
operational_point_reference: OperationalPointReference;
2600+
timetable_id: number;
26002601
train_schedule_ids: number[];
26012602
use_simulation: boolean;
26022603
};

front/src/modules/simulationResult/components/SpaceTimeChartWrapper/useTrackOccupancy.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,12 @@ function getOperationalPointReference(
9595
*/
9696
const useTrackOccupancy = ({
9797
infraId,
98+
timetableId,
9899
timetableItemProjections,
99100
pathOperationalPoints,
100101
}: {
101102
infraId: number;
103+
timetableId: number;
102104
timetableItemProjections: TrainSpaceTimeData[];
103105
pathOperationalPoints: PathOperationalPoint[];
104106
}): {
@@ -194,6 +196,7 @@ const useTrackOccupancy = ({
194196
? {
195197
operational_point_reference: opRef,
196198
infra_id: infraId,
199+
timetable_id: timetableId,
197200
train_schedule_ids: pacedTrainIds.map(extractEditoastIdFromPacedTrainId),
198201
use_simulation: isSimulationEnabled,
199202
}

0 commit comments

Comments
 (0)