Skip to content

Commit 560eb50

Browse files
Wadjetztheocrsb
authored andcommitted
editoast: adapt get train schedules exceptions (#15502)
fix #15428 This PR adapts the `GET /timetable/{id}/train_schedules` endpoint to retrieve the new exceptions. To reduce the amount of refactoring, we convert the new exceptions into the old ones; later, we will replace them entirely. This PR targets a feature branch and breaks e2e tests. We will fix the tests later (normally, they should fix themselves when we complete the refactoring). Signed-off-by: Egor <egor@berezify.fr>
1 parent bc5a18f commit 560eb50

File tree

15 files changed

+177
-25
lines changed

15 files changed

+177
-25
lines changed

editoast/editoast_models/src/train_schedule_exception.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use editoast_derive::Model;
2+
use schemas::paced_train::PacedTrainException;
23
use schemas::train_schedule_exception::TrainScheduleExceptionChangeGroups;
34

45
use crate as editoast_models;
@@ -31,3 +32,23 @@ impl From<TrainScheduleException> for schemas::TrainScheduleException {
3132
}
3233
}
3334
}
35+
36+
impl From<TrainScheduleException> for PacedTrainException {
37+
fn from(train_schedule_exception: TrainScheduleException) -> Self {
38+
let exception_type = match train_schedule_exception.occurrence_index {
39+
Some(occurrence_index) => schemas::paced_train::ExceptionType::Modified {
40+
occurrence_index: occurrence_index as usize,
41+
},
42+
None => schemas::paced_train::ExceptionType::Created {},
43+
};
44+
Self {
45+
id: Some(train_schedule_exception.id),
46+
key: train_schedule_exception
47+
.key
48+
.unwrap_or_else(|| train_schedule_exception.id.to_string()),
49+
exception_type,
50+
disabled: train_schedule_exception.disabled,
51+
change_groups: train_schedule_exception.change_groups,
52+
}
53+
}
54+
}

editoast/openapi.yaml

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

editoast/schemas/src/fixtures.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ pub fn rolling_stock_with_invalid_effort_curves_json() -> &'static str {
109109

