Skip to main content

headless_lms_models/
credit_registration_enrolment_check_signals.rs

1//! Visits to the registration page and check requests, per completion, kept whether or not the
2//! completion has a ledger row or a linked student number yet. A row that starts waiting for an
3//! enrolment takes its group from these, see [`crate::library::credit_registration::preconditions`],
4//! and a student waiting for a student number makes their course code's enrolment list due by
5//! them, see [`crate::credit_registration_roster_schedules`].
6
7use crate::library::credit_registration::enrolment_check_schedule::{
8    CHECK_REQUEST_RESTART_WINDOW, EnrolmentCheckSource, VISIT_RESTART_MIN_INTERVAL,
9};
10use crate::prelude::*;
11
12/// Records a visit.
13pub async fn record_visit(
14    conn: &mut PgConnection,
15    course_module_completion_id: Uuid,
16) -> ModelResult<()> {
17    sqlx::query!(
18        r#"
19INSERT INTO credit_registration_enrolment_check_signals (
20    course_module_completion_id,
21    last_visited_at,
22    visit_ladder_anchor_at
23  )
24VALUES ($1, now(), now()) ON CONFLICT (course_module_completion_id, deleted_at) DO
25UPDATE
26SET last_visited_at = now(),
27  visit_ladder_anchor_at = restarted_ladder_anchor(
28    credit_registration_enrolment_check_signals.visit_ladder_anchor_at,
29    $2::bigint * INTERVAL '1 second'
30  )
31        "#,
32        course_module_completion_id,
33        VISIT_RESTART_MIN_INTERVAL.num_seconds(),
34    )
35    .execute(conn)
36    .await?;
37    Ok(())
38}
39
40/// Records a check request. `source` is who asked: the student, a teacher, or a link made from a
41/// roster mail.
42pub async fn record_check_request(
43    conn: &mut PgConnection,
44    course_module_completion_id: Uuid,
45    source: EnrolmentCheckSource,
46) -> ModelResult<()> {
47    sqlx::query!(
48        r#"
49INSERT INTO credit_registration_enrolment_check_signals (
50    course_module_completion_id,
51    last_check_requested_at,
52    check_request_ladder_anchor_at,
53    check_request_source
54  )
55VALUES ($1, now(), now(), $2) ON CONFLICT (course_module_completion_id, deleted_at) DO
56UPDATE
57SET last_check_requested_at = now(),
58  check_request_ladder_anchor_at = restarted_ladder_anchor(
59    credit_registration_enrolment_check_signals.check_request_ladder_anchor_at,
60    $3::bigint * INTERVAL '1 second'
61  ),
62  check_request_source = EXCLUDED.check_request_source
63        "#,
64        course_module_completion_id,
65        source as EnrolmentCheckSource,
66        CHECK_REQUEST_RESTART_WINDOW.num_seconds(),
67    )
68    .execute(conn)
69    .await?;
70    Ok(())
71}
72
73/// Records a check request from a link made off a roster mail for every live push-path completion
74/// the user has on a module sharing a course code with one of the course's modules, whichever
75/// course it is on: the mail does not say which code's list it came from. Returns those
76/// completions.
77pub async fn record_account_link_for_course(
78    conn: &mut PgConnection,
79    user_id: Uuid,
80    course_id: Uuid,
81) -> ModelResult<Vec<Uuid>> {
82    let res = sqlx::query_scalar!(
83        r#"
84INSERT INTO credit_registration_enrolment_check_signals (
85    course_module_completion_id,
86    last_check_requested_at,
87    check_request_ladder_anchor_at,
88    check_request_source
89  )
90SELECT cmc.id,
91  now(),
92  now(),
93  'account_link'
94FROM course_module_completions cmc
95  JOIN course_modules cm ON cm.id = cmc.course_module_id
96WHERE cmc.user_id = $1
97  AND cmc.register_credits_via_suotar
98  AND cmc.deleted_at IS NULL
99  AND (
100    cmc.course_id = $2
101    OR TRIM(cm.uh_course_code) IN (
102      SELECT TRIM(linked.uh_course_code)
103      FROM course_modules linked
104      WHERE linked.course_id = $2
105        AND linked.deleted_at IS NULL
106        AND TRIM(COALESCE(linked.uh_course_code, '')) <> ''
107    )
108  ) ON CONFLICT (course_module_completion_id, deleted_at) DO
109UPDATE
110SET last_check_requested_at = now(),
111  check_request_ladder_anchor_at = restarted_ladder_anchor(
112    credit_registration_enrolment_check_signals.check_request_ladder_anchor_at,
113    $3::bigint * INTERVAL '1 second'
114  ),
115  check_request_source = EXCLUDED.check_request_source
116RETURNING course_module_completion_id
117        "#,
118        user_id,
119        course_id,
120        CHECK_REQUEST_RESTART_WINDOW.num_seconds(),
121    )
122    .fetch_all(conn)
123    .await?;
124    Ok(res)
125}