-
Notifications
You must be signed in to change notification settings - Fork 46
/
Copy pathstudy.rs
172 lines (152 loc) · 5.28 KB
/
study.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
use crate::error::Result;
use crate::models::List;
use crate::modelsv2::{Changeset, Model, Row, Save};
use crate::views::pagination::{Paginate, PaginatedResponse};
use crate::DbPool;
use actix_web::web::Data;
use async_trait::async_trait;
use chrono::NaiveDate;
use chrono::{NaiveDateTime, Utc};
use diesel::sql_query;
use diesel::sql_types::BigInt;
use diesel::ExpressionMethods;
use diesel::QueryDsl;
use diesel_async::{AsyncPgConnection as PgConnection, RunQueryDsl};
use editoast_derive::ModelV2;
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use crate::modelsv2::projects::{Ordering, Tags};
#[derive(Clone, Debug, Serialize, Deserialize, ModelV2, ToSchema)]
#[model(table = crate::tables::study)]
pub struct Study {
pub id: i64,
pub name: String,
pub description: String,
pub business_code: String,
pub service_code: String,
pub creation_date: NaiveDateTime,
pub last_modification: NaiveDateTime,
pub start_date: Option<NaiveDate>,
pub expected_end_date: Option<NaiveDate>,
pub actual_end_date: Option<NaiveDate>,
pub budget: Option<i32>,
#[model(remote = "Vec<Option<String>>")]
pub tags: Tags,
pub state: String,
pub study_type: String,
pub project_id: i64,
}
impl Study {
pub async fn update_last_modified(&mut self, conn: &mut PgConnection) -> Result<()> {
self.last_modification = Utc::now().naive_utc();
self.save(conn).await?;
Ok(())
}
pub async fn scenarios_count(&self, db_pool: Data<DbPool>) -> Result<i64> {
use crate::tables::scenario::dsl as scenario_dsl;
let conn = &mut db_pool.get().await?;
let scenarios_count = scenario_dsl::scenario
.filter(scenario_dsl::study_id.eq(self.id))
.count()
.get_result(conn)
.await?;
Ok(scenarios_count)
}
pub fn validate(study_changeset: &Changeset<Self>) -> Result<()> {
if !dates_in_order(
study_changeset.start_date,
study_changeset.expected_end_date,
) || !dates_in_order(study_changeset.start_date, study_changeset.actual_end_date)
{
return Err(crate::views::study::StudyError::StartDateAfterEndDate.into());
}
Ok(())
}
}
fn dates_in_order(a: Option<Option<NaiveDate>>, b: Option<Option<NaiveDate>>) -> bool {
match (a, b) {
(Some(Some(a)), Some(Some(b))) => a <= b,
_ => true,
}
}
#[async_trait]
impl List<(i64, Ordering)> for Study {
async fn list_conn(
conn: &mut PgConnection,
page: i64,
page_size: i64,
params: (i64, Ordering),
) -> Result<PaginatedResponse<Self>> {
let project_id = params.0;
let ordering = params.1.to_sql();
let study_row = sql_query(format!(
"SELECT t.* FROM study as t WHERE t.project_id = $1 ORDER BY {ordering}"
))
.bind::<BigInt, _>(project_id)
.paginate(page, page_size)
.load_and_count::<Row<Study>>(conn)
.await?;
let results: Vec<Study> = study_row.results.into_iter().map(Self::from_row).collect();
Ok(PaginatedResponse {
count: study_row.count,
previous: study_row.previous,
next: study_row.next,
results,
})
}
}
#[cfg(test)]
pub mod test {
use super::*;
use crate::fixtures::tests::{db_pool, study_fixture_set, StudyFixtureSet, TestFixture};
use crate::models::List;
use crate::modelsv2::{DeleteStatic, Model, Ordering, Retrieve};
use rstest::rstest;
#[rstest]
async fn create_delete_study(
#[future] study_fixture_set: StudyFixtureSet,
db_pool: Data<DbPool>,
) {
let StudyFixtureSet { study, .. } = study_fixture_set.await;
// Delete the study
let conn = &mut db_pool.get().await.unwrap();
Study::delete_static(conn, study.id()).await.unwrap();
// Second delete should fail
assert!(!Study::delete_static(conn, study.id()).await.unwrap());
}
#[rstest]
async fn get_study(#[future] study_fixture_set: StudyFixtureSet, db_pool: Data<DbPool>) {
let StudyFixtureSet { study, project } = study_fixture_set.await;
// Get a study
let conn = &mut db_pool.get().await.unwrap();
assert!(Study::retrieve(conn, study.id()).await.is_ok());
assert!(Study::list(
db_pool.clone(),
1,
25,
(project.id(), Ordering::LastModifiedAsc)
)
.await
.is_ok());
}
#[rstest]
async fn sort_study(#[future] study_fixture_set: StudyFixtureSet, db_pool: Data<DbPool>) {
let StudyFixtureSet { study, project } = study_fixture_set.await;
// Create second study
let study_2 = study
.model
.clone()
.into_changeset()
.name(study.model.name.clone() + "_bis");
let _: TestFixture<Study> = TestFixture::create(study_2, db_pool.clone()).await;
let studies = Study::list(db_pool.clone(), 1, 25, (project.id(), Ordering::NameDesc))
.await
.unwrap()
.results;
for (s1, s2) in studies.iter().zip(studies.iter().skip(1)) {
let name_1 = s1.name.to_lowercase();
let name_2 = s2.name.to_lowercase();
assert!(name_1.ge(&name_2));
}
}
}