Skip to content

Commit 1a58765

Browse files
committed
Adding OperationalPointIdLocation as location for POST /timetable/{id}
1 parent 3650cc6 commit 1a58765

File tree

2 files changed

+127
-21
lines changed

2 files changed

+127
-21
lines changed

editoast/src/models/infra_objects/operational_point.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use crate::error::Result;
44
use crate::{schema::OperationalPoint, tables::infra_object_operational_point};
55
use derivative::Derivative;
6-
use diesel::sql_types::{Array, BigInt};
6+
use diesel::sql_types::{Array, BigInt, Text};
77
use diesel::{result::Error as DieselError, sql_query};
88
use diesel::{ExpressionMethods, QueryDsl};
99
use diesel_async::{AsyncPgConnection as PgConnection, RunQueryDsl};
@@ -38,4 +38,19 @@ impl OperationalPointModel {
3838
.load(conn)
3939
.await?)
4040
}
41+
42+
/// Retrieve a list of operational points from the database
43+
pub async fn retrieve_from_ids(
44+
conn: &mut PgConnection,
45+
infra_id: i64,
46+
ids: &[String],
47+
) -> Result<Vec<Self>> {
48+
let query = "SELECT * FROM infra_object_operational_point
49+
WHERE infra_id = $1 AND infra_object_operational_point.obj_id = ANY($2)".to_string();
50+
Ok(sql_query(query)
51+
.bind::<BigInt, _>(infra_id)
52+
.bind::<Array<Text>, _>(ids)
53+
.load(conn)
54+
.await?)
55+
}
4156
}

editoast/src/views/timetable/import.rs

+111-20
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ pub enum TimetableImportPathLocation {
6767
TrackOffsetLocation { track_section: String, offset: f64 },
6868
#[serde(rename = "operational_point")]
6969
OperationalPointLocation { uic: i64 },
70+
#[serde(rename = "operational_point_id")]
71+
OperationalPointIdLocation { id: String },
7072
}
7173