110110
pub fn simple_created_exception_with_change_groups(key: &str) -> PacedTrainException {
111111
PacedTrainException {
112+
id: None,
112113
key: key.into(),
113114
exception_type: ExceptionType::Created {},
114115
disabled: false,

editoast/schemas/src/paced_train.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ impl<'de> Deserialize<'de> for TrainSchedule {
104104
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ToSchema)]
105105
#[cfg_attr(feature = "testing", derive(Default))]
106106
pub struct PacedTrainException {
107+
/// TODO The new exception table id (for incremental migration)
108+
pub id: Option<i64>,
107109
/// Unique key for the exception within the paced train, required and generated by the frontend.
108110
pub key: String,
109111
#[serde(flatten)]

editoast/src/models/fixtures.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ async fn link_train_schedule_set_to_timetable(
135135

136136
pub fn create_created_exception_with_change_groups(key: &str) -> PacedTrainException {
137137
PacedTrainException {
138+
id: None,
138139
key: key.into(),
139140
exception_type: ExceptionType::Created {},
140141
disabled: false,

editoast/src/views/timetable.rs

Lines changed: 122 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ use editoast_models::timetable::Timetable;
4141
use editoast_models::timetable::TimetableWithTrains;
4242
use itertools::Itertools;
4343
use itertools::izip;
44-
use paced_train::TrainScheduleResponse;
44+
use schemas::paced_train::Paced;
45+
use schemas::paced_train::TrainSchedule;
4546
use schemas::rolling_stock::EtcsBrakeParams;
4647
use schemas::rolling_stock::RollingResistance;
4748
use schemas::rolling_stock::RollingStock;
@@ -68,6 +69,7 @@ use crate::models::train_schedule::OccurrenceId;
6869
use crate::models::train_schedule_set::TrainScheduleSet;
6970
use crate::views::AuthenticationExt;
7071
use crate::views::AuthorizationError;
72+
use crate::views::timetable::paced_train::TrainScheduleResponse;
7173
use crate::views::timetable::simulation::SimulationResponseSuccess;
7274

7375
#[derive(Debug, Error, EditoastError, derive_more::From)]
@@ -226,7 +228,50 @@ pub(in crate::views) async fn get_train_schedules(
226228

227229
let (paced_trains, stats) = models::TrainSchedule::list_paginated(conn, settings).await?;
228230

229-
let results = paced_trains.into_iter().map_into().collect();
231+
let paced_trains_ids = paced_trains.iter().map(|t| t.id).collect::<Vec<_>>();
232+
let exceptions_settings = SelectionSettings::new()
233+
.filter(move || editoast_models::TrainScheduleException::TIMETABLE_ID.eq(timetable_id))
234+
.filter(move || {
235+
editoast_models::TrainScheduleException::TRAIN_SCHEDULE_ID
236+
.eq_any(paced_trains_ids.clone())
237+
});
238+
239+
let mut exceptions = editoast_models::TrainScheduleException::list(conn, exceptions_settings)
240+
.await?
241+
.into_iter()
242+
.into_group_map_by(|e| e.train_schedule_id);
243+
244+
let results: Vec<TrainScheduleResponse> = paced_trains
245+
.into_iter()
246+
.map(|ts| {
247+
let id = ts.id;
248+
let train_schedule_set_id = ts.train_schedule_set_id;
249+
250+
let paced = if let (Some(interval), Some(time_window)) = (ts.interval, ts.time_window) {
251+
Some(Paced {
252+
interval: interval.try_into().unwrap(),
253+
time_window: time_window.try_into().unwrap(),
254+
exceptions: exceptions
255+
.remove(&id)
256+
.unwrap_or_default()
257+
.into_iter()
258+
.map_into()
259+
.collect(),
260+
})
261+
} else {
262+
None
263+
};
264+
265+
TrainScheduleResponse {
266+
id,
267+
train_schedule_set_id,
268+
train_schedule: TrainSchedule {
269+
train_occurrence: ts.into_train_occurrence(),
270+
paced,
271+
},
272+
}
273+
})
274+
.collect();
230275

231276
Ok(Json(ListTrainSchedulesResponse { stats, results }))
232277
}
@@ -922,6 +967,7 @@ mod tests {
922967
use crate::error::InternalError;
923968
use crate::models::fixtures::create_timetable;
924969
use crate::models::fixtures::create_timetable_with_train_schedule_set;
970+
use crate::models::fixtures::create_train_schedule_exception;
925971
use crate::models::fixtures::create_train_schedule_set;
926972
use crate::models::fixtures::simple_paced_train_base;
927973
use crate::models::train_schedule::TrainScheduleChangeset;
@@ -984,38 +1030,97 @@ mod tests {
9841030
let app = TestAppBuilder::default_app();
9851031
let pool = app.db_pool();
9861032

987-
let (timetable, train_schedule_set) =
1033+
// Setup timetable 1 data
1034+
let (timetable1, train_schedule_set1) =
9881035
create_timetable_with_train_schedule_set(&mut pool.get_ok()).await;
9891036

990-
let train_schedule_1 = simple_paced_train_base();
991-
let mut train_schedule_2 = simple_paced_train_base();
992-
train_schedule_2.train_occurrence.start_time += Duration::minutes(200);
993-
train_schedule_2.paced.as_mut().unwrap().time_window =
1037+
let train_schedule_t1_1 = simple_paced_train_base();
1038+
let mut train_schedule_t1_2 = simple_paced_train_base();
1039+
train_schedule_t1_2.train_occurrence.start_time += Duration::minutes(200);
1040+
train_schedule_t1_2.paced.as_mut().unwrap().time_window =
9941041
Duration::minutes(120).try_into().unwrap();
995-
train_schedule_2.paced.as_mut().unwrap().interval =
1042+
train_schedule_t1_2.paced.as_mut().unwrap().interval =
9961043
Duration::seconds(30).try_into().unwrap();
9971044

998-
let train_schedules = vec![train_schedule_1, train_schedule_2];
999-
1000-
let changesets = train_schedules
1045+
let train_schedules_bases_t1 = vec![train_schedule_t1_1, train_schedule_t1_2];
1046+
let train_schedules_changesets_t1 = train_schedules_bases_t1
10011047
.into_iter()
10021048
.map(TrainScheduleChangeset::from)
1003-
.map(|cs| cs.train_schedule_set_id(train_schedule_set.id))
1049+
.map(|cs| cs.train_schedule_set_id(train_schedule_set1.id))
10041050
.collect::<Vec<_>>();
1005-
1006-
let _train_schedules: Vec<_> =
1007-
models::TrainSchedule::create_batch(&mut pool.get_ok(), changesets)
1051+
let train_schedules_t1: Vec<_> =
1052+
models::TrainSchedule::create_batch(&mut pool.get_ok(), train_schedules_changesets_t1)
10081053
.await
10091054
.expect("Failed to create train schedules");
1010-
1011-
let request = app.get(format!("/timetable/{}/train_schedules", timetable.id).as_str());
1055+
let train_schedule_exception_t1_1 = create_train_schedule_exception(
1056+
&mut pool.get_ok(),
1057+
timetable1.id,
1058+
train_schedules_t1.first().unwrap().id,
1059+
None,
1060+
)
1061+
.await;
1062+
1063+
let _train_schedule_exception_t1_2 = create_train_schedule_exception(
1064+
&mut pool.get_ok(),
1065+
timetable1.id,
1066+
train_schedules_t1.get(1).unwrap().id,
1067+
None,
1068+
)
1069+
.await;
1070+
1071+
// Setup timetable 2 data
1072+
let (timetable2, train_schedule_set2) =
1073+
create_timetable_with_train_schedule_set(&mut pool.get_ok()).await;
1074+
let mut train_schedule_t2_1 = simple_paced_train_base();
1075+
train_schedule_t2_1.train_occurrence.start_time += Duration::minutes(200);
1076+
train_schedule_t2_1.paced.as_mut().unwrap().time_window =
1077+
Duration::minutes(120).try_into().unwrap();
1078+
train_schedule_t2_1.paced.as_mut().unwrap().interval =
1079+
Duration::seconds(30).try_into().unwrap();
1080+
let train_schedules_bases_t2 = vec![train_schedule_t2_1];
1081+
let train_schedules_changesets_t2 = train_schedules_bases_t2
1082+
.into_iter()
1083+
.map(TrainScheduleChangeset::from)
1084+
.map(|cs| cs.train_schedule_set_id(train_schedule_set2.id))
1085+
.collect::<Vec<_>>();
1086+
let train_schedules_t2: Vec<_> =
1087+
models::TrainSchedule::create_batch(&mut pool.get_ok(), train_schedules_changesets_t2)
1088+
.await
1089+
.expect("Failed to create train schedules");
1090+
let _train_schedule_exception_t2_1 = create_train_schedule_exception(
1091+
&mut pool.get_ok(),
1092+
timetable2.id,
1093+
train_schedules_t2.first().unwrap().id,
1094+
None,
1095+
)
1096+
.await;
1097+
1098+
let request = app.get(format!("/timetable/{}/train_schedules", timetable1.id).as_str());
10121099
let list: ListTrainSchedulesResponse = app
10131100
.fetch(request)
10141101
.await
10151102
.assert_status(StatusCode::OK)
10161103
.json_into();
10171104

1105+
// Check if the timetable has the two train schedules
10181106
assert_eq!(list.results.len(), 2);
1107+
1108+
// Check if the first train schedule containe only his own exception
1109+
assert_eq!(
1110+
list.results
1111+
.first()
1112+
.unwrap()
1113+
.clone()
1114+
.train_schedule
1115+
.paced
1116+
.unwrap()
1117+
.exceptions
1118+
.first()
1119+
.unwrap()
1120+
.id
1121+
.unwrap(),
1122+
train_schedule_exception_t1_1.id
1123+
)
10191124
}
10201125

10211126
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]

editoast/src/views/timetable/paced_train.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,10 @@ enum TrainScheduleError {
105105

106106
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
107107
pub(in crate::views) struct TrainScheduleResponse {
108-
id: i64,
109-
train_schedule_set_id: i64,
108+
pub id: i64,
109+
pub train_schedule_set_id: i64,
110110
#[serde(flatten)]
111-
train_schedule: TrainSchedule,
111+
pub train_schedule: TrainSchedule,
112112
}
113113

114114
impl From<models::TrainSchedule> for TrainScheduleResponse {

editoast/src/views/train_schedule_set.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,13 +481,15 @@ mod tests {
481481
let train_schedule_set = create_train_schedule_set(&mut pool.get_ok()).await;
482482
let mut paced_train_1 = simple_paced_train_base();
483483
let exception_1 = PacedTrainException {
484+
id: None,
484485
key: "exception_key_1".into(),
485486
exception_type: ExceptionType::Created {},
486487
disabled: false,
487488
change_groups: TrainScheduleExceptionChangeGroups::default(),
488489
};
489490

490491
let exception_2 = PacedTrainException {
492+
id: None,
491493
key: "exception_key_2".into(),
492494
exception_type: ExceptionType::Modified {
493495
occurrence_index: 1,

front/src/applications/operationalStudies/views/Scenario/components/Timetable/PacedTrain/hooks/useOccurrences.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ const useOccurrences = (
6767
: pacedTrainCategory,
6868
occurrenceIndex: i,
6969
exceptionChangeGroups: correspondingException
70-
? omit(correspondingException, ['key', 'occurrence_index', 'disabled', 'summary'])
70+
? omit(correspondingException, ['key', 'occurrence_index', 'disabled', 'id', 'summary'])
7171
: undefined,
7272
summary: correspondingException?.summary ?? summary,
7373
});
@@ -98,7 +98,13 @@ const useOccurrences = (
9898
category: exception.rolling_stock_category
9999
? exception.rolling_stock_category.value
100100
: pacedTrainCategory,
101-
exceptionChangeGroups: omit(exception, ['key', 'disabled', 'occurrence_index', 'summary']),
101+
exceptionChangeGroups: omit(exception, [
102+
'key',
103+
'disabled',
104+
'occurrence_index',
105+
'id',
106+
'summary',
107+
]),
102108
summary: exception.summary ?? summary,
103109
});
104110
});

front/src/common/api/generatedEditoastApi.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3927,6 +3927,8 @@ export type PacedTrainException = {
39273927
train_name?: TrainNameChangeGroup;
39283928
} & {
39293929
disabled?: boolean;
3930+
/** TODO The new exception table id (for incremental migration) */
3931+
id?: number | null;
39303932
/** Unique key for the exception within the paced train, required and generated by the frontend. */
39313933
key: string;
39323934
};

0 commit comments

Comments
 (0)