diff --git a/editoast/editoast_schemas/src/train_schedule.rs b/editoast/editoast_schemas/src/train_schedule.rs index 650c6ac51bc..ec2051e9c00 100644 --- a/editoast/editoast_schemas/src/train_schedule.rs +++ b/editoast/editoast_schemas/src/train_schedule.rs @@ -7,6 +7,8 @@ pub use schedule_item::ReceptionSignal; pub use schedule_item::ScheduleItem; mod path_item; +pub use path_item::OperationalPointIdentifier; +pub use path_item::OperationalPointReference; pub use path_item::PathItem; pub use path_item::PathItemLocation; diff --git a/editoast/editoast_schemas/src/train_schedule/path_item.rs b/editoast/editoast_schemas/src/train_schedule/path_item.rs index 0ae69acb9ef..e73894a3a03 100644 --- a/editoast/editoast_schemas/src/train_schedule/path_item.rs +++ b/editoast/editoast_schemas/src/train_schedule/path_item.rs @@ -9,6 +9,7 @@ use crate::infra::TrackOffset; editoast_common::schemas! { PathItem, PathItemLocation, + OperationalPointReference, } /// A location on the path of a train @@ -32,6 +33,24 @@ pub struct PathItem { #[serde(untagged, deny_unknown_fields)] pub enum PathItemLocation { TrackOffset(#[schema(inline)] TrackOffset), + OperationalPointReference(#[schema(inline)] OperationalPointReference), +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema, Hash)] +pub struct OperationalPointReference { + #[serde(flatten)] + #[schema(inline)] + pub reference: OperationalPointIdentifier, + #[schema(inline)] + #[serde(default)] + pub track_id: Option, + #[serde(default)] + pub track_label: Option, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema, Hash)] +#[serde(untagged, deny_unknown_fields)] +pub enum OperationalPointIdentifier { OperationalPointId { /// The object id of an operational point #[schema(inline)] diff --git a/editoast/editoast_schemas/src/train_schedule/train_schedule_base.rs b/editoast/editoast_schemas/src/train_schedule/train_schedule_base.rs index d01d0642d03..77df8da9dc8 100644 --- a/editoast/editoast_schemas/src/train_schedule/train_schedule_base.rs +++ b/editoast/editoast_schemas/src/train_schedule/train_schedule_base.rs @@ -162,6 +162,8 @@ mod tests { use serde_json::from_str; use serde_json::to_string; + use crate::train_schedule::path_item::OperationalPointIdentifier::OperationalPointId; + use crate::train_schedule::path_item::OperationalPointReference; use crate::train_schedule::schedule_item::ReceptionSignal; use crate::train_schedule::Margins; use crate::train_schedule::PathItemLocation; @@ -180,9 +182,13 @@ mod tests { /// Test deserialize an invalid train schedule #[test] fn deserialize_duplicate_path_id_train_schedule() { - let location = PathItemLocation::OperationalPointId { - operational_point: "op".into(), - }; + let location = PathItemLocation::OperationalPointReference(OperationalPointReference { + reference: OperationalPointId { + operational_point: "op".into(), + }, + track_id: None, + track_label: None, + }); let path_item = PathItem { id: "a".into(), location, @@ -235,9 +241,13 @@ mod tests { /// Test deserialize an invalid train schedule #[test] fn deserialize_duplicate_schedule_points_train_schedule() { - let location = PathItemLocation::OperationalPointId { - operational_point: "op".into(), - }; + let location = PathItemLocation::OperationalPointReference(OperationalPointReference { + reference: OperationalPointId { + operational_point: "op".into(), + }, + track_id: None, + track_label: None, + }); let path_item = PathItem { id: "a".into(), location, @@ -270,9 +280,13 @@ mod tests { /// Test deserialize an invalid train schedule #[test] fn deserialize_arrival_time_first_waypoint_schedule_train_schedule() { - let location = PathItemLocation::OperationalPointId { - operational_point: "op".into(), - }; + let location = PathItemLocation::OperationalPointReference(OperationalPointReference { + reference: OperationalPointId { + operational_point: "op".into(), + }, + track_id: None, + track_label: None, + }); let path_item = PathItem { id: "a".into(), location, diff --git a/editoast/openapi.yaml b/editoast/openapi.yaml index ebbc3507d24..959ddb06843 100644 --- a/editoast/openapi.yaml +++ b/editoast/openapi.yaml @@ -7073,6 +7073,52 @@ components: maxLength: 255 minLength: 1 additionalProperties: false + OperationalPointReference: + allOf: + - oneOf: + - type: object + required: + - operational_point + properties: + operational_point: + type: string + maxLength: 255 + minLength: 1 + - type: object + required: + - trigram + properties: + secondary_code: + type: string + description: An optional secondary code to identify a more specific location + nullable: true + trigram: + type: string + minLength: 1 + - type: object + required: + - uic + properties: + secondary_code: + type: string + description: An optional secondary code to identify a more specific location + nullable: true + uic: + type: integer + format: int32 + description: The [UIC](https://en.wikipedia.org/wiki/List_of_UIC_country_codes) code of an operational point + minimum: 0 + - type: object + properties: + track_id: + allOf: + - type: string + maxLength: 255 + minLength: 1 + nullable: true + track_label: + type: string + nullable: true Ordering: type: string enum: @@ -7234,38 +7280,7 @@ components: PathItemLocation: oneOf: - $ref: '#/components/schemas/TrackOffset' - - type: object - required: - - operational_point - properties: - operational_point: - type: string - maxLength: 255 - minLength: 1 - - type: object - required: - - trigram - properties: - secondary_code: - type: string - description: An optional secondary code to identify a more specific location - nullable: true - trigram: - type: string - minLength: 1 - - type: object - required: - - uic - properties: - secondary_code: - type: string - description: An optional secondary code to identify a more specific location - nullable: true - uic: - type: integer - format: int32 - description: The [UIC](https://en.wikipedia.org/wiki/List_of_UIC_country_codes) code of an operational point - minimum: 0 + - $ref: '#/components/schemas/OperationalPointReference' description: The location of a path waypoint PathProperties: type: object diff --git a/editoast/src/views/path/path_item_cache.rs b/editoast/src/views/path/path_item_cache.rs index 59b63c9514c..2913c23f316 100644 --- a/editoast/src/views/path/path_item_cache.rs +++ b/editoast/src/views/path/path_item_cache.rs @@ -4,10 +4,12 @@ use crate::error::Result; use crate::models::TrackSectionModel; use crate::RetrieveBatchUnchecked; use editoast_schemas::infra::TrackOffset; +use editoast_schemas::train_schedule::OperationalPointReference; use std::collections::HashMap; use std::collections::HashSet; use editoast_models::DbConnection; +use editoast_schemas::train_schedule::OperationalPointIdentifier; use editoast_schemas::train_schedule::PathItemLocation; use crate::models::OperationalPointModel; @@ -93,26 +95,32 @@ impl PathItemCache { let mut result: Vec> = Vec::default(); let mut invalid_path_items = Vec::new(); for (index, &path_item) in path_items.iter().enumerate() { + dbg!(&path_item); let track_offsets = match path_item { PathItemLocation::TrackOffset(track_offset) => { vec![track_offset.clone()] } - PathItemLocation::OperationalPointId { operational_point } => { - match self.get_from_id(&operational_point.0) { - Some(op) => op.track_offset(), - None => { - invalid_path_items.push(InvalidPathItem { - index, - path_item: path_item.clone(), - }); - continue; - } + PathItemLocation::OperationalPointReference(OperationalPointReference { + reference: OperationalPointIdentifier::OperationalPointId { operational_point }, + .. + }) => match self.get_from_id(&operational_point.0) { + Some(op) => op.track_offset(), + None => { + invalid_path_items.push(InvalidPathItem { + index, + path_item: path_item.clone(), + }); + continue; } - } - PathItemLocation::OperationalPointDescription { - trigram, - secondary_code, - } => { + }, + PathItemLocation::OperationalPointReference(OperationalPointReference { + reference: + OperationalPointIdentifier::OperationalPointDescription { + trigram, + secondary_code, + }, + .. + }) => { let ops = self .get_from_trigram(&trigram.0) .cloned() @@ -127,10 +135,14 @@ impl PathItemCache { } track_offsets_from_ops(&ops) } - PathItemLocation::OperationalPointUic { - uic, - secondary_code, - } => { + PathItemLocation::OperationalPointReference(OperationalPointReference { + reference: + OperationalPointIdentifier::OperationalPointUic { + uic, + secondary_code, + }, + .. + }) => { let ops = self .get_from_uic(i64::from(*uic)) .cloned() @@ -183,15 +195,25 @@ fn collect_path_item_ids(path_items: &[&PathItemLocation]) -> (Vec, Vec< for item in path_items { match item { - PathItemLocation::OperationalPointDescription { trigram, .. } => { + PathItemLocation::OperationalPointReference(OperationalPointReference { + reference: OperationalPointIdentifier::OperationalPointDescription { trigram, .. }, + .. + }) => { trigrams.push(trigram.clone().0); } - PathItemLocation::OperationalPointUic { uic, .. } => { + PathItemLocation::OperationalPointReference(OperationalPointReference { + reference: OperationalPointIdentifier::OperationalPointUic { uic, .. }, + .. + }) => { ops_uic.push(i64::from(*uic)); } - PathItemLocation::OperationalPointId { - operational_point, .. - } => { + PathItemLocation::OperationalPointReference(OperationalPointReference { + reference: + OperationalPointIdentifier::OperationalPointId { + operational_point, .. + }, + .. + }) => { ops_id.push(operational_point.clone().0); } _ => {} diff --git a/editoast/src/views/path/pathfinding.rs b/editoast/src/views/path/pathfinding.rs index ff892b9a171..125e449c931 100644 --- a/editoast/src/views/path/pathfinding.rs +++ b/editoast/src/views/path/pathfinding.rs @@ -431,6 +431,8 @@ fn path_input_hash(infra: i64, infra_version: &String, path_input: &PathfindingI pub mod tests { use axum::http::StatusCode; use editoast_models::DbConnectionPoolV2; + use editoast_schemas::train_schedule::OperationalPointIdentifier; + use editoast_schemas::train_schedule::OperationalPointReference; use editoast_schemas::train_schedule::PathItemLocation; use pretty_assertions::assert_eq; use rstest::rstest; @@ -457,7 +459,7 @@ pub mod tests { "path_items":[ {"trigram":"WS","secondary_code":"BV"}, {"trigram":"NO_TRIGRAM","secondary_code":null}, - {"trigram":"SWS","secondary_code":"BV"} + {"trigram":"SWS","secondary_code":"BV",} ], "rolling_stock_is_thermal":true, "rolling_stock_loading_gauge":"G1", @@ -475,10 +477,17 @@ pub mod tests { PathfindingInputError::InvalidPathItems { items: vec![InvalidPathItem { index: 1, - path_item: PathItemLocation::OperationalPointDescription { - trigram: "NO_TRIGRAM".into(), - secondary_code: None - } + path_item: PathItemLocation::OperationalPointReference( + OperationalPointReference { + reference: + OperationalPointIdentifier::OperationalPointDescription { + trigram: "NO_TRIGRAM".into(), + secondary_code: None + }, + track_id: None, + track_label: None + } + ) }] } )) diff --git a/editoast/src/views/timetable.rs b/editoast/src/views/timetable.rs index 49e49c54622..8b278f6111d 100644 --- a/editoast/src/views/timetable.rs +++ b/editoast/src/views/timetable.rs @@ -235,7 +235,6 @@ async fn train_schedule( timetable_id, }) .await?; - let changesets: Vec = train_schedules .into_iter() .map(|ts| TrainScheduleForm { diff --git a/front/src/common/api/generatedEditoastApi.ts b/front/src/common/api/generatedEditoastApi.ts index 561d8e83a6e..99f2c79e343 100644 --- a/front/src/common/api/generatedEditoastApi.ts +++ b/front/src/common/api/generatedEditoastApi.ts @@ -2421,8 +2421,7 @@ export type TrackOffset = { offset: number; track: string; }; -export type PathItemLocation = - | TrackOffset +export type OperationalPointReference = ( | { operational_point: string; } @@ -2436,7 +2435,12 @@ export type PathItemLocation = secondary_code?: string | null; /** The [UIC](https://en.wikipedia.org/wiki/List_of_UIC_country_codes) code of an operational point */ uic: number; - }; + } +) & { + track_id?: string | null; + track_label?: string | null; +}; +export type PathItemLocation = TrackOffset | OperationalPointReference; export type PathfindingInputError = | { error_type: 'invalid_path_items';