headless_lms_models/library/credit_registration/
sisu_day_gap.rs1use chrono::TimeDelta;
7
8use crate::prelude::*;
9
10const SISU_DAY_GAP_LEAD: TimeDelta = TimeDelta::minutes(5);
13const SISU_DAY_GAP_TAIL: TimeDelta = TimeDelta::minutes(5);
15const SISU_DAY_GAP_SPREAD: TimeDelta = TimeDelta::hours(2);
18
19pub async fn current_gap_end(conn: &mut PgConnection) -> ModelResult<Option<DateTime<Utc>>> {
22 let gap_end = sqlx::query_scalar!(
23 r#"
24SELECT CASE
25 WHEN ((now() + make_interval(secs => $1)) AT TIME ZONE 'Europe/Helsinki')::date
26 > ((now() - make_interval(secs => $2)) AT TIME ZONE 'UTC')::date
27 THEN (
28 ((now() + make_interval(secs => $1)) AT TIME ZONE 'Europe/Helsinki')::date::timestamp
29 AT TIME ZONE 'UTC'
30 ) + make_interval(secs => $2)
31 END AS "gap_end?: DateTime<Utc>"
32 "#,
33 SISU_DAY_GAP_LEAD.num_seconds() as f64,
34 SISU_DAY_GAP_TAIL.num_seconds() as f64,
35 )
36 .fetch_one(conn)
37 .await?;
38 Ok(gap_end)
39}
40
41pub async fn spread_imports_past_gap(
45 conn: &mut PgConnection,
46 gap_end: DateTime<Utc>,
47) -> ModelResult<u64> {
48 let moved = sqlx::query!(
49 r#"
50UPDATE credit_registrations
51SET next_attempt_at = $1 + random() * make_interval(secs => $2)
52WHERE state = 'checking_enrolment'
53 AND deleted_at IS NULL
54 AND next_attempt_at < $1
55 "#,
56 gap_end,
57 SISU_DAY_GAP_SPREAD.num_seconds() as f64,
58 )
59 .execute(conn)
60 .await?;
61 Ok(moved.rows_affected())
62}