1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use crate::prelude::*;
use headless_lms_utils as utils;

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "ts_rs", derive(TS))]
pub struct GeneratedCertificate {
    pub id: Uuid,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,
    pub deleted_at: Option<DateTime<Utc>>,
    pub user_id: Uuid,
    pub name_on_certificate: String,
    pub verification_id: String,
    pub certificate_configuration_id: Uuid,
}

pub async fn get_certificate_for_user(
    conn: &mut PgConnection,
    user_id: Uuid,
    certificate_configuration_id: Uuid,
) -> ModelResult<GeneratedCertificate> {
    let res = sqlx::query_as!(
        GeneratedCertificate,
        "
SELECT *
FROM generated_certificates
WHERE user_id = $1
  AND certificate_configuration_id = $2
  AND deleted_at IS NULL
",
        user_id,
        certificate_configuration_id
    )
    .fetch_one(conn)
    .await?;
    Ok(res)
}
/// Verifies that the user has completed the given module and creates the certificate in the database.
pub async fn generate_and_insert(
    conn: &mut PgConnection,
    user_id: Uuid,
    name_on_certificate: &str,
    certificate_configuration_id: Uuid,
) -> ModelResult<GeneratedCertificate> {
    let requirements = crate::certificate_configuration_to_requirements::get_all_requirements_for_certificate_configuration(conn, certificate_configuration_id).await?;
    // Verify that the user has completed the module in the course instance
    if !requirements
        .has_user_completed_all_requirements(conn, user_id)
        .await?
    {
        return Err(ModelError::new(
            ModelErrorType::PreconditionFailed,
            "User has not completed all the requirements to be eligible for this certificate."
                .to_string(),
            None,
        ));
    }

    // Verify that a certificate doesn't already exist
    if sqlx::query!(
        "
SELECT id
FROM generated_certificates
WHERE user_id = $1
    AND certificate_configuration_id = $2
    AND deleted_at IS NULL
",
        user_id,
        certificate_configuration_id,
    )
    .fetch_optional(&mut *conn)
    .await?
    .is_some()
    {
        // Certificate already exists
        return Err(ModelError::new(
            ModelErrorType::PreconditionFailed,
            "User already has a certificate for the given module and course instance".to_string(),
            None,
        ));
    }

    let verification_id = generate_verification_id();
    let res = sqlx::query_as!(
        GeneratedCertificate,
        "
INSERT INTO generated_certificates (
    user_id,
    certificate_configuration_id,
    name_on_certificate,
    verification_id
  )
VALUES ($1, $2, $3, $4)
RETURNING *
",
        user_id,
        certificate_configuration_id,
        name_on_certificate,
        verification_id
    )
    .fetch_one(conn)
    .await?;
    Ok(res)
}

pub async fn get_certificate_by_verification_id(
    conn: &mut PgConnection,
    certificate_verification_id: &str,
) -> ModelResult<GeneratedCertificate> {
    let res = sqlx::query_as!(
        GeneratedCertificate,
        "
SELECT *
FROM generated_certificates
WHERE verification_id = $1
  AND deleted_at IS NULL
",
        certificate_verification_id
    )
    .fetch_one(conn)
    .await?;
    Ok(res)
}

fn generate_verification_id() -> String {
    utils::strings::generate_easily_writable_random_string(15)
}