@@ -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
@@ -412,7 +423,7 @@ pub(in crate::views) struct TrainScheduleExceptionQueryParam {
412423#[ utoipa:: path(
413424 get, path = "" ,
414425 tags = [ "paced_train" , "pathfinding" ] ,
415- params( TrainScheduleIdParam , InfraIdQueryParam , TrainScheduleExceptionQueryParam ) ,
426+ params( TrainScheduleIdParam , InfraIdQueryParam , ExceptionQueryParam ) ,
416427 responses(
417428 ( status = 200 , description = "The path" , body = PathfindingResult ) ,
418429 ( status = 404 , description = "Infrastructure or Train schedule not found" )
@@ -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 ( ) )
@@ -503,7 +512,7 @@ pub struct ElectricalProfileSetIdQueryParam {
503512#[ utoipa:: path(
504513 get, path = "" ,
505514 tag = "paced_train" ,
506- params( TrainScheduleIdParam , InfraIdQueryParam , ElectricalProfileSetIdQueryParam , TrainScheduleExceptionQueryParam ) ,
515+ params( TrainScheduleIdParam , InfraIdQueryParam , ElectricalProfileSetIdQueryParam , ExceptionQueryParam ) ,
507516 responses(
508517 ( status = 200 , description = "Simulation Output" , body = simulation:: Response ) ,
509518 ) ,
@@ -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 ( ) )
@@ -597,7 +604,7 @@ pub(in crate::views) async fn simulation(
597604#[ utoipa:: path(
598605 get, path = "" ,
599606 tags = [ "paced_train" , "etcs_braking_curves" ] ,
600- params( TrainScheduleIdParam , InfraIdQueryParam , ElectricalProfileSetIdQueryParam , TrainScheduleExceptionQueryParam ) ,
607+ params( TrainScheduleIdParam , InfraIdQueryParam , ElectricalProfileSetIdQueryParam , ExceptionQueryParam ) ,
601608 responses(
602609 ( status = 200 , description = "ETCS Braking Curves Output" , body = core_client:: etcs_braking_curves:: Response ) ,
603610 ) ,
@@ -618,9 +625,7 @@ pub(in crate::views) async fn etcs_braking_curves(
618625 Query ( ElectricalProfileSetIdQueryParam {
619626 electrical_profile_set_id,
620627 } ) : Query < ElectricalProfileSetIdQueryParam > ,
621- Query ( TrainScheduleExceptionQueryParam { exception_id } ) : Query <
622- TrainScheduleExceptionQueryParam ,
623- > ,
628+ Query ( ExceptionQueryParam { exception_id } ) : Query < ExceptionQueryParam > ,
624629) -> Result < Json < core_client:: etcs_braking_curves:: Response > > {
625630 let authorized = auth
626631 . check_roles ( [ authz:: Role :: OperationalStudies , authz:: Role :: Stdcm ] . into ( ) )
@@ -1671,6 +1676,7 @@ mod tests {
16711676 use crate :: models:: fixtures:: create_paced_train_with_exceptions;
16721677 use crate :: models:: fixtures:: create_simple_paced_train;
16731678 use crate :: models:: fixtures:: create_small_infra;
1679+ use crate :: models:: fixtures:: create_timetable;
16741680 use crate :: models:: fixtures:: create_timetable_with_train_schedule_set;
16751681 use crate :: models:: fixtures:: create_train_schedule_exception;
16761682 use crate :: models:: fixtures:: create_train_schedule_set;
@@ -2207,66 +2213,72 @@ mod tests {
22072213
22082214 #[ tokio:: test( flavor = "multi_thread" , worker_threads = 1 ) ]
22092215 async fn paced_train_simulation_summary ( ) {
2210- let ( app, infra_id, paced_train_id, _exception) =
2211- app_infra_id_paced_train_id_for_simulation_tests ( ) . await ;
2212- let request = app. get ( format ! ( "/train_schedules/{paced_train_id}" ) . as_str ( ) ) ;
2213- let mut paced_train_response: TrainScheduleResponse = app
2214- . fetch ( request)
2216+ // Setup tests tools
2217+ let core = mocked_core_pathfinding_sim_and_proj ( ) ;
2218+ let app = TestAppBuilder :: new ( )
2219+ . db_pool ( DbConnectionPoolV2 :: for_tests ( ) )
2220+ . core_client ( core. into ( ) )
2221+ . build ( ) ;
2222+ let db_pool = app. db_pool ( ) ;
2223+
2224+ // Setup tests data
2225+ let small_infra = create_small_infra ( & mut db_pool. get_ok ( ) ) . await ;
2226+
2227+ let ( timetable, train_schedule_set) =
2228+ create_timetable_with_train_schedule_set ( & mut db_pool. get_ok ( ) ) . await ;
2229+ create_fast_rolling_stock ( & mut db_pool. get_ok ( ) , "R2D2" ) . await ;
2230+
2231+ let train_schedule = TrainSchedule {
2232+ train_occurrence : schemas:: TrainOccurrence :: fake ( ) ,
2233+ paced : Some ( Paced {
2234+ time_window : Duration :: hours ( 1 ) . try_into ( ) . unwrap ( ) ,
2235+ interval : Duration :: minutes ( 15 ) . try_into ( ) . unwrap ( ) ,
2236+ exceptions : vec ! [ ] ,
2237+ } ) ,
2238+ } ;
2239+ let train_schedule: TrainScheduleChangeset = train_schedule. into ( ) ;
2240+ let train_schedule = train_schedule
2241+ . train_schedule_set_id ( train_schedule_set. id )
2242+ . create ( & mut db_pool. get_ok ( ) )
22152243 . await
2216- . assert_status ( StatusCode :: OK )
2217- . json_into ( ) ;
2218- // First remove all already generated exceptions
2219- paced_train_response
2220- . train_schedule
2221- . paced
2222- . as_mut ( )
2223- . unwrap ( )
2224- . exceptions
2225- . clear ( ) ;
2244+ . expect ( "Failed to create train schedule" ) ;
2245+
22262246 // Add one exception which will not change the simulation from base
2227- paced_train_response
2228- . train_schedule
2229- . paced
2230- . as_mut ( )
2231- . unwrap ( )
2232- . exceptions
2233- . push ( PacedTrainException {
2234- key : "change_train_name" . to_string ( ) ,
2235- change_groups : TrainScheduleExceptionChangeGroups {
2236- train_name : Some ( TrainNameChangeGroup {
2237- value : "exception_name_but_same_simulation" . into ( ) ,
2238- } ) ,
2239- ..Default :: default ( )
2240- } ,
2247+ let _change_train_name_exception = create_train_schedule_exception (
2248+ & mut db_pool. get_ok ( ) ,
2249+ timetable. id ,
2250+ train_schedule. id ,
2251+ None ,
2252+ Some ( "change_train_name" . to_string ( ) ) ,
2253+ Some ( TrainScheduleExceptionChangeGroups {
2254+ train_name : Some ( TrainNameChangeGroup {
2255+ value : "exception_name_but_same_simulation" . into ( ) ,
2256+ } ) ,
22412257 ..Default :: default ( )
2242- } ) ;
2258+ } ) ,
2259+ )
2260+ . await ;
2261+
22432262 // Add one exception which will change the simulation from base
2244- // and therefore add another entry in the response (field `exceptions`)
2245- paced_train_response
2246- . train_schedule
2247- . paced
2248- . as_mut ( )
2249- . unwrap ( )
2250- . exceptions
2251- . push ( PacedTrainException {
2252- key : "change_initial_speed" . to_string ( ) ,
2253- change_groups : TrainScheduleExceptionChangeGroups {
2254- initial_speed : Some ( InitialSpeedChangeGroup { value : 1.23 } ) ,
2255- ..Default :: default ( )
2256- } ,
2263+ let _change_train_name_exception = create_train_schedule_exception (
2264+ & mut db_pool. get_ok ( ) ,
2265+ timetable. id ,
2266+ train_schedule. id ,
2267+ None ,
2268+ Some ( "change_initial_speed" . to_string ( ) ) ,
2269+ Some ( TrainScheduleExceptionChangeGroups {
2270+ initial_speed : Some ( InitialSpeedChangeGroup { value : 1.23 } ) ,
22572271 ..Default :: default ( )
2258- } ) ;
2259- let request = app
2260- . put ( format ! ( "/train_schedules/{paced_train_id}" ) . as_str ( ) )
2261- . json ( & json ! ( paced_train_response. train_schedule) ) ;
2262- app. fetch ( request)
2263- . await
2264- . assert_status ( StatusCode :: NO_CONTENT ) ;
2272+ } ) ,
2273+ )
2274+ . await ;
2275+
22652276 let request = app
22662277 . post ( "/train_schedules/simulation_summary" )
22672278 . json ( & json ! ( {
2268- "infra_id" : infra_id,
2269- "ids" : vec![ paced_train_id] ,
2279+ "infra_id" : small_infra. id,
2280+ "timetable_id" : timetable. id,
2281+ "ids" : vec![ train_schedule. id] ,
22702282 } ) ) ;
22712283
22722284 let response: HashMap < i64 , TrainScheduleSummaryResponse > = app
@@ -2276,7 +2288,7 @@ mod tests {
22762288 . json_into ( ) ;
22772289 assert_eq ! ( response. len( ) , 1 ) ;
22782290 assert_eq ! (
2279- * response. get( & paced_train_id ) . unwrap( ) ,
2291+ * response. get( & train_schedule . id ) . unwrap( ) ,
22802292 TrainScheduleSummaryResponse {
22812293 train_schedule: SummaryResponse :: Success {
22822294 length: 15_050_000 ,
@@ -2313,10 +2325,12 @@ mod tests {
23132325 async fn paced_train_simulation_summary_not_found ( ) {
23142326 let ( app, infra_id, _paced_train_id, _exception) =
23152327 app_infra_id_paced_train_id_for_simulation_tests ( ) . await ;
2328+ let timetable = create_timetable ( & mut app. db_pool ( ) . get_ok ( ) ) . await ;
23162329 let request = app
23172330 . post ( "/train_schedules/simulation_summary" )
23182331 . json ( & json ! ( {
23192332 "infra_id" : infra_id,
2333+ "timetable_id" : timetable. id,
23202334 "ids" : vec![ 0 ] ,
23212335 } ) ) ;
23222336 let response: InternalError = app
0 commit comments