Skip to main content

headless_lms_models/course_module_suotar_configurations/
pause.rs

1//! Pausing and resuming a module's credit registration.
2
3use crate::prelude::*;
4
5/// Who paused a module's credit registration, and why. One value rather than three arguments
6/// because `course_module_suotar_configurations_pause_pair` rejects a timestamp without an actor.
7#[derive(Debug, Clone)]
8pub struct SuotarPause<'a> {
9    pub paused_at: DateTime<Utc>,
10    pub paused_by_user_id: Uuid,
11    pub reason: Option<&'a str>,
12}
13
14/// Pauses or resumes the module. Every phase's claim query skips a paused module, so pausing
15/// freezes its ledger rows where they stand instead of cancelling them. Pausing a paused module
16/// only updates who paused it and why. `None` resumes, and moves the rows' enrolment check
17/// schedules on by as long as the pause lasted.
18pub async fn set_paused(
19    conn: &mut PgConnection,
20    course_module_id: Uuid,
21    pause: Option<SuotarPause<'_>>,
22) -> ModelResult<()> {
23    let pause = pause.as_ref();
24    let mut tx = conn.begin().await?;
25    let previously_paused_at = sqlx::query_scalar!(
26        r#"
27SELECT paused_at
28FROM course_module_suotar_configurations
29WHERE course_module_id = $1
30  AND deleted_at IS NULL
31FOR UPDATE
32        "#,
33        course_module_id,
34    )
35    .fetch_optional(&mut *tx)
36    .await?
37    .flatten();
38    sqlx::query!(
39        r#"
40UPDATE course_module_suotar_configurations
41SET paused_at = CASE
42    WHEN $2::timestamptz IS NOT NULL THEN COALESCE(paused_at, $2)
43  END,
44  paused_by_user_id = $3,
45  pause_reason = $4
46WHERE course_module_id = $1
47  AND deleted_at IS NULL
48        "#,
49        course_module_id,
50        pause.map(|pause| pause.paused_at),
51        pause.map(|pause| pause.paused_by_user_id),
52        pause.and_then(|pause| pause.reason),
53    )
54    .execute(&mut *tx)
55    .await?;
56    if pause.is_none()
57        && let Some(paused_at) = previously_paused_at
58    {
59        crate::library::credit_registration::enrolment_checks::shift_past_pause(
60            &mut tx,
61            course_module_id,
62            // Whole seconds: Postgres intervals hold no nanoseconds, which the clock has.
63            chrono::TimeDelta::seconds((Utc::now() - paused_at).num_seconds()),
64        )
65        .await?;
66    }
67    tx.commit().await?;
68    Ok(())
69}