Skip to main content

headless_lms_models/
code_giveaways.rs

1use crate::prelude::*;
2use utoipa::ToSchema;
3
4#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
5
6pub struct CodeGiveaway {
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_id: Uuid,
12    pub course_module_id: Option<Uuid>,
13    pub require_course_specific_consent_form_question_id: Option<Uuid>,
14    pub enabled: bool,
15    pub name: String,
16}
17
18#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
19
20pub struct NewCodeGiveaway {
21    pub course_id: Uuid,
22    pub name: String,
23    pub course_module_id: Option<Uuid>,
24    pub require_course_specific_consent_form_question_id: Option<Uuid>,
25}
26
27#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
28#[serde(tag = "tag")]
29pub enum CodeGiveawayStatus {
30    Disabled,
31    NotEligible,
32    Eligible {
33        codes_left: bool,
34    },
35    AlreadyGottenCode {
36        #[schema(value_type = String)]
37        given_code: OutboundSecret,
38    },
39}
40
41pub async fn insert(conn: &mut PgConnection, input: &NewCodeGiveaway) -> ModelResult<CodeGiveaway> {
42    let res = sqlx::query_as!(
43        CodeGiveaway,
44        r#"
45INSERT INTO code_giveaways (course_id, name, course_module_id, require_course_specific_consent_form_question_id)
46VALUES ($1, $2, $3, $4)
47RETURNING *
48        "#,
49        input.course_id,
50        input.name,
51        input.course_module_id,
52        input.require_course_specific_consent_form_question_id
53    )
54    .fetch_one(&mut *conn)
55    .await?;
56
57    Ok(res)
58}
59
60pub async fn get_all_for_course(
61    conn: &mut PgConnection,
62    course_id: Uuid,
63) -> ModelResult<Vec<CodeGiveaway>> {
64    let res = sqlx::query_as!(
65        CodeGiveaway,
66        r#"
67SELECT *
68FROM code_giveaways
69WHERE course_id = $1
70  AND deleted_at IS NULL
71"#,
72        course_id
73    )
74    .fetch_all(&mut *conn)
75    .await?;
76
77    Ok(res)
78}
79
80pub async fn get_by_id(conn: &mut PgConnection, id: Uuid) -> ModelResult<CodeGiveaway> {
81    let res = sqlx::query_as!(
82        CodeGiveaway,
83        r#"
84SELECT *
85FROM code_giveaways
86WHERE id = $1
87"#,
88        id
89    )
90    .fetch_one(&mut *conn)
91    .await?;
92
93    Ok(res)
94}
95
96pub async fn set_enabled(
97    conn: &mut PgConnection,
98    id: Uuid,
99    enabled: bool,
100) -> ModelResult<CodeGiveaway> {
101    let res = sqlx::query_as!(
102        CodeGiveaway,
103        r#"
104UPDATE code_giveaways
105SET enabled = $2
106WHERE id = $1
107RETURNING *
108"#,
109        id,
110        enabled
111    )
112    .fetch_one(&mut *conn)
113    .await?;
114
115    Ok(res)
116}
117
118pub async fn get_code_giveaway_status(
119    conn: &mut PgConnection,
120    code_giveaway_id: Uuid,
121    user_id: Uuid,
122) -> ModelResult<CodeGiveawayStatus> {
123    let code_giveaway = get_by_id(conn, code_giveaway_id).await?;
124    if !code_giveaway.enabled {
125        return Ok(CodeGiveawayStatus::Disabled);
126    }
127
128    if let Some(course_module_id) = code_giveaway.course_module_id {
129        let course_module_completions =
130            crate::course_module_completions::get_all_by_user_id_and_course_module_id(
131                conn,
132                user_id,
133                course_module_id,
134            )
135            .await?;
136
137        if !course_module_completions.iter().any(|c| c.passed) {
138            return Ok(CodeGiveawayStatus::NotEligible);
139        }
140    } else {
141        warn!(
142            "Code giveaway {} does not have a course module requirement",
143            code_giveaway_id
144        );
145        return Ok(CodeGiveawayStatus::Disabled);
146    }
147    if let Some(question_id) = code_giveaway.require_course_specific_consent_form_question_id {
148        let research_form_answers =
149            crate::research_forms::get_all_research_form_answers_with_user_course_and_question_id(
150                conn,
151                user_id,
152                code_giveaway.course_id,
153                question_id,
154            )
155            .await?;
156
157        if !research_form_answers.iter().any(|a| a.research_consent) {
158            return Ok(CodeGiveawayStatus::NotEligible);
159        }
160    }
161    let already_given_code =
162        crate::code_giveaway_codes::get_code_given_to_user(conn, code_giveaway_id, user_id).await?;
163
164    if let Some(code) = already_given_code {
165        return Ok(CodeGiveawayStatus::AlreadyGottenCode {
166            given_code: code.code,
167        });
168    }
169
170    let codes_left = crate::code_giveaway_codes::are_any_codes_left(conn, code_giveaway_id).await?;
171
172    Ok(CodeGiveawayStatus::Eligible { codes_left })
173}