headless_lms_server/controllers/main_frontend/credit_registration_admin/
history.rs1use chrono::{Duration, NaiveDate};
9use headless_lms_models::credit_registration_daily_snapshots;
10use headless_lms_models::credit_registrations::CreditRegistrationState;
11use headless_lms_models::library::credit_registration::timeline::{
12 Engagement, TimelinePhase, TimelineStep,
13};
14use utoipa::ToSchema;
15
16use crate::prelude::*;
17
18use super::authorize_credit_registration_admin;
19
20const DEFAULT_DAYS: i64 = 30;
21const MAX_DAYS: i64 = 365;
22
23#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
25pub struct CreditRegistrationHistoryPoint {
26 pub state: CreditRegistrationState,
27 pub count: i32,
29 pub entered_count: i32,
31 pub left_count: i32,
32}
33
34#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
36pub struct CreditRegistrationHistoryStepPoint {
37 pub phase: TimelinePhase,
38 pub step: TimelineStep,
39 pub engagement: Option<Engagement>,
41 pub count: i32,
42}
43
44#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
45pub struct CreditRegistrationHistoryDay {
46 pub snapshot_date: NaiveDate,
48 pub states: Vec<CreditRegistrationHistoryPoint>,
51 pub steps: Vec<CreditRegistrationHistoryStepPoint>,
54 pub needs_attention_count: Option<i32>,
56}
57
58#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
59pub struct CreditRegistrationHistory {
60 pub days: Vec<CreditRegistrationHistoryDay>,
63 pub from: NaiveDate,
64 pub to: NaiveDate,
65}
66
67#[derive(Debug, Deserialize)]
68pub struct HistoryQuery {
69 days: Option<i64>,
70}
71
72#[instrument(skip(pool))]
78#[utoipa::path(
79 get,
80 path = "/pipeline-history",
81 operation_id = "getCreditRegistrationPipelineHistory",
82 tag = "credit-registration-admin",
83 params(("days" = Option<i64>, Query, description = "How many days back to read, today included")),
84 responses(
85 (status = 200, description = "One entry per day that has a snapshot", body = CreditRegistrationHistory)
86 )
87)]
88pub async fn get_credit_registration_pipeline_history(
89 user: AuthUser,
90 pool: web::Data<PgPool>,
91 query: web::Query<HistoryQuery>,
92) -> ControllerResult<web::Json<CreditRegistrationHistory>> {
93 let mut conn = pool.acquire().await?;
94 let token = authorize_credit_registration_admin(&mut conn, user.id).await?;
95
96 let days = query.days.unwrap_or(DEFAULT_DAYS).clamp(1, MAX_DAYS);
97 let to = Utc::now().date_naive();
98 let from = to - Duration::days(days - 1);
99
100 let mut history: Vec<CreditRegistrationHistoryDay> = Vec::new();
101 for row in credit_registration_daily_snapshots::get_between(&mut conn, from, to).await? {
102 if history
104 .last()
105 .is_none_or(|day| day.snapshot_date != row.snapshot_date)
106 {
107 history.push(CreditRegistrationHistoryDay {
108 snapshot_date: row.snapshot_date,
109 states: Vec::new(),
110 steps: Vec::new(),
111 needs_attention_count: None,
112 });
113 }
114 if let Some(day) = history.last_mut() {
115 day.states.push(CreditRegistrationHistoryPoint {
116 state: row.state,
117 count: row.count,
118 entered_count: row.entered_count,
119 left_count: row.left_count,
120 });
121 }
122 }
123
124 for row in
125 credit_registration_daily_snapshots::get_step_counts_between(&mut conn, from, to).await?
126 {
127 if let Some(day) = history
128 .iter_mut()
129 .find(|day| day.snapshot_date == row.snapshot_date)
130 {
131 day.steps.push(CreditRegistrationHistoryStepPoint {
132 phase: row.step.phase(),
133 step: row.step,
134 engagement: row.engagement,
135 count: row.count,
136 });
137 }
138 }
139 for (snapshot_date, count) in
140 credit_registration_daily_snapshots::get_attention_counts_between(&mut conn, from, to)
141 .await?
142 {
143 if let Some(day) = history
144 .iter_mut()
145 .find(|day| day.snapshot_date == snapshot_date)
146 {
147 day.needs_attention_count = Some(count);
148 }
149 }
150
151 token.authorized_ok(web::Json(CreditRegistrationHistory {
152 days: history,
153 from,
154 to,
155 }))
156}
157
158pub fn _add_routes(cfg: &mut ServiceConfig) {
159 cfg.route(
160 "/pipeline-history",
161 web::get().to(get_credit_registration_pipeline_history),
162 );
163}