headless_lms_models/
research_forms.rs

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