Skip to content

Commit 2f6a549

Browse files
Wadjetztheocrsb
authored andcommitted
editoast: adapt simulation endpoint with new exceptions (#15695)
Part of #15653 This PR adapt the `GET /train_schedules/{id}/simulation` with new exceptions Rename query string to 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 2a5ca28 commit 2f6a549

File tree

6 files changed

+108
-81
lines changed

6 files changed

+108
-81
lines changed

editoast/openapi.yaml

Lines changed: 3 additions & 2 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: 66 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,9 @@ pub(in crate::views) async fn simulation(
524524
Query(ElectricalProfileSetIdQueryParam {
525525
electrical_profile_set_id,
526526
}): Query<ElectricalProfileSetIdQueryParam>,
527-
Query(ExceptionQueryParam { exception_key }): Query<ExceptionQueryParam>,
527+
Query(TrainScheduleExceptionQueryParam { exception_id }): Query<
528+
TrainScheduleExceptionQueryParam,
529+
>,
528530
) -> Result<Json<simulation::Response>> {
529531
let authorized = auth
530532
.check_roles([authz::Role::OperationalStudies].into())
@@ -555,17 +557,20 @@ pub(in crate::views) async fn simulation(
555557
})
556558
.await?;
557559

558-
let train_schedule = match exception_key {
559-
Some(exception_key) => {
560-
let exception = train_schedule
561-
.exceptions
562-
.iter()
563-
.find(|e| e.key == exception_key)
564-
.ok_or_else(|| TrainScheduleError::ExceptionNotFound {
565-
exception_key: exception_key.clone(),
566-
})?;
567-
568-
train_schedule.apply_exception(exception)
560+
let train_schedule = match exception_id {
561+
Some(exception_id) => {
562+
let exception = TrainScheduleException::retrieve_or_fail(
563+
db_pool.get().await?,
564+
exception_id,
565+
|| {
566+
TrainScheduleError::ExceptionNotFound {
567+
// TODO rename to exception_id
568+
exception_key: exception_id.to_string(),
569+
}
570+
},
571+
)
572+
.await?;
573+
train_schedule.apply_train_schedule_exception(&exception.into())
569574
}
570575
None => train_schedule.into_train_occurrence(),
571576
};
@@ -1635,6 +1640,7 @@ mod tests {
16351640
use core_client::simulation::ReportTrain;
16361641
use core_client::simulation::SpeedLimitProperties;
16371642
use database::DbConnectionPoolV2;
1643+
use editoast_models::TrainScheduleException;
16381644
use editoast_models::prelude::*;
16391645
use editoast_models::rolling_stock::TrainMainCategory;
16401646
use pretty_assertions::assert_eq;
@@ -1999,12 +2005,16 @@ mod tests {
19992005
assert_eq!(response.train_schedule, paced_train.into());
20002006
}
20012007

2002-
async fn app_infra_id_paced_train_id_for_simulation_tests() -> (TestApp, i64, i64) {
2008+
async fn app_infra_id_paced_train_id_for_simulation_tests()
2009+
-> (TestApp, i64, i64, TrainScheduleException) {
20032010
let db_pool = DbConnectionPoolV2::for_tests();
20042011
let small_infra = create_small_infra(&mut db_pool.get_ok()).await;
2005-
let train_schedule_set = create_train_schedule_set(&mut db_pool.get_ok()).await;
20062012
let rolling_stock =
20072013
create_fast_rolling_stock(&mut db_pool.get_ok(), "simulation_rolling_stock").await;
2014+
let (timetable, train_schedule_set) =
2015+
create_timetable_with_train_schedule_set(&mut db_pool.get_ok()).await;
2016+
let exception = create_created_exception_with_change_groups("created_exception_key");
2017+
20082018
let paced_train_base = TrainSchedule {
20092019
train_occurrence: TrainOccurrence {
20102020
rolling_stock_name: rolling_stock.name.clone(),
@@ -2013,9 +2023,7 @@ mod tests {
20132023
paced: Some(Paced {
20142024
time_window: Duration::hours(1).try_into().unwrap(),
20152025
interval: Duration::minutes(15).try_into().unwrap(),
2016-
exceptions: vec![create_created_exception_with_change_groups(
2017-
"created_exception_key",
2018-
)],
2026+
exceptions: vec![],
20192027
}),
20202028
};
20212029
let paced_train: TrainScheduleChangeset = paced_train_base.into();
@@ -2024,17 +2032,28 @@ mod tests {
20242032
.create(&mut db_pool.get_ok())
20252033
.await
20262034
.expect("Failed to create paced train");
2035+
2036+
let exception = create_train_schedule_exception(
2037+
&mut db_pool.get_ok(),
2038+
timetable.id,
2039+
paced_train.id,
2040+
None,
2041+
Some("created_exception_key".to_string()),
2042+
Some(exception.change_groups),
2043+
)
2044+
.await;
2045+
20272046
let core = mocked_core_pathfinding_sim_and_proj();
20282047
let app = TestAppBuilder::new()
20292048
.db_pool(db_pool)
20302049
.core_client(core.into())
20312050
.build();
2032-
(app, small_infra.id, paced_train.id)
2051+
(app, small_infra.id, paced_train.id, exception)
20332052
}
20342053

20352054
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
20362055
async fn paced_train_simulation() {
2037-
let (app, infra_id, train_schedule_id) =
2056+
let (app, infra_id, train_schedule_id, _exception) =
20382057
app_infra_id_paced_train_id_for_simulation_tests().await;
20392058
let request = app.get(
20402059
format!("/train_schedules/{train_schedule_id}/simulation/?infra_id={infra_id}")
@@ -2051,11 +2070,11 @@ mod tests {
20512070

20522071
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
20532072
async fn paced_train_exception_simulation_with_invalid_exception_key() {
2054-
let (app, infra_id, train_schedule_id) =
2073+
let (app, infra_id, train_schedule_id, _exception) =
20552074
app_infra_id_paced_train_id_for_simulation_tests().await;
20562075
let request = app.get(
20572076
format!(
2058-
"/train_schedules/{train_schedule_id}/simulation/?infra_id={infra_id}&exception_key=toto"
2077+
"/train_schedules/{train_schedule_id}/simulation/?infra_id={infra_id}&exception_id=9999"
20592078
)
20602079
.as_str(),
20612080
);
@@ -2073,10 +2092,14 @@ mod tests {
20732092

20742093
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
20752094
async fn paced_train_exception_simulation() {
2076-
let (app, infra_id, train_schedule_id) =
2095+
let (app, infra_id, train_schedule_id, exception) =
20772096
app_infra_id_paced_train_id_for_simulation_tests().await;
20782097
let request = app.get(
2079-
format!("/train_schedules/{train_schedule_id}/simulation/?infra_id={infra_id}&exception_key=created_exception_key").as_str(),
2098+
format!(
2099+
"/train_schedules/{train_schedule_id}/simulation/?infra_id={infra_id}&exception_id={}",
2100+
exception.id
2101+
)
2102+
.as_str(),
20802103
);
20812104
let response: simulation::Response = app
20822105
.fetch(request)
@@ -2129,34 +2152,28 @@ mod tests {
21292152
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
21302153
async fn paced_train_exception_simulation_with_rolling_stock_not_found() {
21312154
// GIVEN
2132-
let (app, infra_id, train_schedule_id) =
2155+
let (app, infra_id, train_schedule_id, exception) =
21332156
app_infra_id_paced_train_id_for_simulation_tests().await;
2134-
let request = app.get(format!("/train_schedules/{train_schedule_id}").as_str());
2135-
let mut paced_train_response: TrainScheduleResponse = app
2136-
.fetch(request)
2137-
.await
2138-
.assert_status(StatusCode::OK)
2139-
.json_into();
2140-
paced_train_response
2141-
.train_schedule
2142-
.paced
2143-
.as_mut()
2144-
.unwrap()
2145-
.exceptions[0]
2146-
.change_groups
2147-
.rolling_stock = Some(RollingStockChangeGroup {
2157+
2158+
let mut change_groupe = exception.change_groups;
2159+
change_groupe.rolling_stock = Some(RollingStockChangeGroup {
21482160
rolling_stock_name: "R2D2".into(),
21492161
comfort: Comfort::AirConditioning,
21502162
});
2151-
let request = app
2152-
.put(format!("/train_schedules/{train_schedule_id}").as_str())
2153-
.json(&json!(paced_train_response.train_schedule));
2154-
app.fetch(request)
2163+
let exception = editoast_models::TrainScheduleException::changeset()
2164+
.change_groups(change_groupe)
2165+
.update(&mut app.db_pool().get_ok(), train_schedule_id)
21552166
.await
2156-
.assert_status(StatusCode::NO_CONTENT);
2167+
.expect("Fail to update exception")
2168+
.expect("Fail to update exception");
2169+
21572170
// WHEN
21582171
let request = app.get(
2159-
format!("/train_schedules/{train_schedule_id}/simulation/?infra_id={infra_id}&exception_key=created_exception_key").as_str(),
2172+
format!(
2173+
"/train_schedules/{train_schedule_id}/simulation/?infra_id={infra_id}&exception_id={}",
2174+
exception.id
2175+
)
2176+
.as_str(),
21602177
);
21612178
let response: simulation::Response = app
21622179
.fetch(request)
@@ -2179,7 +2196,7 @@ mod tests {
21792196

21802197
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
21812198
async fn paced_train_simulation_not_found() {
2182-
let (app, infra_id, _paced_train_id) =
2199+
let (app, infra_id, _paced_train_id, _exception) =
21832200
app_infra_id_paced_train_id_for_simulation_tests().await;
21842201
let request =
21852202
app.get(format!("/train_schedules/{}/simulation/?infra_id={}", 0, infra_id).as_str());
@@ -2195,7 +2212,7 @@ mod tests {
21952212

21962213
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
21972214
async fn paced_train_simulation_summary() {
2198-
let (app, infra_id, paced_train_id) =
2215+
let (app, infra_id, paced_train_id, _exception) =
21992216
app_infra_id_paced_train_id_for_simulation_tests().await;
22002217
let request = app.get(format!("/train_schedules/{paced_train_id}").as_str());
22012218
let mut paced_train_response: TrainScheduleResponse = app
@@ -2299,7 +2316,7 @@ mod tests {
22992316

23002317
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
23012318
async fn paced_train_simulation_summary_not_found() {
2302-
let (app, infra_id, _paced_train_id) =
2319+
let (app, infra_id, _paced_train_id, _exception) =
23032320
app_infra_id_paced_train_id_for_simulation_tests().await;
23042321
let request = app
23052322
.post("/train_schedules/simulation_summary")
@@ -2632,8 +2649,8 @@ mod tests {
26322649
}
26332650

26342651
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
2635-
async fn train_schedule_occupancy_blocks() {
2636-
let (app, infra_id, paced_train_id) =
2652+
async fn paced_train_occupancy_blocks() {
2653+
let (app, infra_id, paced_train_id, _exception) =
26372654
app_infra_id_paced_train_id_for_simulation_tests().await;
26382655

26392656
let request = app.get(format!("/train_schedules/{paced_train_id}").as_str());

front/src/applications/operationalStudies/hooks/useSimulationResults.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ const useSimulationResults = (): {
9595
id: selectedTrainId,
9696
infraId,
9797
electricalProfileSetId,
98-
exceptionKey: exception?.key,
98+
exceptionId: exception?.id ?? undefined,
9999
}
100100
: skipToken
101101
);

front/src/common/api/generatedEditoastApi.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,7 +1353,7 @@ const injectedRtkApi = api
13531353
params: {
13541354
infra_id: queryArg.infraId,
13551355
electrical_profile_set_id: queryArg.electricalProfileSetId,
1356-
exception_key: queryArg.exceptionKey,
1356+
exception_id: queryArg.exceptionId,
13571357
},
13581358
}),
13591359
providesTags: ['train_schedule'],
@@ -2631,7 +2631,7 @@ export type GetTrainSchedulesByIdSimulationApiArg = {
26312631
id: number;
26322632
infraId: number;
26332633
electricalProfileSetId?: number;
2634-
exceptionKey?: string;
2634+
exceptionId?: number;
26352635
};
26362636
export type GetVersionApiResponse = /** status 200 Return the service version */ Version;
26372637
export type GetVersionApiArg = void;

front/src/common/api/osrdEditoastApi.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,10 @@ const osrdEditoastApi = generatedEditoastApi
9696
}),
9797
getTrainSimulation: builder.query<
9898
SimulationResponse,
99-
{ id: TrainId; infraId: number; electricalProfileSetId?: number; exceptionKey?: string }
99+
{ id: TrainId; infraId: number; electricalProfileSetId?: number; exceptionId?: number }
100100
>({
101101
queryFn: async (
102-
{ id: trainId, infraId, electricalProfileSetId, exceptionKey },
102+
{ id: trainId, infraId, electricalProfileSetId, exceptionId },
103103
{ dispatch }
104104
) => {
105105
const pacedTrainId = isOccurrenceId(trainId)
@@ -111,7 +111,7 @@ const osrdEditoastApi = generatedEditoastApi
111111
id: extractEditoastIdFromPacedTrainId(pacedTrainId),
112112
infraId,
113113
electricalProfileSetId,
114-
exceptionKey,
114+
exceptionId,
115115
},
116116
{ subscribe: false }
117117
)

tests/tests/test_paced_train.py

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,10 @@ def test_get_paced_train_with_exception_path(
159159

160160

161161
def test_get_paced_train_with_exception_simulation(
162-
west_to_south_east_paced_train: Sequence[Any], small_infra: Infra, session: Session
162+
west_to_south_east_paced_train: Sequence[Any],
163+
small_infra: Infra,
164+
timetable_id: int,
165+
session: Session,
163166
):
164167
paced_train = west_to_south_east_paced_train[0]
165168
paced_train_id = paced_train["id"]
@@ -170,36 +173,42 @@ def test_get_paced_train_with_exception_simulation(
170173
).json()
171174

172175
# Add exception to the paced train
173-
exception = {
176+
exception_payload = {
174177
"key": "exception_key",
178+
"train_schedule_id": paced_train_id,
175179
"disabled": False,
176-
"path_and_schedule": {
177-
"path": [
178-
{
179-
"id": "id1",
180-
"deleted": False,
181-
"location": {"track": "TA0", "offset": 470000},
182-
},
183-
{
184-
"id": "id2",
185-
"deleted": False,
186-
"location": {"track": "TG4", "offset": 1993000},
180+
"change_groups": {
181+
"path_and_schedule": {
182+
"power_restrictions": [],
183+
"schedule": [],
184+
"path": [
185+
{
186+
"id": "id1",
187+
"deleted": False,
188+
"location": {"track": "TA0", "offset": 470000},
189+
},
190+
{
191+
"id": "id2",
192+
"deleted": False,
193+
"location": {"track": "TG4", "offset": 1993000},
194+
},
195+
],
196+
"margins": {
197+
"boundaries": [],
198+
"values": ["5%"],
187199
},
188-
],
189-
"schedule": [],
190-
"margins": {"boundaries": [], "values": ["0%"]},
191-
"power_restrictions": [],
200+
},
201+
"initial_speed": {"value": 20.0},
192202
},
193-
"initial_speed": {"value": 20.0},
194203
}
195-
paced_train["paced"]["exceptions"] = [exception]
196-
update_response = session.put(
197-
f"{EDITOAST_URL}train_schedules/{paced_train_id}", json=paced_train
198-
)
199-
assert update_response.status_code == 204
204+
exception = session.post(
205+
f"{EDITOAST_URL}timetable/{timetable_id}/train_schedule_exception",
206+
json=exception_payload,
207+
).json()
208+
200209
# Get exception path
201210
exception_simulation_result = session.get(
202-
f"{EDITOAST_URL}train_schedules/{paced_train_id}/simulation?infra_id={small_infra.id}&exception_key=exception_key"
211+
f"{EDITOAST_URL}train_schedules/{paced_train_id}/simulation?infra_id={small_infra.id}&exception_id={exception['id']}"
203212
).json()
204213

205214
# Check if the response is different from paced train

0 commit comments

Comments
 (0)