Skip to main content

headless_lms_models/
credit_registration_enrolment_routes.rs

1//! What a student said about enrolling, on the new registration page.
2//!
3//! Advisory throughout: the pipeline never reads these rows. They decide which enrolment
4//! instructions the page shows and how far its step list has come, and the study registry is what
5//! actually settles whether an enrolment exists.
6
7use utoipa::ToSchema;
8
9use crate::prelude::*;
10
11/// Which university relationship a student picked, which decides only where they are told to enrol.
12#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy, Type, ToSchema)]
13#[sqlx(
14    type_name = "credit_registration_enrolment_route",
15    rename_all = "snake_case"
16)]
17#[serde(rename_all = "snake_case")]
18pub enum CreditRegistrationEnrolmentRoute {
19    UniversityOfHelsinki,
20    OpenUniversity,
21}
22
23#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
24pub struct EnrolmentRouteAnswer {
25    pub course_module_completion_id: Uuid,
26    pub route: CreditRegistrationEnrolmentRoute,
27    /// When the student pressed Done. `None` until they do, and cleared again if they take it back.
28    pub enrolment_confirmed_at: Option<DateTime<Utc>>,
29}
30
31pub async fn get_by_completion_id(
32    conn: &mut PgConnection,
33    course_module_completion_id: Uuid,
34) -> ModelResult<Option<EnrolmentRouteAnswer>> {
35    let res = sqlx::query_as!(
36        EnrolmentRouteAnswer,
37        r#"
38SELECT course_module_completion_id,
39  route AS "route: _",
40  enrolment_confirmed_at
41FROM credit_registration_enrolment_routes
42WHERE course_module_completion_id = $1
43  AND deleted_at IS NULL
44        "#,
45        course_module_completion_id,
46    )
47    .fetch_optional(conn)
48    .await?;
49    Ok(res)
50}
51
52/// Records the student's answer, replacing any earlier one.
53///
54/// Changing the answer clears the Done that went with it: the confirmation was about enrolling the
55/// way the old answer described, and it says nothing about the new one.
56pub async fn set_route(
57    conn: &mut PgConnection,
58    course_module_completion_id: Uuid,
59    user_id: Uuid,
60    route: CreditRegistrationEnrolmentRoute,
61) -> ModelResult<EnrolmentRouteAnswer> {
62    let res = sqlx::query_as!(
63        EnrolmentRouteAnswer,
64        r#"
65INSERT INTO credit_registration_enrolment_routes (
66    course_module_completion_id,
67    user_id,
68    route
69  )
70VALUES ($1, $2, $3)
71ON CONFLICT (course_module_completion_id, deleted_at) DO
72UPDATE
73SET route = EXCLUDED.route,
74  enrolment_confirmed_at = CASE
75    WHEN credit_registration_enrolment_routes.route = EXCLUDED.route THEN credit_registration_enrolment_routes.enrolment_confirmed_at
76    ELSE NULL
77  END
78RETURNING course_module_completion_id,
79  route AS "route: _",
80  enrolment_confirmed_at
81        "#,
82        course_module_completion_id,
83        user_id,
84        route as CreditRegistrationEnrolmentRoute,
85    )
86    .fetch_one(conn)
87    .await?;
88    Ok(res)
89}
90
91/// Sets or clears the Done the student pressed to say they had enrolled.
92pub async fn set_enrolment_confirmed(
93    conn: &mut PgConnection,
94    course_module_completion_id: Uuid,
95    confirmed: bool,
96) -> ModelResult<EnrolmentRouteAnswer> {
97    let res = sqlx::query_as!(
98        EnrolmentRouteAnswer,
99        r#"
100UPDATE credit_registration_enrolment_routes
101SET enrolment_confirmed_at = CASE
102    WHEN $2 THEN COALESCE(enrolment_confirmed_at, now())
103    ELSE NULL
104  END
105WHERE course_module_completion_id = $1
106  AND deleted_at IS NULL
107RETURNING course_module_completion_id,
108  route AS "route: _",
109  enrolment_confirmed_at
110        "#,
111        course_module_completion_id,
112        confirmed,
113    )
114    .fetch_one(conn)
115    .await?;
116    Ok(res)
117}