Skip to content

Commit 9358bbd

Browse files
committed
editoast : CRUD TimetableV2
1 parent 429aa94 commit 9358bbd

File tree

8 files changed

+205
-237
lines changed

8 files changed

+205
-237
lines changed

editoast/src/modelsv2/mod.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@ pub mod infra_objects;
33
pub mod scenario;
44
pub mod timetable;
55
pub mod trainschedule;
6-
pub use scenario::ScenarioV2WithCountTrains;
7-
86
pub use documents::Document;
7+
pub use timetable::TimetableV2;
98

109
use async_trait::async_trait;
1110
use diesel::{pg::Pg, result::Error::NotFound, AsChangeset, QueryableByName};

editoast/src/modelsv2/scenario.rs

+1-230
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use editoast_derive::ModelV2;
55
use serde_derive::{Deserialize, Serialize};
66
use utoipa::ToSchema;
77

8-
#[derive(Debug, Default, Clone, ModelV2)]
8+
#[derive(Debug, Default, Clone, ModelV2, Deserialize, Serialize)]
99
#[model(changeset(derive(Deserialize)))]
1010
#[model(table = crate::tables::scenariov2)]
1111
pub struct ScenarioV2 {
@@ -19,232 +19,3 @@ pub struct ScenarioV2 {
1919
pub timetable_id: i64,
2020
pub study_id: i64,
2121
}
22-
23-
crate::schemas! {
24-
ScenarioV2WithCountTrains
25-
}
26-
27-
#[derive(Debug, Clone, Deserialize, Serialize, QueryableByName, ToSchema)]
28-
pub struct ScenarioWithDetails {
29-
pub id: i64,
30-
pub infra_id: i64,
31-
pub name: String,
32-
pub description: String,
33-
pub creation_date: NaiveDateTime,
34-
pub last_modification: NaiveDateTime,
35-
pub tags: Vec<Option<String>>,
36-
pub timetable_id: i64,
37-
pub study_id: i64,
38-
#[diesel(sql_type = Text)]
39-
pub infra_name: String,
40-
#[diesel(sql_type = Nullable<Text>)]
41-
pub electrical_profile_set_name: Option<String>,
42-
#[diesel(sql_type = Array<LightTrainSchedule>)]
43-
pub train_schedules: Vec<LightTrainSchedule>,
44-
#[diesel(sql_type = BigInt)]
45-
pub trains_count: i64,
46-
}
47-
48-
#[derive(Debug, Clone, Serialize, QueryableByName, ToSchema)]
49-
pub struct ScenarioV2WithCountTrains {
50-
pub id: i64,
51-
pub infra_id: i64,
52-
pub name: String,
53-
pub description: String,
54-
pub creation_date: NaiveDateTime,
55-
pub last_modification: NaiveDateTime,
56-
pub tags: Vec<Option<String>>,
57-
pub timetable_id: i64,
58-
pub study_id: i64,
59-
#[diesel(sql_type = BigInt)]
60-
pub trains_count: i64,
61-
#[diesel(sql_type = Text)]
62-
pub infra_name: String,
63-
}
64-
65-
impl ScenarioV2 {
66-
pub async fn with_details(self, db_pool: Data<DbPool>) -> Result<ScenarioWithDetails> {
67-
let mut conn = db_pool.get().await?;
68-
self.with_details_conn(&mut conn).await
69-
}
70-
71-
pub async fn with_details_conn(self, conn: &mut PgConnection) -> Result<ScenarioWithDetails> {
72-
use crate::tables::electrical_profile_set::dsl as elec_dsl;
73-
use crate::tables::infra::dsl as infra_dsl;
74-
use crate::tables::train_schedule::dsl::*;
75-
76-
let infra_name = infra_dsl::infra
77-
.filter(infra_dsl::id.eq(self.infra_id.unwrap()))
78-
.select(infra_dsl::name)
79-
.first::<String>(conn)
80-
.await?;
81-
82-
let electrical_profile_set_name = match self.electrical_profile_set_id.unwrap() {
83-
Some(electrical_profile_set) => Some(
84-
elec_dsl::electrical_profile_set
85-
.filter(elec_dsl::id.eq(electrical_profile_set))
86-
.select(elec_dsl::name)
87-
.first::<String>(conn)
88-
.await?,
89-
),
90-
None => None,
91-
};
92-
93-
let train_schedules = train_schedule
94-
.filter(timetable_id.eq(self.timetable_id.unwrap()))
95-
.select((id, train_name, departure_time, path_id))
96-
.load::<LightTrainSchedule>(conn)
97-
.await?;
98-
99-
let trains_count = train_schedules.len() as i64;
100-
101-
Ok(ScenarioWithDetails {
102-
scenario: self,
103-
infra_name,
104-
electrical_profile_set_name,
105-
train_schedules,
106-
trains_count,
107-
})
108-
}
109-
}
110-
111-
/// Delete a scenario.
112-
/// When we delete a scenario, the associated timetable is deleted too.
113-
#[async_trait]
114-
impl Delete for Scenario {
115-
async fn delete_conn(conn: &mut PgConnection, scenario_id: i64) -> Result<bool> {
116-
use crate::tables::scenario::dsl as scenario_dsl;
117-
use crate::tables::timetable::dsl as timetable_dsl;
118-
119-
// Delete scenario
120-
let scenario = match delete(scenario_dsl::scenario.filter(scenario_dsl::id.eq(scenario_id)))
121-
.get_result::<Scenario>(conn)
122-
.await
123-
{
124-
Ok(scenario) => scenario,
125-
Err(DieselError::NotFound) => return Ok(false),
126-
Err(err) => return Err(err.into()),
127-
};
128-
129-
// Delete timetable
130-
delete(
131-
timetable_dsl::timetable.filter(timetable_dsl::id.eq(scenario.timetable_id.unwrap())),
132-
)
133-
.execute(conn)
134-
.await?;
135-
Ok(true)
136-
}
137-
}
138-
139-
#[async_trait]
140-
impl List<(i64, Ordering)> for ScenarioWithCountTrains {
141-
/// List all scenarios with the number of trains.
142-
/// This functions takes a study_id to filter scenarios.
143-
async fn list_conn(
144-
conn: &mut PgConnection,
145-
page: i64,
146-
page_size: i64,
147-
params: (i64, Ordering),
148-
) -> Result<PaginatedResponse<Self>> {
149-
let study_id = params.0;
150-
let ordering = params.1.to_sql();
151-
sql_query(format!("WITH scenarios_with_train_counts AS (
152-
SELECT t.*, COUNT(train_schedule.id) as trains_count
153-
FROM scenario as t
154-
LEFT JOIN train_schedule ON t.timetable_id = train_schedule.timetable_id WHERE t.study_id = $1
155-
GROUP BY t.id ORDER BY {ordering}
156-
)
157-
SELECT scenarios_with_train_counts.*, infra.name as infra_name
158-
FROM scenarios_with_train_counts
159-
JOIN infra ON infra.id = infra_id"))
160-
.bind::<BigInt, _>(study_id)
161-
.paginate(page, page_size)
162-
.load_and_count(conn).await
163-
}
164-
}
165-
166-
#[cfg(test)]
167-
pub mod test {
168-
use super::*;
169-
use crate::fixtures::tests::{db_pool, scenario_fixture_set, ScenarioFixtureSet, TestFixture};
170-
use crate::models::Delete;
171-
use crate::models::List;
172-
use crate::models::Ordering;
173-
use crate::models::Retrieve;
174-
use crate::models::Timetable;
175-
use rstest::rstest;
176-
177-
#[rstest]
178-
async fn create_delete_scenario(db_pool: Data<DbPool>) {
179-
let ScenarioFixtureSet { scenario, .. } = scenario_fixture_set().await;
180-
181-
// Delete the scenario
182-
Scenario::delete(db_pool.clone(), scenario.id())
183-
.await
184-
.unwrap();
185-
186-
// Second delete should fail
187-
assert!(!Scenario::delete(db_pool.clone(), scenario.id())
188-
.await
189-
.unwrap());
190-
}
191-
192-
#[rstest]
193-
async fn get_study(db_pool: Data<DbPool>) {
194-
let ScenarioFixtureSet { study, .. } = scenario_fixture_set().await;
195-
196-
// Get a scenario
197-
assert!(Scenario::retrieve(db_pool.clone(), study.id())
198-
.await
199-
.is_ok());
200-
assert!(ScenarioWithCountTrains::list(
201-
db_pool.clone(),
202-
1,
203-
25,
204-
(study.id(), Ordering::LastModifiedAsc)
205-
)
206-
.await
207-
.is_ok());
208-
}
209-
210-
#[rstest]
211-
async fn sort_scenario(db_pool: Data<DbPool>) {
212-
let ScenarioFixtureSet {
213-
scenario,
214-
study,
215-
timetable,
216-
..
217-
} = scenario_fixture_set().await;
218-
219-
// Create second timetable
220-
let timetable_2 = TestFixture::create_legacy(
221-
Timetable {
222-
id: None,
223-
name: Some(timetable.model.name.clone().unwrap() + "_bis"),
224-
},
225-
db_pool.clone(),
226-
)
227-
.await;
228-
229-
// Create second scenario
230-
let scenario_2 = Scenario {
231-
name: Some(scenario.model.name.clone().unwrap() + "_bis"),
232-
id: None,
233-
timetable_id: Some(timetable_2.id()),
234-
..scenario.model.clone()
235-
};
236-
let _scenario_2 = TestFixture::create_legacy(scenario_2, db_pool.clone());
237-
238-
let scenarios =
239-
ScenarioWithCountTrains::list(db_pool.clone(), 1, 25, (study.id(), Ordering::NameDesc))
240-
.await
241-
.unwrap()
242-
.results;
243-
244-
for (p1, p2) in scenarios.iter().zip(scenarios.iter().skip(1)) {
245-
let name_1 = p1.scenario.name.as_ref().unwrap().to_lowercase();
246-
let name_2 = p2.scenario.name.as_ref().unwrap().to_lowercase();
247-
assert!(name_1.ge(&name_2));
248-
}
249-
}
250-
}

editoast/src/modelsv2/timetable.rs

+26-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,32 @@
1+
use std::collections::HashMap;
2+
3+
use crate::diesel::QueryDsl;
4+
use crate::error::Result;
5+
use crate::models::LightRollingStockModel;
6+
use crate::models::Retrieve;
7+
use crate::models::{
8+
train_schedule::{
9+
LightTrainSchedule, MechanicalEnergyConsumedBaseEco, TrainSchedule, TrainScheduleSummary,
10+
},
11+
SimulationOutput,
12+
};
13+
use crate::tables::timetable;
14+
use crate::DbPool;
15+
use actix_web::web::Data;
16+
use derivative::Derivative;
17+
use diesel::prelude::*;
18+
use diesel::result::Error as DieselError;
19+
use diesel::ExpressionMethods;
20+
use diesel_async::AsyncPgConnection as PgConnection;
21+
use diesel_async::RunQueryDsl;
22+
use editoast_derive::Model;
123
use editoast_derive::ModelV2;
24+
use futures::future::try_join_all;
25+
use serde::{Deserialize, Serialize};
226

3-
#[derive(Debug, Default, Clone, ModelV2)]
27+
#[derive(Debug, Default, Clone, Serialize, Deserialize, ModelV2)]
428
#[model(table = crate::tables::timetablev2)]
29+
#[model(changeset(public))]
530
pub struct TimetableV2 {
631
pub id: i64,
732
pub electrical_profile_set_id: Option<i64>,

editoast/src/modelsv2/trainschedule.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::DieselJson;
22
use chrono::NaiveDateTime;
3+
use diesel_async::RunQueryDsl;
34
use editoast_derive::ModelV2;
45

56
#[derive(Debug, Default, Clone, ModelV2)]

editoast/src/views/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub mod sprites;
1717
pub mod stdcm;
1818
pub mod study;
1919
pub mod timetable;
20+
pub mod timetablev2;
2021
pub mod train_schedule;
2122

2223
use self::openapi::{merge_path_items, remove_discriminator, OpenApiMerger, Routes};

editoast/src/views/scenario.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ crate::schemas! {
5656
#[derive(Debug, Error, EditoastError)]
5757
#[editoast_error(base_id = "scenario")]
5858
#[allow(clippy::enum_variant_names)]
59-
enum ScenarioError {
59+
pub enum ScenarioError {
6060
/// Couldn't found the scenario with the given scenario ID
6161
6262
#[error("Scenario '{scenario_id}', could not be found")]

editoast/src/views/scenariov2.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::models::train_schedule::LightTrainSchedule;
44
use crate::models::{
55
List, Project, Retrieve, ScenarioWithCountTrains, ScenarioWithDetails, Study, Timetable, Update,
66
};
7+
use crate::modelsv2::scenario::ScenarioV2;
78
use crate::views::pagination::{PaginatedResponse, PaginationQueryParam};
89
use crate::views::projects::{ProjectError, ProjectIdParam};
910
use crate::views::study::{StudyError, StudyIdParam};
@@ -180,12 +181,12 @@ async fn create(
180181
id: None,
181182
name: Some("timetable".into()),
182183
};
183-
let timetable = timetable.create(db_pool.clone()).await?;
184+
let timetable = timetablev2.create(db_pool.clone()).await?;
184185
let timetable_id = timetable.id.unwrap();
185186

186187
// Create Scenario
187-
let scenario: Scenario = data.into_inner().into_scenario(study_id, timetable_id);
188-
let scenario = scenario.create(db_pool.clone()).await?;
188+
let scenario: ScenarioV2 = data.into_inner().into_scenario(study_id, timetable_id);
189+
let scenario = scenario.create().await?;
189190

190191
// Update study last_modification field
191192
let study = study

0 commit comments

Comments
 (0)