1use futures::Stream;
2use utoipa::ToSchema;
3
4use crate::prelude::*;
5
6#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
7
8pub struct ResearchForm {
9 pub id: Uuid,
10 pub course_id: Uuid,
11 pub content: serde_json::Value,
12 pub created_at: DateTime<Utc>,
13 pub updated_at: DateTime<Utc>,
14 pub deleted_at: Option<DateTime<Utc>>,
15}
16
17#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
18
19pub struct NewResearchForm {
20 pub course_id: Uuid,
21 pub content: serde_json::Value,
22}
23
24#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
25
26pub struct ResearchFormQuestion {
27 pub id: Uuid,
28 pub course_id: Uuid,
29 pub research_consent_form_id: Uuid,
30 pub question: String,
31 pub created_at: DateTime<Utc>,
32 pub updated_at: DateTime<Utc>,
33 pub deleted_at: Option<DateTime<Utc>>,
34}
35
36#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
37
38pub struct NewResearchFormQuestion {
39 pub question_id: Uuid,
40 pub course_id: Uuid,
41 pub research_consent_form_id: Uuid,
42 pub question: String,
43}
44
45#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
46
47pub struct NewResearchFormQuestionAnswer {
48 pub user_id: Uuid,
49 pub research_form_question_id: Uuid,
50 pub research_consent: bool,
51}
52
53#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
54
55pub struct ResearchFormQuestionAnswer {
56 pub id: Uuid,
57 pub user_id: Uuid,
58 pub course_id: Uuid,
59 pub research_form_question_id: Uuid,
60 pub research_consent: bool,
61 pub created_at: DateTime<Utc>,
62 pub updated_at: DateTime<Utc>,
63 pub deleted_at: Option<DateTime<Utc>>,
64}
65
66impl NewResearchForm {
67 pub fn new(course_id: Uuid) -> Self {
69 Self {
70 course_id,
71 content: Default::default(),
72 }
73 }
74
75 pub fn set_content(mut self, content: serde_json::Value) -> Self {
77 self.content = content;
78 self
79 }
80}
81
82pub async fn upsert_research_form(
83 conn: &mut PgConnection,
84 pkey_policy: PKeyPolicy<Uuid>,
85 new_research_form: &NewResearchForm,
86) -> ModelResult<ResearchForm> {
87 let form_res = sqlx::query_as!(
88 ResearchForm,
89 "
90INSERT INTO course_specific_research_consent_forms (
91 id,
92 course_id,
93 content
94 )
95VALUES ($1, $2, $3) ON CONFLICT (course_id, deleted_at)
96DO UPDATE SET content = $3
97RETURNING *
98",
99 pkey_policy.into_uuid(),
100 new_research_form.course_id,
101 serde_json::to_value(new_research_form.content.clone())?,
102 )
103 .fetch_one(conn)
104 .await?;
105 Ok(form_res)
106}
107
108pub async fn get_research_form_with_course_id(
109 conn: &mut PgConnection,
110 course_id: Uuid,
111) -> ModelResult<ResearchForm> {
112 let form_res = sqlx::query_as!(
113 ResearchForm,
114 "
115SELECT * FROM course_specific_research_consent_forms
116WHERE course_id = $1
117AND deleted_at IS NULL
118",
119 course_id,
120 )
121 .fetch_one(conn)
122 .await?;
123 Ok(form_res)
124}
125
126pub async fn upsert_research_form_questions(
127 conn: &mut PgConnection,
128 questions: &[NewResearchFormQuestion],
129) -> ModelResult<Vec<ResearchFormQuestion>> {
130 let mut tx = conn.begin().await?;
131
132 let mut inserted_questions = Vec::new();
133
134 for question in questions {
135 let form_res = sqlx::query_as!(
138 ResearchFormQuestion,
139 "
140INSERT INTO course_specific_consent_form_questions (
141 id,
142 course_id,
143 research_consent_form_id,
144 question
145 )
146VALUES ($1, $2, $3, $4) ON CONFLICT (id)
147DO UPDATE SET question = $4,
148research_consent_form_id = $3,
149deleted_at = NULL
150WHERE course_specific_consent_form_questions.course_id = excluded.course_id
151RETURNING *
152",
153 question.question_id,
154 question.course_id,
155 question.research_consent_form_id,
156 question.question
157 )
158 .fetch_optional(&mut *tx)
159 .await?
160 .ok_or_else(|| {
161 model_err!(
162 InvalidRequest,
163 "Question id belongs to a different course".to_string()
164 )
165 })?;
166
167 inserted_questions.push(form_res);
168 }
169
170 tx.commit().await?;
171
172 Ok(inserted_questions)
173}
174
175pub async fn get_research_form_questions_with_course_id(
176 conn: &mut PgConnection,
177 course_id: Uuid,
178) -> ModelResult<Vec<ResearchFormQuestion>> {
179 let form_res = sqlx::query_as!(
180 ResearchFormQuestion,
181 "
182SELECT * FROM course_specific_consent_form_questions
183WHERE course_id = $1
184AND deleted_at IS NULL
185",
186 course_id,
187 )
188 .fetch_all(conn)
189 .await?;
190 Ok(form_res)
191}
192
193pub async fn get_question_by_id(
194 conn: &mut PgConnection,
195 question_id: Uuid,
196) -> ModelResult<ResearchFormQuestion> {
197 let res = sqlx::query_as!(
198 ResearchFormQuestion,
199 "
200SELECT *
201FROM course_specific_consent_form_questions
202WHERE id = $1
203 AND deleted_at IS NULL
204 ",
205 question_id
206 )
207 .fetch_one(conn)
208 .await?;
209 Ok(res)
210}
211
212pub struct ExportedCourseResearchFormQustionAnswer {
213 pub course_id: Uuid,
214 pub research_consent_form_id: Uuid,
215 pub research_form_question_id: Uuid,
216 pub question: String,
217 pub user_id: Uuid,
218 pub research_consent: bool,
219 pub created_at: DateTime<Utc>,
220 pub updated_at: DateTime<Utc>,
221}
222
223pub fn stream_course_research_form_user_answers(
224 conn: &mut PgConnection,
225 course_id: Uuid,
226) -> impl Stream<Item = sqlx::Result<ExportedCourseResearchFormQustionAnswer>> + '_ {
227 sqlx::query_as!(
228 ExportedCourseResearchFormQustionAnswer,
229 r#"
230 SELECT DISTINCT ON (a.research_form_question_id, a.user_id)
231 q.course_id,
232 q.research_consent_form_id,
233 a.research_form_question_id,
234 q.question,
235 a.user_id,
236 a.research_consent,
237 a.created_at,
238 a.updated_at
239 FROM course_specific_consent_form_answers a
240 LEFT JOIN course_specific_consent_form_questions q ON a.research_form_question_id = q.id
241 WHERE a.course_id = $1
242 AND a.deleted_at IS NULL
243 AND q.deleted_at IS NULL
244 ORDER BY a.user_id, a.research_form_question_id, a.updated_at DESC
245 "#,
246 course_id
247 )
248 .fetch(conn)
249}
250
251pub async fn upsert_research_form_anwser(
252 conn: &mut PgConnection,
253 course_id: Uuid,
254 answer: &NewResearchFormQuestionAnswer,
255) -> ModelResult<Uuid> {
256 let form_res = sqlx::query!(
257 "
258INSERT INTO course_specific_consent_form_answers (
259 user_id,
260 course_id,
261 research_form_question_id,
262 research_consent
263 )
264VALUES ($1, $2, $3, $4) ON CONFLICT (user_id, research_form_question_id)
265DO UPDATE SET research_consent = $4
266RETURNING *
267",
268 answer.user_id,
269 course_id,
270 answer.research_form_question_id,
271 answer.research_consent
272 )
273 .fetch_one(conn)
274 .await?;
275 Ok(form_res.id)
276}
277
278pub async fn upsert_answer_for_user_id_and_question_id(
279 conn: &mut PgConnection,
280 user_id: Uuid,
281 course_id: Uuid,
282 question_id: Uuid,
283 research_consent: bool,
284) -> ModelResult<Uuid> {
285 let res = sqlx::query!(
286 "
287INSERT INTO course_specific_consent_form_answers (
288 user_id,
289 course_id,
290 research_form_question_id,
291 research_consent
292)
293SELECT $1, q.course_id, q.id, $4
294FROM course_specific_consent_form_questions q
295WHERE q.id = $2
296 AND q.course_id = $3
297 AND q.deleted_at IS NULL
298ON CONFLICT (user_id, research_form_question_id)
299DO UPDATE SET
300 course_id = excluded.course_id,
301 research_consent = excluded.research_consent,
302 deleted_at = NULL
303RETURNING id
304 ",
305 user_id,
306 question_id,
307 course_id,
308 research_consent
309 )
310 .fetch_one(conn)
311 .await?;
312 Ok(res.id)
313}
314
315pub async fn get_research_form_answers_with_user_id(
316 conn: &mut PgConnection,
317 course_id: Uuid,
318 user_id: Uuid,
319) -> ModelResult<Vec<ResearchFormQuestionAnswer>> {
320 let form_res = sqlx::query_as!(
321 ResearchFormQuestionAnswer,
322 "
323SELECT * FROM course_specific_consent_form_answers
324WHERE course_id = $1 AND user_id = $2
325AND deleted_at IS NULL
326",
327 course_id,
328 user_id
329 )
330 .fetch_all(conn)
331 .await?;
332 Ok(form_res)
333}
334
335pub async fn get_all_research_form_answers_with_user_id(
336 conn: &mut PgConnection,
337 user_id: Uuid,
338) -> ModelResult<Vec<ResearchFormQuestionAnswer>> {
339 let form_res = sqlx::query_as!(
340 ResearchFormQuestionAnswer,
341 "
342SELECT * FROM course_specific_consent_form_answers
343WHERE user_id = $1
344AND deleted_at IS NULL
345",
346 user_id
347 )
348 .fetch_all(conn)
349 .await?;
350 Ok(form_res)
351}
352
353pub async fn get_all_research_form_answers_with_user_course_and_question_id(
354 conn: &mut PgConnection,
355 user_id: Uuid,
356 course_id: Uuid,
357 course_specific_consent_form_question_id: Uuid,
358) -> ModelResult<Vec<ResearchFormQuestionAnswer>> {
359 let form_res = sqlx::query_as!(
360 ResearchFormQuestionAnswer,
361 "
362SELECT * FROM course_specific_consent_form_answers
363WHERE user_id = $1
364AND course_id = $2
365AND research_form_question_id = $3
366AND deleted_at IS NULL
367",
368 user_id,
369 course_id,
370 course_specific_consent_form_question_id
371 )
372 .fetch_all(conn)
373 .await?;
374 Ok(form_res)
375}