@@ -245,6 +245,7 @@ pub(in crate::views) async fn delete(
245245#[ derive( Debug , Clone , Deserialize , ToSchema ) ]
246246pub ( in crate :: views) struct SimulationBatchForm {
247247 infra_id : i64 ,
248+ timetable_id : i64 ,
248249 electrical_profile_set_id : Option < i64 > ,
249250 ids : HashSet < i64 > ,
250251}
@@ -287,6 +288,7 @@ pub(in crate::views) async fn simulation_summary(
287288 Extension ( auth) : AuthenticationExt ,
288289 Json ( SimulationBatchForm {
289290 infra_id,
291+ timetable_id,
290292 electrical_profile_set_id,
291293 ids : paced_train_ids,
292294 } ) : Json < SimulationBatchForm > ,
@@ -315,31 +317,46 @@ pub(in crate::views) async fn simulation_summary(
315317 . await ?;
316318
317319 let paced_trains: Vec < models:: TrainSchedule > =
318- models:: TrainSchedule :: retrieve_batch_or_fail ( conn, paced_train_ids, |missing| {
320+ models:: TrainSchedule :: retrieve_batch_or_fail ( conn, paced_train_ids. clone ( ) , |missing| {
319321 TrainScheduleError :: BatchNotFound {
320322 count : missing. len ( ) ,
321323 }
322324 } )
323325 . await ?;
324326
325- let simulation_contexts: Vec < SimulationContext > =
326- paced_trains
327- . iter ( )
328- . flat_map ( |paced_train| {
329- std:: iter:: once ( SimulationContext {
330- paced_train_id : paced_train. id ,
331- exception_key : None ,
332- train_schedule : paced_train. clone ( ) . into_train_occurrence ( ) ,
333- } )
334- . chain ( paced_train. exceptions . iter ( ) . map ( |exception| {
335- SimulationContext {
336- paced_train_id : paced_train. id ,
337- exception_key : Some ( exception. key . clone ( ) ) ,
338- train_schedule : paced_train. apply_exception ( exception) ,
339- }
340- } ) )
327+ let mut exceptions = TrainScheduleException :: retrieve_exceptions_by_train_schedules (
328+ conn,
329+ timetable_id,
330+ paced_train_ids. into_iter ( ) . collect ( ) ,
331+ )
332+ . await ?
333+ . into_iter ( )
334+ . map_into :: < schemas:: TrainScheduleException > ( )
335+ . into_group_map_by ( |e| e. train_schedule_id ) ;
336+
337+ let simulation_contexts: Vec < SimulationContext > = paced_trains
338+ . iter ( )
339+ . flat_map ( |paced_train| {
340+ let ts_exceptions = exceptions. remove ( & paced_train. id ) . unwrap_or_default ( ) ;
341+ std:: iter:: once ( SimulationContext {
342+ paced_train_id : paced_train. id ,
343+ exception_key : None ,
344+ train_schedule : paced_train. clone ( ) . into_train_occurrence ( ) ,
341345 } )
342- . collect ( ) ;
346+ . chain (
347+ ts_exceptions
348+ . into_iter ( )
349+ . map ( |exception| {
350+ SimulationContext {
351+ paced_train_id : paced_train. id ,
352+ exception_key : exception. key . clone ( ) , // TODO use exception.id
353+ train_schedule : paced_train. apply_train_schedule_exception ( & exception) ,
354+ }
355+ } )
356+ . collect :: < Vec < _ > > ( ) ,
357+ )
358+ } )
359+ . collect ( ) ;
343360
344361 let schedules: Vec < schemas:: TrainOccurrence > = simulation_contexts
345362 . iter ( )
@@ -398,12 +415,6 @@ pub(in crate::views) async fn simulation_summary(
398415#[ derive( Debug , Default , Clone , Serialize , Deserialize , IntoParams , ToSchema ) ]
399416#[ into_params( parameter_in = Query ) ]
400417pub ( in crate :: views) struct ExceptionQueryParam {
401- exception_key : Option < String > ,
402- }
403-
404- #[ derive( Debug , Default , Clone , Serialize , Deserialize , IntoParams , ToSchema ) ]
405- #[ into_params( parameter_in = Query ) ]
406- pub ( in crate :: views) struct TrainScheduleExceptionQueryParam {
407418 exception_id : Option < i64 > ,
408419}
409420
@@ -431,9 +442,7 @@ pub(in crate::views) async fn get_path(
431442 id : train_schedule_id,
432443 } ) : Path < TrainScheduleIdParam > ,
433444 Query ( InfraIdQueryParam { infra_id } ) : Query < InfraIdQueryParam > ,
434- Query ( TrainScheduleExceptionQueryParam { exception_id } ) : Query <
435- TrainScheduleExceptionQueryParam ,
436- > ,
445+ Query ( ExceptionQueryParam { exception_id } ) : Query < ExceptionQueryParam > ,
437446) -> Result < Json < PathfindingResult > > {
438447 let authorized = auth
439448 . check_roles ( [ authz:: Role :: OperationalStudies , authz:: Role :: Stdcm ] . into ( ) )
@@ -524,9 +533,7 @@ pub(in crate::views) async fn simulation(
524533 Query ( ElectricalProfileSetIdQueryParam {
525534 electrical_profile_set_id,
526535 } ) : Query < ElectricalProfileSetIdQueryParam > ,
527- Query ( TrainScheduleExceptionQueryParam { exception_id } ) : Query <
528- TrainScheduleExceptionQueryParam ,
529- > ,
536+ Query ( ExceptionQueryParam { exception_id } ) : Query < ExceptionQueryParam > ,
530537) -> Result < Json < simulation:: Response > > {
531538 let authorized = auth
532539 . check_roles ( [ authz:: Role :: OperationalStudies ] . into ( ) )
@@ -620,9 +627,7 @@ pub(in crate::views) async fn etcs_braking_curves(
620627 Query ( ElectricalProfileSetIdQueryParam {
621628 electrical_profile_set_id,
622629 } ) : Query < ElectricalProfileSetIdQueryParam > ,
623- Query ( TrainScheduleExceptionQueryParam { exception_id } ) : Query <
624- TrainScheduleExceptionQueryParam ,
625- > ,
630+ Query ( ExceptionQueryParam { exception_id } ) : Query < ExceptionQueryParam > ,
626631) -> Result < Json < core_client:: etcs_braking_curves:: Response > > {
627632 let authorized = auth
628633 . check_roles ( [ authz:: Role :: OperationalStudies , authz:: Role :: Stdcm ] . into ( ) )
@@ -1692,6 +1697,7 @@ mod tests {
16921697 use crate :: models:: fixtures:: create_paced_train_with_exceptions;
16931698 use crate :: models:: fixtures:: create_simple_paced_train;
16941699 use crate :: models:: fixtures:: create_small_infra;
1700+ use crate :: models:: fixtures:: create_timetable;
16951701 use crate :: models:: fixtures:: create_timetable_with_train_schedule_set;
16961702 use crate :: models:: fixtures:: create_train_schedule_exception;
16971703 use crate :: models:: fixtures:: create_train_schedule_set;
@@ -2228,66 +2234,72 @@ mod tests {
22282234
22292235 #[ tokio:: test( flavor = "multi_thread" , worker_threads = 1 ) ]
22302236 async fn paced_train_simulation_summary ( ) {
2231- let ( app, infra_id, paced_train_id, _exception) =
2232- app_infra_id_paced_train_id_for_simulation_tests ( ) . await ;
2233- let request = app. get ( format ! ( "/train_schedules/{paced_train_id}" ) . as_str ( ) ) ;
2234- let mut paced_train_response: TrainScheduleResponse = app
2235- . fetch ( request)
2237+ // Setup tests tools
2238+ let core = mocked_core_pathfinding_sim_and_proj ( ) ;
2239+ let app = TestAppBuilder :: new ( )
2240+ . db_pool ( DbConnectionPoolV2 :: for_tests ( ) )
2241+ . core_client ( core. into ( ) )
2242+ . build ( ) ;
2243+ let db_pool = app. db_pool ( ) ;
2244+
2245+ // Setup tests data
2246+ let small_infra = create_small_infra ( & mut db_pool. get_ok ( ) ) . await ;
2247+
2248+ let ( timetable, train_schedule_set) =
2249+ create_timetable_with_train_schedule_set ( & mut db_pool. get_ok ( ) ) . await ;
2250+ create_fast_rolling_stock ( & mut db_pool. get_ok ( ) , "R2D2" ) . await ;
2251+
2252+ let train_schedule = TrainSchedule {
2253+ train_occurrence : schemas:: TrainOccurrence :: fake ( ) ,
2254+ paced : Some ( Paced {
2255+ time_window : Duration :: hours ( 1 ) . try_into ( ) . unwrap ( ) ,
2256+ interval : Duration :: minutes ( 15 ) . try_into ( ) . unwrap ( ) ,
2257+ exceptions : vec ! [ ] ,
2258+ } ) ,
2259+ } ;
2260+ let train_schedule: TrainScheduleChangeset = train_schedule. into ( ) ;
2261+ let train_schedule = train_schedule
2262+ . train_schedule_set_id ( train_schedule_set. id )
2263+ . create ( & mut db_pool. get_ok ( ) )
22362264 . await
2237- . assert_status ( StatusCode :: OK )
2238- . json_into ( ) ;
2239- // First remove all already generated exceptions
2240- paced_train_response
2241- . train_schedule
2242- . paced
2243- . as_mut ( )
2244- . unwrap ( )
2245- . exceptions
2246- . clear ( ) ;
2265+ . expect ( "Failed to create train schedule" ) ;
2266+
22472267 // Add one exception which will not change the simulation from base
2248- paced_train_response
2249- . train_schedule
2250- . paced
2251- . as_mut ( )
2252- . unwrap ( )
2253- . exceptions
2254- . push ( PacedTrainException {
2255- key : "change_train_name" . to_string ( ) ,
2256- change_groups : TrainScheduleExceptionChangeGroups {
2257- train_name : Some ( TrainNameChangeGroup {
2258- value : "exception_name_but_same_simulation" . into ( ) ,
2259- } ) ,
2260- ..Default :: default ( )
2261- } ,
2268+ let _change_train_name_exception = create_train_schedule_exception (
2269+ & mut db_pool. get_ok ( ) ,
2270+ timetable. id ,
2271+ train_schedule. id ,
2272+ None ,
2273+ Some ( "change_train_name" . to_string ( ) ) ,
2274+ Some ( TrainScheduleExceptionChangeGroups {
2275+ train_name : Some ( TrainNameChangeGroup {
2276+ value : "exception_name_but_same_simulation" . into ( ) ,
2277+ } ) ,
22622278 ..Default :: default ( )
2263- } ) ;
2279+ } ) ,
2280+ )
2281+ . await ;
2282+
22642283 // Add one exception which will change the simulation from base
2265- // and therefore add another entry in the response (field `exceptions`)
2266- paced_train_response
2267- . train_schedule
2268- . paced
2269- . as_mut ( )
2270- . unwrap ( )
2271- . exceptions
2272- . push ( PacedTrainException {
2273- key : "change_initial_speed" . to_string ( ) ,
2274- change_groups : TrainScheduleExceptionChangeGroups {
2275- initial_speed : Some ( InitialSpeedChangeGroup { value : 1.23 } ) ,
2276- ..Default :: default ( )
2277- } ,
2284+ let _change_train_name_exception = create_train_schedule_exception (
2285+ & mut db_pool. get_ok ( ) ,
2286+ timetable. id ,
2287+ train_schedule. id ,
2288+ None ,
2289+ Some ( "change_initial_speed" . to_string ( ) ) ,
2290+ Some ( TrainScheduleExceptionChangeGroups {
2291+ initial_speed : Some ( InitialSpeedChangeGroup { value : 1.23 } ) ,
22782292 ..Default :: default ( )
2279- } ) ;
2280- let request = app
2281- . put ( format ! ( "/train_schedules/{paced_train_id}" ) . as_str ( ) )
2282- . json ( & json ! ( paced_train_response. train_schedule) ) ;
2283- app. fetch ( request)
2284- . await
2285- . assert_status ( StatusCode :: NO_CONTENT ) ;
2293+ } ) ,
2294+ )
2295+ . await ;
2296+
22862297 let request = app
22872298 . post ( "/train_schedules/simulation_summary" )
22882299 . json ( & json ! ( {
2289- "infra_id" : infra_id,
2290- "ids" : vec![ paced_train_id] ,
2300+ "infra_id" : small_infra. id,
2301+ "timetable_id" : timetable. id,
2302+ "ids" : vec![ train_schedule. id] ,
22912303 } ) ) ;
22922304
22932305 let response: HashMap < i64 , TrainScheduleSummaryResponse > = app
@@ -2297,7 +2309,7 @@ mod tests {
22972309 . json_into ( ) ;
22982310 assert_eq ! ( response. len( ) , 1 ) ;
22992311 assert_eq ! (
2300- * response. get( & paced_train_id ) . unwrap( ) ,
2312+ * response. get( & train_schedule . id ) . unwrap( ) ,
23012313 TrainScheduleSummaryResponse {
23022314 train_schedule: SummaryResponse :: Success {
23032315 length: 15_050_000 ,
@@ -2334,10 +2346,12 @@ mod tests {
23342346 async fn paced_train_simulation_summary_not_found ( ) {
23352347 let ( app, infra_id, _paced_train_id, _exception) =
23362348 app_infra_id_paced_train_id_for_simulation_tests ( ) . await ;
2349+ let timetable = create_timetable ( & mut app. db_pool ( ) . get_ok ( ) ) . await ;
23372350 let request = app
23382351 . post ( "/train_schedules/simulation_summary" )
23392352 . json ( & json ! ( {
23402353 "infra_id" : infra_id,
2354+ "timetable_id" : timetable. id,
23412355 "ids" : vec![ 0 ] ,
23422356 } ) ) ;
23432357 let response: InternalError = app
0 commit comments