Skip to content

Commit 2565b9a

Browse files
committed
editoast: adapt simulation endpoint with new exceptions
Signed-off-by: Egor <egor@berezify.fr>
1 parent 0d8a0a0 commit 2565b9a

File tree

6 files changed

+109
-82
lines changed

6 files changed

+109
-82
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: 67 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ pub struct ElectricalProfileSetIdQueryParam {
503503
#[utoipa::path(
504504
get, path = "",
505505
tag = "paced_train",
506-
params(TrainScheduleIdParam, InfraIdQueryParam, ElectricalProfileSetIdQueryParam, ExceptionQueryParam),
506+
params(TrainScheduleIdParam, InfraIdQueryParam, ElectricalProfileSetIdQueryParam, TrainScheduleExceptionQueryParam),
507507
responses(
508508
(status = 200, description = "Simulation Output", body = simulation::Response),
509509
),
@@ -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
};
@@ -1614,6 +1619,7 @@ mod tests {
16141619
use core_client::simulation::ReportTrain;
16151620
use core_client::simulation::SpeedLimitProperties;
16161621
use database::DbConnectionPoolV2;
1622+
use editoast_models::TrainScheduleException;
16171623
use editoast_models::prelude::*;
16181624
use editoast_models::rolling_stock::TrainMainCategory;
16191625
use pretty_assertions::assert_eq;
@@ -1978,12 +1984,16 @@ mod tests {
19781984
assert_eq!(response.train_schedule, paced_train.into());
19791985
}
19801986

1981-
async fn app_infra_id_paced_train_id_for_simulation_tests() -> (TestApp, i64, i64) {
1987+
async fn app_infra_id_paced_train_id_for_simulation_tests()
1988+
-> (TestApp, i64, i64, TrainScheduleException) {
19821989
let db_pool = DbConnectionPoolV2::for_tests();
19831990
let small_infra = create_small_infra(&mut db_pool.get_ok()).await;
1984-
let train_schedule_set = create_train_schedule_set(&mut db_pool.get_ok()).await;
19851991
let rolling_stock =
19861992
create_fast_rolling_stock(&mut db_pool.get_ok(), "simulation_rolling_stock").await;
1993+
let (timetable, train_schedule_set) =
1994+
create_timetable_with_train_schedule_set(&mut db_pool.get_ok()).await;
1995+
let exception = create_created_exception_with_change_groups("created_exception_key");
1996+
19871997
let paced_train_base = TrainSchedule {
19881998
train_occurrence: TrainOccurrence {
19891999
rolling_stock_name: rolling_stock.name.clone(),
@@ -1992,9 +2002,7 @@ mod tests {
19922002
paced: Some(Paced {
19932003
time_window: Duration::hours(1).try_into().unwrap(),
19942004
interval: Duration::minutes(15).try_into().unwrap(),
1995-
exceptions: vec![create_created_exception_with_change_groups(
1996-
"created_exception_key",
1997-
)],
2005+
exceptions: vec![],
19982006
}),
19992007
};
20002008
let paced_train: TrainScheduleChangeset = paced_train_base.into();
@@ -2003,17 +2011,28 @@ mod tests {
20032011
.create(&mut db_pool.get_ok())
20042012
.await
20052013
.expect("Failed to create paced train");
2014+
2015+
let exception = create_train_schedule_exception(
2016+
&mut db_pool.get_ok(),
2017+
timetable.id,
2018+
paced_train.id,
2019+
None,
2020+
Some("created_exception_key".to_string()),
2021+
Some(exception.change_groups),
2022+
)
2023+
.await;
2024+
20062025
let core = mocked_core_pathfinding_sim_and_proj();
20072026
let app = TestAppBuilder::new()
20082027
.db_pool(db_pool)
20092028
.core_client(core.into())
20102029
.build();
2011-
(app, small_infra.id, paced_train.id)
2030+
(app, small_infra.id, paced_train.id, exception)
20122031
}
20132032

20142033
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
20152034
async fn paced_train_simulation() {
2016-
let (app, infra_id, train_schedule_id) =
2035+
let (app, infra_id, train_schedule_id, _exception) =
20172036
app_infra_id_paced_train_id_for_simulation_tests().await;
20182037
let request = app.get(
20192038
format!("/train_schedules/{train_schedule_id}/simulation/?infra_id={infra_id}")
@@ -2030,11 +2049,11 @@ mod tests {
20302049

20312050
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
20322051
async fn paced_train_exception_simulation_with_invalid_exception_key() {
2033-
let (app, infra_id, train_schedule_id) =
2052+
let (app, infra_id, train_schedule_id, _exception) =
20342053
app_infra_id_paced_train_id_for_simulation_tests().await;
20352054
let request = app.get(
20362055
format!(
2037-
"/train_schedules/{train_schedule_id}/simulation/?infra_id={infra_id}&exception_key=toto"
2056+
"/train_schedules/{train_schedule_id}/simulation/?infra_id={infra_id}&exception_id=9999"
20382057
)
20392058
.as_str(),
20402059
);
@@ -2052,10 +2071,14 @@ mod tests {
20522071

20532072
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
20542073
async fn paced_train_exception_simulation() {
2055-
let (app, infra_id, train_schedule_id) =
2074+
let (app, infra_id, train_schedule_id, exception) =
20562075
app_infra_id_paced_train_id_for_simulation_tests().await;
20572076
let request = app.get(
2058-
format!("/train_schedules/{train_schedule_id}/simulation/?infra_id={infra_id}&exception_key=created_exception_key").as_str(),
2077+
format!(
2078+
"/train_schedules/{train_schedule_id}/simulation/?infra_id={infra_id}&exception_id={}",
2079+
exception.id
2080+
)
2081+
.as_str(),
20592082
);
20602083
let response: simulation::Response = app
20612084
.fetch(request)
@@ -2108,34 +2131,28 @@ mod tests {
21082131
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
21092132
async fn paced_train_exception_simulation_with_rolling_stock_not_found() {
21102133
// GIVEN
2111-
let (app, infra_id, train_schedule_id) =
2134+
let (app, infra_id, train_schedule_id, exception) =
21122135
app_infra_id_paced_train_id_for_simulation_tests().await;
2113-
let request = app.get(format!("/train_schedules/{train_schedule_id}").as_str());
2114-
let mut paced_train_response: TrainScheduleResponse = app
2115-
.fetch(request)
2116-
.await
2117-
.assert_status(StatusCode::OK)
2118-
.json_into();
2119-
paced_train_response
2120-
.train_schedule
2121-
.paced
2122-
.as_mut()
2123-
.unwrap()
2124-
.exceptions[0]
2125-
.change_groups
2126-
.rolling_stock = Some(RollingStockChangeGroup {
2136+
2137+
let mut change_groupe = exception.change_groups;
2138+
change_groupe.rolling_stock = Some(RollingStockChangeGroup {
21272139
rolling_stock_name: "R2D2".into(),
21282140
comfort: Comfort::AirConditioning,
21292141
});
2130-
let request = app
2131-
.put(format!("/train_schedules/{train_schedule_id}").as_str())
2132-
.json(&json!(paced_train_response.train_schedule));
2133-
app.fetch(request)
2142+
let exception = editoast_models::TrainScheduleException::changeset()
2143+
.change_groups(change_groupe)
2144+
.update(&mut app.db_pool().get_ok(), train_schedule_id)
21342145
.await
2135-
.assert_status(StatusCode::NO_CONTENT);
2146+
.expect("Fail to update exception")
2147+
.expect("Fail to update exception");
2148+
21362149
// WHEN
21372150
let request = app.get(
2138-
format!("/train_schedules/{train_schedule_id}/simulation/?infra_id={infra_id}&exception_key=created_exception_key").as_str(),
2151+
format!(
2152+
"/train_schedules/{train_schedule_id}/simulation/?infra_id={infra_id}&exception_id={}",
2153+
exception.id
2154+
)
2155+
.as_str(),
21392156
);
21402157
let response: simulation::Response = app
21412158
.fetch(request)
@@ -2158,7 +2175,7 @@ mod tests {
21582175

21592176
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
21602177
async fn paced_train_simulation_not_found() {
2161-
let (app, infra_id, _paced_train_id) =
2178+
let (app, infra_id, _paced_train_id, _exception) =
21622179
app_infra_id_paced_train_id_for_simulation_tests().await;
21632180
let request =
21642181
app.get(format!("/train_schedules/{}/simulation/?infra_id={}", 0, infra_id).as_str());
@@ -2174,7 +2191,7 @@ mod tests {
21742191

21752192
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
21762193
async fn paced_train_simulation_summary() {
2177-
let (app, infra_id, paced_train_id) =
2194+
let (app, infra_id, paced_train_id, _exception) =
21782195
app_infra_id_paced_train_id_for_simulation_tests().await;
21792196
let request = app.get(format!("/train_schedules/{paced_train_id}").as_str());
21802197
let mut paced_train_response: TrainScheduleResponse = app
@@ -2278,7 +2295,7 @@ mod tests {
22782295

22792296
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
22802297
async fn paced_train_simulation_summary_not_found() {
2281-
let (app, infra_id, _paced_train_id) =
2298+
let (app, infra_id, _paced_train_id, _exception) =
22822299
app_infra_id_paced_train_id_for_simulation_tests().await;
22832300
let request = app
22842301
.post("/train_schedules/simulation_summary")
@@ -2605,8 +2622,8 @@ mod tests {
26052622
}
26062623

26072624
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
2608-
async fn train_schedule_occupancy_blocks() {
2609-
let (app, infra_id, paced_train_id) =
2625+
async fn paced_train_occupancy_blocks() {
2626+
let (app, infra_id, paced_train_id, _exception) =
26102627
app_infra_id_paced_train_id_for_simulation_tests().await;
26112628

26122629
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
@@ -91,7 +91,7 @@ const useSimulationResults = (): {
9191
id: selectedTrainId,
9292
infraId,
9393
electricalProfileSetId,
94-
exceptionKey: exception?.key,
94+
exceptionId: exception?.id ?? undefined,
9595
}
9696
: skipToken
9797
);

front/src/common/api/generatedEditoastApi.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1354,7 +1354,7 @@ const injectedRtkApi = api
13541354
params: {
13551355
infra_id: queryArg.infraId,
13561356
electrical_profile_set_id: queryArg.electricalProfileSetId,
1357-
exception_key: queryArg.exceptionKey,
1357+
exception_id: queryArg.exceptionId,
13581358
},
13591359
}),
13601360
providesTags: ['paced_train'],
@@ -2632,7 +2632,7 @@ export type GetTrainSchedulesByIdSimulationApiArg = {
26322632
id: number;
26332633
infraId: number;
26342634
electricalProfileSetId?: number;
2635-
exceptionKey?: string;
2635+
exceptionId?: number;
26362636
};
26372637
export type GetVersionApiResponse = /** status 200 Return the service version */ Version;
26382638
export type GetVersionApiArg = void;

front/src/common/api/osrdEditoastApi.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,10 @@ const osrdEditoastApi = generatedEditoastApi
9797
}),
9898
getTrainSimulation: builder.query<
9999
SimulationResponse,
100-
{ id: TrainId; infraId: number; electricalProfileSetId?: number; exceptionKey?: string }
100+
{ id: TrainId; infraId: number; electricalProfileSetId?: number; exceptionId?: number }
101101
>({
102102
queryFn: async (
103-
{ id: trainId, infraId, electricalProfileSetId, exceptionKey },
103+
{ id: trainId, infraId, electricalProfileSetId, exceptionId },
104104
{ dispatch }
105105
) => {
106106
const pacedTrainId = isOccurrenceId(trainId)
@@ -112,7 +112,7 @@ const osrdEditoastApi = generatedEditoastApi
112112
id: extractEditoastIdFromPacedTrainId(pacedTrainId),
113113
infraId,
114114
electricalProfileSetId,
115-
exceptionKey,
115+
exceptionId,
116116
},
117117
{ subscribe: false }
118118
)

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)