headless_lms_models/
user_course_instance_exercise_service_variables.rs

1use std::collections::HashMap;
2
3use crate::{
4    exercise_tasks::ExerciseTask,
5    prelude::*,
6    user_exercise_states::{CourseInstanceOrExamId, UserExerciseState},
7};
8
9#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
10#[cfg_attr(feature = "ts_rs", derive(TS))]
11pub struct UserCourseInstanceExerciseServiceVariable {
12    pub id: Uuid,
13    pub created_at: DateTime<Utc>,
14    pub updated_at: DateTime<Utc>,
15    pub deleted_at: Option<DateTime<Utc>>,
16    pub exercise_service_slug: String,
17    pub user_id: Uuid,
18    pub course_instance_id: Option<Uuid>,
19    pub exam_id: Option<Uuid>,
20    pub variable_key: String,
21    pub variable_value: serde_json::Value,
22}
23
24pub(crate) async fn get_all_variables_for_user_and_course_instance_or_exam(
25    conn: &mut PgConnection,
26    user_id: Uuid,
27    instance_or_exam_id: CourseInstanceOrExamId,
28) -> ModelResult<Vec<UserCourseInstanceExerciseServiceVariable>> {
29    let (course_instance_id, exam_id) = instance_or_exam_id.to_instance_and_exam_ids();
30    let res = sqlx::query_as!(
31        UserCourseInstanceExerciseServiceVariable,
32        r#"
33SELECT *
34FROM user_course_instance_exercise_service_variables
35WHERE deleted_at IS NULL
36  AND user_id = $1
37  AND (course_instance_id = $2 OR course_instance_id IS NULL)
38  AND (exam_id = $3 OR exam_id IS NULL);
39    "#,
40        user_id,
41        course_instance_id,
42        exam_id
43    )
44    .fetch_all(conn)
45    .await?;
46    Ok(res)
47}
48
49pub async fn get_all_user_variables_for_user_and_course_instance_and_exercise_type(
50    conn: &mut PgConnection,
51    user_id: Uuid,
52    course_instance_id: Uuid,
53    exercise_type: &str,
54) -> ModelResult<Vec<UserCourseInstanceExerciseServiceVariable>> {
55    let res = sqlx::query_as!(
56        UserCourseInstanceExerciseServiceVariable,
57        r#"
58SELECT *
59FROM user_course_instance_exercise_service_variables
60WHERE deleted_at IS NULL
61  AND user_id = $1
62  AND course_instance_id = $2
63  AND exercise_service_slug = $3;
64    "#,
65        user_id,
66        course_instance_id,
67        exercise_type
68    )
69    .fetch_all(conn)
70    .await?;
71    Ok(res)
72}
73
74pub(crate) async fn insert_after_exercise_task_graded(
75    conn: &mut PgConnection,
76    set_user_variables: &Option<HashMap<String, serde_json::Value>>,
77    exercise_task: &ExerciseTask,
78    user_exercise_state: &UserExerciseState,
79) -> ModelResult<()> {
80    if let Some(set_user_variables) = set_user_variables {
81        for (k, v) in set_user_variables {
82            sqlx::query!(
83                r#"
84INSERT INTO user_course_instance_exercise_service_variables (
85    exercise_service_slug,
86    user_id,
87    course_instance_id,
88    exam_id,
89    variable_key,
90    variable_value
91  )
92VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (
93    variable_key,
94    user_id,
95    course_instance_id,
96    exercise_service_slug,
97    exam_id,
98    deleted_at
99  ) DO
100UPDATE
101SET variable_value = $6;
102    "#,
103                exercise_task.exercise_type,
104                user_exercise_state.user_id,
105                user_exercise_state.course_instance_id,
106                user_exercise_state.exam_id,
107                k,
108                v
109            )
110            .execute(&mut *conn)
111            .await?;
112        }
113        Ok(())
114    } else {
115        Ok(())
116    }
117}