headless_lms_server/controllers/main_frontend/credit_registration_admin/
history.rs1use chrono::{Duration, NaiveDate};
8use headless_lms_models::credit_registration_daily_snapshots;
9use headless_lms_models::credit_registrations::CreditRegistrationState;
10use utoipa::ToSchema;
11
12use crate::prelude::*;
13
14use super::authorize_credit_registration_admin;
15
16const DEFAULT_DAYS: i64 = 30;
17const MAX_DAYS: i64 = 365;
18
19#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
21pub struct CreditRegistrationHistoryPoint {
22 pub state: CreditRegistrationState,
23 pub count: i32,
25 pub entered_count: i32,
27 pub left_count: i32,
28}
29
30#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
31pub struct CreditRegistrationHistoryDay {
32 pub snapshot_date: NaiveDate,
34 pub states: Vec<CreditRegistrationHistoryPoint>,
37}
38
39#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
40pub struct CreditRegistrationHistory {
41 pub days: Vec<CreditRegistrationHistoryDay>,
44 pub from: NaiveDate,
45 pub to: NaiveDate,
46}
47
48#[derive(Debug, Deserialize)]
49pub struct HistoryQuery {
50 days: Option<i64>,
51}
52
53#[instrument(skip(pool))]
58#[utoipa::path(
59 get,
60 path = "/pipeline-history",
61 operation_id = "getCreditRegistrationPipelineHistory",
62 tag = "credit-registration-admin",
63 params(("days" = Option<i64>, Query, description = "How many days back to read, today included")),
64 responses(
65 (status = 200, description = "One entry per day that has a snapshot", body = CreditRegistrationHistory)
66 )
67)]
68pub async fn get_credit_registration_pipeline_history(
69 user: AuthUser,
70 pool: web::Data<PgPool>,
71 query: web::Query<HistoryQuery>,
72) -> ControllerResult<web::Json<CreditRegistrationHistory>> {
73 let mut conn = pool.acquire().await?;
74 let token = authorize_credit_registration_admin(&mut conn, user.id).await?;
75
76 let days = query.days.unwrap_or(DEFAULT_DAYS).clamp(1, MAX_DAYS);
77 let to = Utc::now().date_naive();
78 let from = to - Duration::days(days - 1);
79
80 let mut history: Vec<CreditRegistrationHistoryDay> = Vec::new();
81 for row in credit_registration_daily_snapshots::get_between(&mut conn, from, to).await? {
82 if history
84 .last()
85 .is_none_or(|day| day.snapshot_date != row.snapshot_date)
86 {
87 history.push(CreditRegistrationHistoryDay {
88 snapshot_date: row.snapshot_date,
89 states: Vec::new(),
90 });
91 }
92 if let Some(day) = history.last_mut() {
93 day.states.push(CreditRegistrationHistoryPoint {
94 state: row.state,
95 count: row.count,
96 entered_count: row.entered_count,
97 left_count: row.left_count,
98 });
99 }
100 }
101
102 token.authorized_ok(web::Json(CreditRegistrationHistory {
103 days: history,
104 from,
105 to,
106 }))
107}
108
109pub fn _add_routes(cfg: &mut ServiceConfig) {
110 cfg.route(
111 "/pipeline-history",
112 web::get().to(get_credit_registration_pipeline_history),
113 );
114}