Skip to content

Commit 302e525

Browse files
authored
editoast: adapt occupancy blocks endpoint (#15765)
Part of #15202 This PR adapt the `/train_schedules/occupancy_blocks`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 Rename exception_key into exception_id 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 99aa5d0 commit 302e525

File tree

6 files changed

+108
-82
lines changed

6 files changed

+108
-82
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/occupancy_blocks.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ impl TrainBlockOccupancyDetails {
8888
#[derive(Debug, Deserialize, ToSchema)]
8989
pub(in crate::views) struct OccupancyBlockForm {
9090
pub(super) infra_id: i64,
91+
pub(super) timetable_id: i64,
9192
pub(super) electrical_profile_set_id: Option<i64>,
9293
pub(super) ids: HashSet<i64>,
9394
pub(super) path: TrainPath,

editoast/src/views/timetable/paced_train.rs

Lines changed: 99 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,6 +1119,7 @@ pub(in crate::views) async fn occupancy_blocks(
11191119
Extension(auth): AuthenticationExt,
11201120
Json(OccupancyBlockForm {
11211121
infra_id,
1122+
timetable_id,
11221123
ids: train_schedule_ids,
11231124
path,
11241125
electrical_profile_set_id,
@@ -1154,21 +1155,39 @@ pub(in crate::views) async fn occupancy_blocks(
11541155
})
11551156
.await?;
11561157

1158+
let mut exceptions =
1159+
editoast_models::TrainScheduleException::retrieve_exceptions_by_train_schedules(
1160+
conn,
1161+
timetable_id,
1162+
train_schedules.iter().map(|t| t.id).collect::<Vec<_>>(),
1163+
)
1164+
.await?
1165+
.into_iter()
1166+
.map_into::<schemas::TrainScheduleException>()
1167+
.into_group_map_by(|e| e.train_schedule_id);
1168+
11571169
let simulation_contexts: Vec<SimulationContext> = train_schedules
11581170
.iter()
11591171
.flat_map(|train_schedule| {
1172+
let ts_exceptions = exceptions.remove(&train_schedule.id).unwrap_or_default();
11601173
std::iter::once(SimulationContext {
11611174
paced_train_id: train_schedule.id,
11621175
exception_key: None,
11631176
train_schedule: train_schedule.clone().into_train_occurrence(),
11641177
})
1165-
.chain(train_schedule.exceptions.iter().map(|exception| {
1166-
SimulationContext {
1167-
paced_train_id: train_schedule.id,
1168-
exception_key: Some(exception.key.clone()),
1169-
train_schedule: train_schedule.apply_exception(exception),
1170-
}
1171-
}))
1178+
.chain(
1179+
ts_exceptions
1180+
.iter()
1181+
.map(|exception| {
1182+
SimulationContext {
1183+
paced_train_id: train_schedule.id,
1184+
exception_key: exception.key.clone(), // TODO use exception.id
1185+
train_schedule: train_schedule
1186+
.apply_train_schedule_exception(exception),
1187+
}
1188+
})
1189+
.collect::<Vec<_>>(),
1190+
)
11721191
})
11731192
.collect();
11741193

@@ -1679,6 +1698,7 @@ mod tests {
16791698
use editoast_models::TrainScheduleException;
16801699
use editoast_models::prelude::*;
16811700
use editoast_models::rolling_stock::TrainMainCategory;
1701+
use editoast_models::timetable::Timetable;
16821702
use pretty_assertions::assert_eq;
16831703
use rstest::rstest;
16841704
use schemas::TrainScheduleExceptionChangeGroups;
@@ -1687,7 +1707,6 @@ mod tests {
16871707
use schemas::infra::Direction;
16881708
use schemas::paced_train::InitialSpeedChangeGroup;
16891709
use schemas::paced_train::Paced;
1690-
use schemas::paced_train::PacedTrainException;
16911710
use schemas::paced_train::RollingStockChangeGroup;
16921711
use schemas::paced_train::TrainNameChangeGroup;
16931712
use schemas::paced_train::TrainSchedule;
@@ -2041,8 +2060,13 @@ mod tests {
20412060
assert_eq!(response.train_schedule, paced_train.into());
20422061
}
20432062

2044-
async fn app_infra_id_paced_train_id_for_simulation_tests()
2045-
-> (TestApp, i64, i64, TrainScheduleException) {
2063+
async fn app_infra_id_paced_train_id_for_simulation_tests() -> (
2064+
TestApp,
2065+
i64,
2066+
Timetable,
2067+
models::TrainSchedule,
2068+
TrainScheduleException,
2069+
) {
20462070
let db_pool = DbConnectionPoolV2::for_tests();
20472071
let small_infra = create_small_infra(&mut db_pool.get_ok()).await;
20482072
let rolling_stock =
@@ -2051,7 +2075,7 @@ mod tests {
20512075
create_timetable_with_train_schedule_set(&mut db_pool.get_ok()).await;
20522076
let exception = create_created_exception_with_change_groups("created_exception_key");
20532077

2054-
let paced_train_base = TrainSchedule {
2078+
let train_schedule_base = TrainSchedule {
20552079
train_occurrence: TrainOccurrence {
20562080
rolling_stock_name: rolling_stock.name.clone(),
20572081
..TrainOccurrence::fake()
@@ -2062,8 +2086,8 @@ mod tests {
20622086
exceptions: vec![],
20632087
}),
20642088
};
2065-
let paced_train: TrainScheduleChangeset = paced_train_base.into();
2066-
let paced_train = paced_train
2089+
let train_schedule: TrainScheduleChangeset = train_schedule_base.into();
2090+
let train_schedule = train_schedule
20672091
.train_schedule_set_id(train_schedule_set.id)
20682092
.create(&mut db_pool.get_ok())
20692093
.await
@@ -2072,7 +2096,7 @@ mod tests {
20722096
let exception = create_train_schedule_exception(
20732097
&mut db_pool.get_ok(),
20742098
timetable.id,
2075-
paced_train.id,
2099+
train_schedule.id,
20762100
None,
20772101
Some("created_exception_key".to_string()),
20782102
Some(exception.change_groups),
@@ -2084,16 +2108,19 @@ mod tests {
20842108
.db_pool(db_pool)
20852109
.core_client(core.into())
20862110
.build();
2087-
(app, small_infra.id, paced_train.id, exception)
2111+
(app, small_infra.id, timetable, train_schedule, exception)
20882112
}
20892113

20902114
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
20912115
async fn paced_train_simulation() {
2092-
let (app, infra_id, train_schedule_id, _exception) =
2116+
let (app, infra_id, _timetable, train_schedule, _exception) =
20932117
app_infra_id_paced_train_id_for_simulation_tests().await;
20942118
let request = app.get(
2095-
format!("/train_schedules/{train_schedule_id}/simulation/?infra_id={infra_id}")
2096-
.as_str(),
2119+
format!(
2120+
"/train_schedules/{}/simulation/?infra_id={infra_id}",
2121+
train_schedule.id
2122+
)
2123+
.as_str(),
20972124
);
20982125
let response: core_client::simulation::Response = app
20992126
.fetch(request)
@@ -2106,11 +2133,12 @@ mod tests {
21062133

21072134
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
21082135
async fn paced_train_exception_simulation_with_invalid_exception_key() {
2109-
let (app, infra_id, train_schedule_id, _exception) =
2136+
let (app, infra_id, _timetable, train_schedule, _exception) =
21102137
app_infra_id_paced_train_id_for_simulation_tests().await;
21112138
let request = app.get(
21122139
format!(
2113-
"/train_schedules/{train_schedule_id}/simulation/?infra_id={infra_id}&exception_id=9999"
2140+
"/train_schedules/{}/simulation/?infra_id={infra_id}&exception_id=9999",
2141+
train_schedule.id
21142142
)
21152143
.as_str(),
21162144
);
@@ -2128,12 +2156,12 @@ mod tests {
21282156

21292157
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
21302158
async fn paced_train_exception_simulation() {
2131-
let (app, infra_id, train_schedule_id, exception) =
2159+
let (app, infra_id, _timetable, train_schedule, exception) =
21322160
app_infra_id_paced_train_id_for_simulation_tests().await;
21332161
let request = app.get(
21342162
format!(
2135-
"/train_schedules/{train_schedule_id}/simulation/?infra_id={infra_id}&exception_id={}",
2136-
exception.id
2163+
"/train_schedules/{}/simulation/?infra_id={infra_id}&exception_id={}",
2164+
train_schedule.id, exception.id
21372165
)
21382166
.as_str(),
21392167
);
@@ -2188,7 +2216,7 @@ mod tests {
21882216
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
21892217
async fn paced_train_exception_simulation_with_rolling_stock_not_found() {
21902218
// GIVEN
2191-
let (app, infra_id, train_schedule_id, exception) =
2219+
let (app, infra_id, _timetable, train_schedule, exception) =
21922220
app_infra_id_paced_train_id_for_simulation_tests().await;
21932221

21942222
let mut change_groupe = exception.change_groups;
@@ -2198,16 +2226,16 @@ mod tests {
21982226
});
21992227
let exception = editoast_models::TrainScheduleException::changeset()
22002228
.change_groups(change_groupe)
2201-
.update(&mut app.db_pool().get_ok(), train_schedule_id)
2229+
.update(&mut app.db_pool().get_ok(), train_schedule.id)
22022230
.await
22032231
.expect("Fail to update exception")
22042232
.expect("Fail to update exception");
22052233

22062234
// WHEN
22072235
let request = app.get(
22082236
format!(
2209-
"/train_schedules/{train_schedule_id}/simulation/?infra_id={infra_id}&exception_id={}",
2210-
exception.id
2237+
"/train_schedules/{}/simulation/?infra_id={infra_id}&exception_id={}",
2238+
train_schedule.id, exception.id
22112239
)
22122240
.as_str(),
22132241
);
@@ -2232,7 +2260,7 @@ mod tests {
22322260

22332261
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
22342262
async fn paced_train_simulation_not_found() {
2235-
let (app, infra_id, _paced_train_id, _exception) =
2263+
let (app, infra_id, _timetable, _train_schedule, _exception) =
22362264
app_infra_id_paced_train_id_for_simulation_tests().await;
22372265
let request =
22382266
app.get(format!("/train_schedules/{}/simulation/?infra_id={}", 0, infra_id).as_str());
@@ -2358,7 +2386,7 @@ mod tests {
23582386

23592387
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
23602388
async fn paced_train_simulation_summary_not_found() {
2361-
let (app, infra_id, _paced_train_id, _exception) =
2389+
let (app, infra_id, _timetable, _paced_train_id, _exception) =
23622390
app_infra_id_paced_train_id_for_simulation_tests().await;
23632391
let timetable = create_timetable(&mut app.db_pool().get_ok()).await;
23642392
let request = app
@@ -2696,67 +2724,50 @@ mod tests {
26962724

26972725
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
26982726
async fn paced_train_occupancy_blocks() {
2699-
let (app, infra_id, paced_train_id, _exception) =
2727+
let (app, infra_id, timetable, train_schedule, exception) =
27002728
app_infra_id_paced_train_id_for_simulation_tests().await;
2729+
let db_pool = app.db_pool();
27012730

2702-
let request = app.get(format!("/train_schedules/{paced_train_id}").as_str());
2703-
let mut paced_train_response: TrainScheduleResponse = app
2704-
.fetch(request)
2705-
.await
2706-
.assert_status(StatusCode::OK)
2707-
.json_into();
27082731
// First remove all already generated exceptions
2709-
paced_train_response
2710-
.train_schedule
2711-
.paced
2712-
.as_mut()
2713-
.unwrap()
2714-
.exceptions
2715-
.clear();
2732+
exception
2733+
.delete(&mut db_pool.get_ok())
2734+
.await
2735+
.expect("Failled to remove exception");
27162736

27172737
// Add one exception which will not change the simulation from base
2718-
paced_train_response
2719-
.train_schedule
2720-
.paced
2721-
.as_mut()
2722-
.unwrap()
2723-
.exceptions
2724-
.push(PacedTrainException {
2725-
key: "change_train_name".to_string(),
2726-
change_groups: TrainScheduleExceptionChangeGroups {
2727-
train_name: Some(TrainNameChangeGroup {
2728-
value: "exception_name_but_same_simulation".into(),
2729-
}),
2730-
..Default::default()
2731-
},
2738+
let _exception1 = create_train_schedule_exception(
2739+
&mut db_pool.get_ok(),
2740+
timetable.id,
2741+
train_schedule.id,
2742+
None,
2743+
Some("change_train_name".to_string()),
2744+
Some(TrainScheduleExceptionChangeGroups {
2745+
train_name: Some(TrainNameChangeGroup {
2746+
value: "exception_name_but_same_simulation".into(),
2747+
}),
27322748
..Default::default()
2733-
});
2749+
}),
2750+
)
2751+
.await;
2752+
27342753
// Add one exception which will change the simulation from base
2735-
// and therefore add another entry in the response (field `exceptions`)
2736-
paced_train_response
2737-
.train_schedule
2738-
.paced
2739-
.as_mut()
2740-
.unwrap()
2741-
.exceptions
2742-
.push(PacedTrainException {
2743-
key: "change_initial_speed".to_string(),
2744-
change_groups: TrainScheduleExceptionChangeGroups {
2745-
initial_speed: Some(InitialSpeedChangeGroup { value: 1.23 }),
2746-
..Default::default()
2747-
},
2754+
let _exception1 = create_train_schedule_exception(
2755+
&mut db_pool.get_ok(),
2756+
timetable.id,
2757+
train_schedule.id,
2758+
None,
2759+
Some("change_initial_speed".to_string()),
2760+
Some(TrainScheduleExceptionChangeGroups {
2761+
initial_speed: Some(InitialSpeedChangeGroup { value: 1.23 }),
27482762
..Default::default()
2749-
});
2750-
let request = app
2751-
.put(format!("/train_schedules/{paced_train_id}").as_str())
2752-
.json(&json!(paced_train_response.train_schedule));
2753-
app.fetch(request)
2754-
.await
2755-
.assert_status(StatusCode::NO_CONTENT);
2763+
}),
2764+
)
2765+
.await;
27562766

27572767
let request = app.post("/train_schedules/occupancy_blocks").json(
2758-
&json!({"ids": vec![paced_train_id],
2768+
&json!({"ids": vec![train_schedule.id],
27592769
"infra_id": infra_id,
2770+
"timetable_id": timetable.id,
27602771
"path": {
27612772
"track_section_ranges": [{
27622773
"track_section": "T1",
@@ -2775,10 +2786,17 @@ mod tests {
27752786
assert_eq!(response.len(), 1);
27762787
// TODO fix mocked simulation to return path item times that respect times
27772788
assert_eq!(
2778-
response.get(&paced_train_id).unwrap().train_schedule.len(),
2789+
response
2790+
.get(&train_schedule.id)
2791+
.unwrap()
2792+
.train_schedule
2793+
.len(),
2794+
0
2795+
);
2796+
assert_eq!(
2797+
response.get(&train_schedule.id).unwrap().exceptions.len(),
27792798
0
27802799
);
2781-
assert_eq!(response.get(&paced_train_id).unwrap().exceptions.len(), 0);
27822800
}
27832801

27842802
fn pathfinding_result_success() -> PathfindingResultSuccess {

front/src/applications/operationalStudies/helpers/TrainOpProjectionLazyLoader.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export default class TrainOpProjectionLazyLoader extends TrainProjectionLazyLoad
3434
}
3535

3636
async processBatch(ids: number[]) {
37-
const { infraId, path, electricalProfileSetId } = this.options;
37+
const { infraId, timetableId, path, electricalProfileSetId } = this.options;
3838

3939
if (this.opRefs.length < 2) {
4040
this.options.onProgress(new Map());
@@ -72,6 +72,7 @@ export default class TrainOpProjectionLazyLoader extends TrainProjectionLazyLoad
7272
{
7373
occupancyBlockForm: {
7474
infra_id: infraId,
75+
timetable_id: timetableId,
7576
path,
7677
ids,
7778
electrical_profile_set_id: electricalProfileSetId,

front/src/applications/operationalStudies/helpers/TrainTrackProjectionLazyLoader.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export default class TrainTrackProjectionLazyLoader extends TrainProjectionLazyL
6161
{
6262
occupancyBlockForm: {
6363
infra_id: infraId,
64+
timetable_id: timetableId,
6465
path,
6566
ids,
6667
electrical_profile_set_id: electricalProfileSetId,

front/src/common/api/generatedEditoastApi.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4848,6 +4848,7 @@ export type OccupancyBlockForm = {
48484848
ids: number[];
48494849
infra_id: number;
48504850
path: CoreTrainPath;
4851+
timetable_id: number;
48514852
};
48524853
export type SpaceTimeCurve = {
48534854
/** List of positions of a train in mm

0 commit comments

Comments
 (0)