headless_lms_models/
generated_certificates.rs

1use crate::prelude::*;
2use headless_lms_utils as utils;
3
4#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
5#[cfg_attr(feature = "ts_rs", derive(TS))]
6pub struct GeneratedCertificate {
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 user_id: Uuid,
12    pub name_on_certificate: String,
13    pub verification_id: String,
14    pub certificate_configuration_id: Uuid,
15}
16
17pub async fn get_certificate_for_user(
18    conn: &mut PgConnection,
19    user_id: Uuid,
20    certificate_configuration_id: Uuid,
21) -> ModelResult<GeneratedCertificate> {
22    let res = sqlx::query_as!(
23        GeneratedCertificate,
24        "
25SELECT *
26FROM generated_certificates
27WHERE user_id = $1
28  AND certificate_configuration_id = $2
29  AND deleted_at IS NULL
30",
31        user_id,
32        certificate_configuration_id
33    )
34    .fetch_one(conn)
35    .await?;
36    Ok(res)
37}
38/// Verifies that the user has completed the given module and creates the certificate in the database.
39pub async fn generate_and_insert(
40    conn: &mut PgConnection,
41    user_id: Uuid,
42    name_on_certificate: &str,
43    certificate_configuration_id: Uuid,
44) -> ModelResult<GeneratedCertificate> {
45    let requirements = crate::certificate_configuration_to_requirements::get_all_requirements_for_certificate_configuration(conn, certificate_configuration_id).await?;
46    // Verify that the user has completed the module in the course instance
47    if !requirements
48        .has_user_completed_all_requirements(conn, user_id)
49        .await?
50    {
51        return Err(ModelError::new(
52            ModelErrorType::PreconditionFailed,
53            "User has not completed all the requirements to be eligible for this certificate."
54                .to_string(),
55            None,
56        ));
57    }
58
59    // Verify that a certificate doesn't already exist
60    if sqlx::query!(
61        "
62SELECT id
63FROM generated_certificates
64WHERE user_id = $1
65    AND certificate_configuration_id = $2
66    AND deleted_at IS NULL
67",
68        user_id,
69        certificate_configuration_id,
70    )
71    .fetch_optional(&mut *conn)
72    .await?
73    .is_some()
74    {
75        // Certificate already exists
76        return Err(ModelError::new(
77            ModelErrorType::PreconditionFailed,
78            "User already has a certificate for the given module and course instance".to_string(),
79            None,
80        ));
81    }
82
83    let verification_id = generate_verification_id();
84    let res = sqlx::query_as!(
85        GeneratedCertificate,
86        "
87INSERT INTO generated_certificates (
88    user_id,
89    certificate_configuration_id,
90    name_on_certificate,
91    verification_id
92  )
93VALUES ($1, $2, $3, $4)
94RETURNING *
95",
96        user_id,
97        certificate_configuration_id,
98        name_on_certificate,
99        verification_id,
100    )
101    .fetch_one(conn)
102    .await?;
103    Ok(res)
104}
105
106pub async fn get_certificate_by_verification_id(
107    conn: &mut PgConnection,
108    certificate_verification_id: &str,
109) -> ModelResult<GeneratedCertificate> {
110    let res = sqlx::query_as!(
111        GeneratedCertificate,
112        "
113SELECT *
114FROM generated_certificates
115WHERE verification_id = $1
116  AND deleted_at IS NULL
117",
118        certificate_verification_id
119    )
120    .fetch_one(conn)
121    .await?;
122    Ok(res)
123}
124
125fn generate_verification_id() -> String {
126    utils::strings::generate_easily_writable_random_string(15)
127}