Skip to main content

headless_lms_models/
course_background_question_answers.rs

1use crate::{course_background_questions::CourseBackgroundQuestion, prelude::*};
2use utoipa::ToSchema;
3
4#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
5
6pub struct CourseBackgroundQuestionAnswer {
7    pub id: Uuid,
8    pub created_at: DateTime<Utc>,
9    pub updated_at: DateTime<Utc>,
10    pub deleted_at: Option<DateTime<Utc>>,
11    pub course_background_question_id: Uuid,
12    pub answer_value: Option<String>,
13    pub user_id: Uuid,
14}
15
16#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
17
18pub struct NewCourseBackgroundQuestionAnswer {
19    pub answer_value: Option<String>,
20    pub course_background_question_id: Uuid,
21}
22
23pub async fn get_background_question_answers_for_background_questions(
24    conn: &mut PgConnection,
25    user_id: Uuid,
26    background_questions: &[CourseBackgroundQuestion],
27) -> ModelResult<Vec<CourseBackgroundQuestionAnswer>> {
28    let ids = background_questions
29        .iter()
30        .map(|o| o.id)
31        .collect::<Vec<_>>();
32    let res: Vec<CourseBackgroundQuestionAnswer> = sqlx::query_as!(
33        CourseBackgroundQuestionAnswer,
34        r#"
35SELECT *
36FROM course_background_question_answers
37WHERE deleted_at IS NULL
38AND user_id = $1
39AND course_background_question_id IN (
40    SELECT UNNEST($2::uuid [])
41  )
42  "#,
43        user_id,
44        &ids,
45    )
46    .fetch_all(conn)
47    .await?;
48    Ok(res)
49}
50
51pub async fn upsert_backround_question_answers(
52    conn: &mut PgConnection,
53    user_id: Uuid,
54    background_question_answers: &[NewCourseBackgroundQuestionAnswer],
55) -> ModelResult<()> {
56    let mut tx = conn.begin().await?;
57    for answer in background_question_answers {
58        sqlx::query!(
59            r#"
60INSERT INTO course_background_question_answers (
61    course_background_question_id,
62    user_id,
63    answer_value
64  )
65VALUES ($1, $2, $3) ON CONFLICT (
66    course_background_question_id,
67    user_id,
68    deleted_at
69  ) DO
70UPDATE
71SET answer_value = $3
72        "#,
73            answer.course_background_question_id,
74            user_id,
75            answer.answer_value
76        )
77        .execute(&mut *tx)
78        .await?;
79    }
80
81    tx.commit().await?;
82
83    Ok(())
84}
85
86/// Upserts a user's background question answers for allowed question ids only.
87///
88/// Validates that each answer targets a question id contained in
89/// `allowed_question_ids`; rejected answers return a precondition error.
90pub async fn upsert_by_user_id_and_question_ids(
91    conn: &mut PgConnection,
92    user_id: Uuid,
93    background_question_answers: &[NewCourseBackgroundQuestionAnswer],
94    allowed_question_ids: &[Uuid],
95) -> ModelResult<()> {
96    let mut tx = conn.begin().await?;
97    for answer in background_question_answers {
98        let result = sqlx::query!(
99            r#"
100INSERT INTO course_background_question_answers (
101    course_background_question_id,
102    user_id,
103    answer_value
104)
105SELECT q.id, $2, $3
106FROM course_background_questions q
107WHERE q.id = $1
108  AND q.id = ANY($4)
109  AND q.deleted_at IS NULL
110ON CONFLICT (
111    course_background_question_id,
112    user_id,
113    deleted_at
114) DO UPDATE SET answer_value = EXCLUDED.answer_value
115            "#,
116            answer.course_background_question_id,
117            user_id,
118            answer.answer_value,
119            allowed_question_ids
120        )
121        .execute(&mut *tx)
122        .await;
123
124        let result = match result {
125            Ok(result) => result,
126            Err(err) => {
127                tx.rollback().await?;
128                return Err(err.into());
129            }
130        };
131
132        if result.rows_affected() == 0 {
133            tx.rollback().await?;
134            return Err(model_err!(
135                PreconditionFailed,
136                "Background question is not in the allowed question set"
137            ));
138        }
139    }
140
141    tx.commit().await?;
142
143    Ok(())
144}