Skip to main content

headless_lms_models/
credit_registration_daily_snapshots.rs

1//! Daily queue depth per ledger state.
2//!
3//! The ledger holds current state only, so a row that passed through a state in an hour leaves no
4//! depth trace. Aggregates only: anything per-person belongs in the ledger.
5use chrono::NaiveDate;
6use utoipa::ToSchema;
7
8use crate::credit_registrations::CreditRegistrationState;
9use crate::prelude::*;
10
11#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
12pub struct CreditRegistrationDailySnapshot {
13    pub id: Uuid,
14    pub created_at: DateTime<Utc>,
15    pub updated_at: DateTime<Utc>,
16    pub deleted_at: Option<DateTime<Utc>>,
17    pub snapshot_date: NaiveDate,
18    pub state: CreditRegistrationState,
19    pub count: i32,
20    pub entered_count: i32,
21    pub left_count: i32,
22}
23
24#[derive(Debug, Clone, PartialEq)]
25pub struct DailyStateCounts {
26    pub state: CreditRegistrationState,
27    pub count: i32,
28    pub entered_count: i32,
29    pub left_count: i32,
30}
31
32/// Writes one day's counts. Idempotent, so a re-run cannot double-count.
33pub async fn write_snapshot_for_date(
34    conn: &mut PgConnection,
35    snapshot_date: NaiveDate,
36    counts: &[DailyStateCounts],
37) -> ModelResult<()> {
38    for row in counts {
39        sqlx::query!(
40            r#"
41INSERT INTO credit_registration_daily_snapshots (
42    snapshot_date,
43    state,
44    count,
45    entered_count,
46    left_count
47  )
48VALUES ($1, $2, $3, $4, $5) ON CONFLICT (snapshot_date, state, deleted_at) DO
49UPDATE
50SET count = $3,
51  entered_count = $4,
52  left_count = $5
53            "#,
54            snapshot_date,
55            row.state as CreditRegistrationState,
56            row.count,
57            row.entered_count,
58            row.left_count,
59        )
60        .execute(&mut *conn)
61        .await?;
62    }
63    Ok(())
64}
65
66pub async fn get_between(
67    conn: &mut PgConnection,
68    from: NaiveDate,
69    to: NaiveDate,
70) -> ModelResult<Vec<CreditRegistrationDailySnapshot>> {
71    let res = sqlx::query_as!(
72        CreditRegistrationDailySnapshot,
73        r#"
74SELECT *
75FROM credit_registration_daily_snapshots
76WHERE snapshot_date BETWEEN $1 AND $2
77  AND deleted_at IS NULL
78ORDER BY snapshot_date,
79  state
80        "#,
81        from,
82        to,
83    )
84    .fetch_all(conn)
85    .await?;
86    Ok(res)
87}
88
89pub async fn get_series_for_state(
90    conn: &mut PgConnection,
91    state: CreditRegistrationState,
92    from: NaiveDate,
93    to: NaiveDate,
94) -> ModelResult<Vec<CreditRegistrationDailySnapshot>> {
95    let res = sqlx::query_as!(
96        CreditRegistrationDailySnapshot,
97        r#"
98SELECT *
99FROM credit_registration_daily_snapshots
100WHERE state = $1
101  AND snapshot_date BETWEEN $2 AND $3
102  AND deleted_at IS NULL
103ORDER BY snapshot_date
104        "#,
105        state as CreditRegistrationState,
106        from,
107        to,
108    )
109    .fetch_all(conn)
110    .await?;
111    Ok(res)
112}