Skip to main content

headless_lms_models/
research_forms.rs

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    /// Creates `NewResearchForm` with provided values that is public by default.
68    pub fn new(course_id: Uuid) -> Self {
69        Self {
70            course_id,
71            content: Default::default(),
72        }
73    }
74
75    /// Sets the content of this research form.
76    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!(
136            ResearchFormQuestion,
137            "
138INSERT INTO course_specific_consent_form_questions (
139    id,
140    course_id,
141    research_consent_form_id,
142    question
143  )
144VALUES ($1, $2, $3, $4) ON CONFLICT (id)
145DO UPDATE SET question = $4,
146deleted_at = NULL
147RETURNING *
148",
149            question.question_id,
150            question.course_id,
151            question.research_consent_form_id,
152            question.question
153        )
154        .fetch_one(&mut *tx)
155        .await?;
156
157        inserted_questions.push(form_res);
158    }
159
160    tx.commit().await?;
161
162    Ok(inserted_questions)
163}
164
165pub async fn get_research_form_questions_with_course_id(
166    conn: &mut PgConnection,
167    course_id: Uuid,
168) -> ModelResult<Vec<ResearchFormQuestion>> {
169    let form_res = sqlx::query_as!(
170        ResearchFormQuestion,
171        "
172SELECT * FROM course_specific_consent_form_questions
173WHERE course_id = $1
174AND deleted_at IS NULL
175",
176        course_id,
177    )
178    .fetch_all(conn)
179    .await?;
180    Ok(form_res)
181}
182
183pub async fn get_question_by_id(
184    conn: &mut PgConnection,
185    question_id: Uuid,
186) -> ModelResult<ResearchFormQuestion> {
187    let res = sqlx::query_as!(
188        ResearchFormQuestion,
189        "
190SELECT *
191FROM course_specific_consent_form_questions
192WHERE id = $1
193  AND deleted_at IS NULL
194        ",
195        question_id
196    )
197    .fetch_one(conn)
198    .await?;
199    Ok(res)
200}
201
202pub struct ExportedCourseResearchFormQustionAnswer {
203    pub course_id: Uuid,
204    pub research_consent_form_id: Uuid,
205    pub research_form_question_id: Uuid,
206    pub question: String,
207    pub user_id: Uuid,
208    pub research_consent: bool,
209    pub created_at: DateTime<Utc>,
210    pub updated_at: DateTime<Utc>,
211}
212
213pub fn stream_course_research_form_user_answers(
214    conn: &mut PgConnection,
215    course_id: Uuid,
216) -> impl Stream<Item = sqlx::Result<ExportedCourseResearchFormQustionAnswer>> + '_ {
217    sqlx::query_as!(
218        ExportedCourseResearchFormQustionAnswer,
219        r#"
220    SELECT DISTINCT ON (a.research_form_question_id, a.user_id)
221        q.course_id,
222        q.research_consent_form_id,
223        a.research_form_question_id,
224        q.question,
225        a.user_id,
226        a.research_consent,
227        a.created_at,
228        a.updated_at
229        FROM course_specific_consent_form_answers a
230    LEFT JOIN course_specific_consent_form_questions q ON a.research_form_question_id = q.id
231    WHERE a.course_id = $1
232    AND a.deleted_at IS NULL
233    AND q.deleted_at IS NULL
234    ORDER BY a.user_id, a.research_form_question_id, a.updated_at DESC
235    "#,
236        course_id
237    )
238    .fetch(conn)
239}
240
241pub async fn upsert_research_form_anwser(
242    conn: &mut PgConnection,
243    course_id: Uuid,
244    answer: &NewResearchFormQuestionAnswer,
245) -> ModelResult<Uuid> {
246    let form_res = sqlx::query!(
247        "
248INSERT INTO course_specific_consent_form_answers (
249    user_id,
250    course_id,
251    research_form_question_id,
252    research_consent
253  )
254VALUES ($1, $2, $3, $4) ON CONFLICT (user_id, research_form_question_id)
255DO UPDATE SET research_consent = $4
256RETURNING *
257",
258        answer.user_id,
259        course_id,
260        answer.research_form_question_id,
261        answer.research_consent
262    )
263    .fetch_one(conn)
264    .await?;
265    Ok(form_res.id)
266}
267
268pub async fn upsert_answer_for_user_id_and_question_id(
269    conn: &mut PgConnection,
270    user_id: Uuid,
271    course_id: Uuid,
272    question_id: Uuid,
273    research_consent: bool,
274) -> ModelResult<Uuid> {
275    let res = sqlx::query!(
276        "
277INSERT INTO course_specific_consent_form_answers (
278    user_id,
279    course_id,
280    research_form_question_id,
281    research_consent
282)
283SELECT $1, q.course_id, q.id, $4
284FROM course_specific_consent_form_questions q
285WHERE q.id = $2
286  AND q.course_id = $3
287  AND q.deleted_at IS NULL
288ON CONFLICT (user_id, research_form_question_id)
289DO UPDATE SET
290  course_id = excluded.course_id,
291  research_consent = excluded.research_consent,
292  deleted_at = NULL
293RETURNING id
294        ",
295        user_id,
296        question_id,
297        course_id,
298        research_consent
299    )
300    .fetch_one(conn)
301    .await?;
302    Ok(res.id)
303}
304
305pub async fn get_research_form_answers_with_user_id(
306    conn: &mut PgConnection,
307    course_id: Uuid,
308    user_id: Uuid,
309) -> ModelResult<Vec<ResearchFormQuestionAnswer>> {
310    let form_res = sqlx::query_as!(
311        ResearchFormQuestionAnswer,
312        "
313SELECT * FROM course_specific_consent_form_answers
314WHERE course_id = $1 AND user_id = $2
315AND deleted_at IS NULL
316",
317        course_id,
318        user_id
319    )
320    .fetch_all(conn)
321    .await?;
322    Ok(form_res)
323}
324
325pub async fn get_all_research_form_answers_with_user_id(
326    conn: &mut PgConnection,
327    user_id: Uuid,
328) -> ModelResult<Vec<ResearchFormQuestionAnswer>> {
329    let form_res = sqlx::query_as!(
330        ResearchFormQuestionAnswer,
331        "
332SELECT * FROM course_specific_consent_form_answers
333WHERE user_id = $1
334AND deleted_at IS NULL
335",
336        user_id
337    )
338    .fetch_all(conn)
339    .await?;
340    Ok(form_res)
341}
342
343pub async fn get_all_research_form_answers_with_user_course_and_question_id(
344    conn: &mut PgConnection,
345    user_id: Uuid,
346    course_id: Uuid,
347    course_specific_consent_form_question_id: Uuid,
348) -> ModelResult<Vec<ResearchFormQuestionAnswer>> {
349    let form_res = sqlx::query_as!(
350        ResearchFormQuestionAnswer,
351        "
352SELECT * FROM course_specific_consent_form_answers
353WHERE user_id = $1
354AND course_id = $2
355AND research_form_question_id = $3
356AND deleted_at IS NULL
357",
358        user_id,
359        course_id,
360        course_specific_consent_form_question_id
361    )
362    .fetch_all(conn)
363    .await?;
364    Ok(form_res)
365}