headless_lms_models/
code_giveaways.rs

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