7274
#[derive(Debug, Serialize, ToSchema)]
@@ -77,7 +79,7 @@ struct TimetableImportReport {
7779
#[derive(Debug, Clone, Serialize, ToSchema)]
7880
enum TimetableImportError {
7981
RollingStockNotFound { name: String },
80-
OperationalPointNotFound { missing_uics: Vec<i64> },
82+
OperationalPointNotFound { missing_uics: Vec<i64>, missing_ids: Vec<String> },
8183
PathfindingError { cause: InternalError },
8284
SimulationError { cause: InternalError },
8385
}
@@ -183,9 +185,10 @@ async fn import_item(
183185
pf_request.with_rolling_stocks(&mut vec![rolling_stock.clone()]);
184186
// List operational points uic needed for this import
185187
let ops_uic = ops_uic_from_path(&import_item.path);
188+
let ops_id = ops_id_from_path(&import_item.path);
186189
// Retrieve operational points
187-
let op_id_to_parts = match find_operation_points(&ops_uic, infra_id, conn).await? {
188-
Ok(op_id_to_parts) => op_id_to_parts,
190+
let (op_uic_to_parts, op_id_to_parts) = match find_operation_points(&ops_uic, &ops_id, infra_id, conn).await? {
191+
Ok((op_uic_to_parts, op_id_to_parts)) => (op_uic_to_parts, op_id_to_parts),
189192
Err(err) => {
190193
return Ok(import_item
191194
.trains
@@ -195,7 +198,7 @@ async fn import_item(
195198
}
196199
};
197200
// Create waypoints
198-
let mut waypoints = waypoints_from_steps(&import_item.path, &op_id_to_parts);
201+
let mut waypoints = waypoints_from_steps(&import_item.path, &op_uic_to_parts, &op_id_to_parts);
199202
pf_request.with_waypoints(&mut waypoints);
200203

201204
// Run pathfinding
@@ -295,30 +298,50 @@ async fn import_item(
295298

296299
async fn find_operation_points(
297300
ops_uic: &[i64],
301+
ops_id: &[String],
298302
infra_id: i64,
299303
conn: &mut AsyncPgConnection,
300-
) -> Result<std::result::Result<HashMap<i64, Vec<OperationalPointPart>>, TimetableImportError>> {
304+
) -> Result<std::result::Result<(HashMap<i64, Vec<OperationalPointPart>>, HashMap<String, Vec<OperationalPointPart>>), TimetableImportError>> {
301305
// Retrieve operational points
302306
let ops = OperationalPointModel::retrieve_from_uic(conn, infra_id, ops_uic).await?;
303-
let mut op_id_to_parts = HashMap::<_, Vec<_>>::new();
307+
let mut op_uic_to_parts = HashMap::<_, Vec<_>>::new();
304308
for op in ops {
305-
op_id_to_parts
309+
op_uic_to_parts
306310
.entry(op.data.0.extensions.identifier.unwrap().uic)
307311
.or_default()
308312
.extend(op.data.0.parts);
309313
}
314+
let mut missing_uics: Vec<i64> = vec![];
310315
// If we didn't find all the operational points, we can't run the pathfinding
311-
if op_id_to_parts.len() != ops_uic.len() {
312-
let missing_uics = ops_uic
316+
if op_uic_to_parts.len() != ops_uic.len() {
317+
ops_uic
313318
.iter()
314-
.filter(|uic| !op_id_to_parts.contains_key(uic))
315-
.cloned()
316-
.collect();
319+
.for_each(|uic| {if !op_uic_to_parts.contains_key(uic) {missing_uics.push(*uic)}});
320+
}
321+
322+
let ops2 = OperationalPointModel::retrieve_from_ids(conn, infra_id, ops_id).await?;
323+
let mut op_id_to_parts = HashMap::<_, Vec<_>>::new();
324+
for op in ops2 {
325+
op_id_to_parts
326+
.entry(op.obj_id)
327+
.or_default()
328+
.extend(op.data.0.parts);
329+
}
330+
// If we didn't find all the operational points, we can't run the pathfinding
331+
let mut missing_ids: Vec<String> = vec![];
332+
if op_id_to_parts.len() != ops_id.len() {
333+
ops_id
334+
.iter()
335+
.for_each(|id| {if !op_id_to_parts.contains_key(id) {missing_ids.push(id.to_string())}});
336+
}
337+
if missing_uics.len() + missing_ids.len() > 0 {
317338
return Ok(Err(TimetableImportError::OperationalPointNotFound {
318339
missing_uics,
340+
missing_ids,
319341
}));
320342
}
321-
Ok(Ok(op_id_to_parts))
343+
344+
Ok(Ok((op_uic_to_parts, op_id_to_parts)))
322345
}
323346

324347
fn ops_uic_from_path(path: &[TimetableImportPathStep]) -> Vec<i64> {
@@ -335,9 +358,24 @@ fn ops_uic_from_path(path: &[TimetableImportPathStep]) -> Vec<i64> {
335358
ops_uic
336359
}
337360

361+
fn ops_id_from_path(path: &[TimetableImportPathStep]) -> Vec<String> {
362+
let mut ops_id = path
363+
.iter()
364+
.filter_map(|step| match &step.location {
365+
TimetableImportPathLocation::OperationalPointIdLocation { id } => Some(id.to_string()),
366+
_ => None,
367+
})
368+
.collect::<Vec<_>>();
369+
// Remove duplicates
370+
ops_id.sort();
371+
ops_id.dedup();
372+
ops_id
373+
}
374+
338375
fn waypoints_from_steps(
339376
path: &Vec<TimetableImportPathStep>,
340-
op_id_to_parts: &HashMap<i64, Vec<OperationalPointPart>>,
377+
op_uic_to_parts: &HashMap<i64, Vec<OperationalPointPart>>,
378+
op_id_to_parts: &HashMap<String, Vec<OperationalPointPart>>,
341379
) -> Vec<Vec<Waypoint>> {
342380
let mut res = PathfindingWaypoints::new();
343381
for step in path {
@@ -346,12 +384,18 @@ fn waypoints_from_steps(
346384
track_section,
347385
offset,
348386
} => Vec::from(Waypoint::bidirectional(track_section, *offset)),
349-
TimetableImportPathLocation::OperationalPointLocation { uic } => op_id_to_parts
387+
TimetableImportPathLocation::OperationalPointLocation { uic } => op_uic_to_parts
350388
.get(uic)
351389
.unwrap()
352390
.iter()
353391
.flat_map(|op_part| Waypoint::bidirectional(&op_part.track, op_part.position))
354392
.collect(),
393+
TimetableImportPathLocation::OperationalPointIdLocation { id } => op_id_to_parts
394+
.get(id)
395+
.unwrap()
396+
.iter()
397+
.flat_map(|op_part| Waypoint::bidirectional(&op_part.track, op_part.position))
398+
.collect(),
355399
});
356400
}
357401
res
@@ -438,8 +482,8 @@ mod tests {
438482

439483
#[test]
440484
fn test_waypoints_from_steps() {
441-
let mut op_id_to_parts = HashMap::new();
442-
op_id_to_parts.insert(
485+
let mut op_uic_to_parts = HashMap::new();
486+
op_uic_to_parts.insert(
443487
1,
444488
vec![
445489
OperationalPointPart {
@@ -455,6 +499,23 @@ mod tests {
455499
],
456500
);
457501

502+
let mut op_id_to_parts = HashMap::new();
503+
op_id_to_parts.insert(
504+
"a1".to_string(),
505+
vec![
506+
OperationalPointPart {
507+
track: Identifier("E".to_string()),
508+
position: 0.,
509+
..Default::default()
510+
},
511+
OperationalPointPart {
512+
track: Identifier("F".to_string()),
513+
position: 100.,
514+
..Default::default()
515+
},
516+
],
517+
);
518+
458519
let path = vec![
459520
TimetableImportPathStep {
460521
location: TimetableImportPathLocation::TrackOffsetLocation {
@@ -467,11 +528,15 @@ mod tests {
467528
location: TimetableImportPathLocation::OperationalPointLocation { uic: 1 },
468529
schedule: HashMap::new(),
469530
},
531+
TimetableImportPathStep {
532+
location: TimetableImportPathLocation::OperationalPointIdLocation { id: "a1".to_string() },
533+
schedule: HashMap::new(),
534+
},
470535
];
471536

472-
let waypoints = waypoints_from_steps(&path, &op_id_to_parts);
537+
let waypoints = waypoints_from_steps(&path, &op_uic_to_parts, &op_id_to_parts);
473538

474-
assert_eq!(waypoints.len(), 2);
539+
assert_eq!(waypoints.len(), 3);
475540
assert_eq!(waypoints[0], Waypoint::bidirectional("C", 50.));
476541
assert_eq!(
477542
waypoints[1],
@@ -481,10 +546,18 @@ mod tests {
481546
]
482547
.concat()
483548
);
549+
assert_eq!(
550+
waypoints[2],
551+
[
552+
Waypoint::bidirectional("E", 0.),
553+
Waypoint::bidirectional("F", 100.),
554+
]
555+
.concat()
556+
);
484557
}
485558

486559
#[test]
487-
fn test_ops_uic_from_path() {
560+
fn test_ops_uic_id_from_path() {
488561
let path = vec![
489562
TimetableImportPathStep {
490563
location: TimetableImportPathLocation::TrackOffsetLocation {
@@ -497,6 +570,10 @@ mod tests {
497570
location: TimetableImportPathLocation::OperationalPointLocation { uic: 1 },
498571
schedule: HashMap::new(),
499572
},
573+
TimetableImportPathStep {
574+
location: TimetableImportPathLocation::OperationalPointIdLocation { id: "a1".to_string() },
575+
schedule: HashMap::new(),
576+
},
500577
TimetableImportPathStep {
501578
location: TimetableImportPathLocation::OperationalPointLocation { uic: 2 },
502579
schedule: HashMap::new(),
@@ -508,6 +585,14 @@ mod tests {
508585
},
509586
schedule: HashMap::new(),
510587
},
588+
TimetableImportPathStep {
589+
location: TimetableImportPathLocation::OperationalPointIdLocation { id: "a2".to_string() },
590+
schedule: HashMap::new(),
591+
},
592+
TimetableImportPathStep {
593+
location: TimetableImportPathLocation::OperationalPointIdLocation { id: "a1".to_string() },
594+
schedule: HashMap::new(),
595+
},
511596
TimetableImportPathStep {
512597
location: TimetableImportPathLocation::OperationalPointLocation { uic: 1 },
513598
schedule: HashMap::new(),
@@ -519,5 +604,11 @@ mod tests {
519604
assert_eq!(ops_uic.len(), 2);
520605
assert_eq!(ops_uic[0], 1);
521606
assert_eq!(ops_uic[1], 2);
607+
608+
let ops_id = ops_id_from_path(&path);
609+
610+
assert_eq!(ops_id.len(), 2);
611+
assert_eq!(ops_id[0], "a1");
612+
assert_eq!(ops_id[1], "a2");
522613
}
523614
}

0 commit comments

Comments
 (0)