Average of tickets submitted per weekday
Avvailable in the diagnostics
This commit is contained in:
@@ -1,15 +1,85 @@
|
||||
use chrono::{DateTime, Datelike, Utc};
|
||||
use gloo_net::http::Request;
|
||||
use serde::Deserialize;
|
||||
use wasm_bindgen_futures::spawn_local;
|
||||
use yew::prelude::*;
|
||||
use yew_router::prelude::*;
|
||||
|
||||
use crate::pages::ticket::{ActiveUser, Ticket};
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
struct Date {
|
||||
date: DateTime<Utc>, // matches JSON "date": "2026-05-01T14:23:00Z"
|
||||
}
|
||||
|
||||
fn weekday_index(dt: &DateTime<Utc>) -> usize {
|
||||
// chrono::Weekday: Mon = 1 ... Sun = 7
|
||||
match dt.weekday() {
|
||||
chrono::Weekday::Mon => 0,
|
||||
chrono::Weekday::Tue => 1,
|
||||
chrono::Weekday::Wed => 2,
|
||||
chrono::Weekday::Thu => 3,
|
||||
chrono::Weekday::Fri => 4,
|
||||
chrono::Weekday::Sat => 5,
|
||||
chrono::Weekday::Sun => 6,
|
||||
}
|
||||
}
|
||||
|
||||
fn count_by_weekday(tickets: &[Date]) -> [usize; 7] {
|
||||
let mut counts = [0usize; 7];
|
||||
for t in tickets {
|
||||
counts[weekday_index(&t.date)] += 1;
|
||||
}
|
||||
counts
|
||||
}
|
||||
|
||||
fn day_counts(dates: &[Date]) -> [usize; 7] {
|
||||
if dates.is_empty() {
|
||||
return [0usize; 7];
|
||||
}
|
||||
|
||||
let mut min = dates[0].date.date_naive();
|
||||
let mut max = min;
|
||||
for d in dates.iter().skip(1) {
|
||||
let dt = d.date.date_naive();
|
||||
if dt < min {
|
||||
min = dt
|
||||
}
|
||||
if dt > max {
|
||||
max = dt
|
||||
}
|
||||
}
|
||||
|
||||
let total_days = (max - min).num_days() + 1;
|
||||
if total_days <= 0 {
|
||||
return [0usize; 7];
|
||||
}
|
||||
|
||||
let mut occ = [0usize; 7];
|
||||
let mut current = min;
|
||||
for _ in 0..total_days {
|
||||
let wd = current.weekday();
|
||||
let idx = match wd {
|
||||
chrono::Weekday::Mon => 0,
|
||||
chrono::Weekday::Tue => 1,
|
||||
chrono::Weekday::Wed => 2,
|
||||
chrono::Weekday::Thu => 3,
|
||||
chrono::Weekday::Fri => 4,
|
||||
chrono::Weekday::Sat => 5,
|
||||
chrono::Weekday::Sun => 6,
|
||||
};
|
||||
occ[idx] += 1;
|
||||
current = current.succ();
|
||||
}
|
||||
|
||||
occ
|
||||
}
|
||||
|
||||
#[component(Diagnostics)]
|
||||
pub fn diagnostics_component() -> Html {
|
||||
html! {
|
||||
<div>
|
||||
<TicketCount/>
|
||||
<SubmitStats/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@@ -108,5 +178,83 @@ pub fn ticket_count_component() -> Html {
|
||||
}
|
||||
}
|
||||
|
||||
// #[component(SubmitStats)]
|
||||
// pub fn submit_stats_component() -> Html {}
|
||||
#[component(SubmitStats)]
|
||||
pub fn submit_stats_component() -> Html {
|
||||
let tickets = use_state(|| Vec::<Date>::new());
|
||||
let error = use_state(|| None::<String>);
|
||||
let loading = use_state(|| false);
|
||||
|
||||
{
|
||||
let tickets = tickets.clone();
|
||||
let error = error.clone();
|
||||
let loading = loading.clone();
|
||||
|
||||
use_effect_with((), move |_| {
|
||||
loading.set(true);
|
||||
spawn_local(async move {
|
||||
let url = "/api/tickets".to_string();
|
||||
match Request::get(&url).send().await {
|
||||
Ok(response) if response.status() == 200 => {
|
||||
match response.json::<Vec<Date>>().await {
|
||||
Ok(t) => tickets.set(t),
|
||||
Err(e) => error.set(Some(format!("parse error: {}", e))),
|
||||
}
|
||||
}
|
||||
Ok(response) => {
|
||||
if let Ok(text) = response.text().await {
|
||||
error.set(Some(text));
|
||||
} else {
|
||||
error.set(Some(format!("status {}", response.status())));
|
||||
}
|
||||
}
|
||||
Err(err) => error.set(Some(format!("Network error: {}", err))),
|
||||
}
|
||||
loading.set(false);
|
||||
});
|
||||
|| ()
|
||||
});
|
||||
}
|
||||
|
||||
let counts = count_by_weekday(&tickets);
|
||||
let occ = day_counts(&tickets);
|
||||
|
||||
let mut avg = [0.0f64; 7];
|
||||
for i in 0..7 {
|
||||
if occ[i] > 0 {
|
||||
avg[i] = counts[i] as f64 / occ[i] as f64;
|
||||
} else {
|
||||
avg[i] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
let weekdays = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
||||
let (max_idx, _max_val) = counts
|
||||
.iter()
|
||||
.enumerate()
|
||||
.max_by(|a, b| a.1.partial_cmp(b.1).unwrap())
|
||||
.map(|(i, _)| (i, ()))
|
||||
.unwrap_or((0, ()));
|
||||
|
||||
html! {
|
||||
<div>
|
||||
if *loading {
|
||||
<p>{ "Loading..." }</p>
|
||||
}
|
||||
if let Some(e) = &*error {
|
||||
<p style="color: red;">{ e.clone() }</p>
|
||||
}
|
||||
<h3>{ "Tickets per weekday" }</h3>
|
||||
<ul>
|
||||
{ for (0..7).map(|i| {
|
||||
let is_max = i == max_idx;
|
||||
html! {
|
||||
<li style={ if is_max { "font-weight: bold; color: green;" } else { "" } }>
|
||||
{ format!("{}: {:3}", weekdays[i], avg[i]) }
|
||||
{ if is_max { html!{ <span>{ " ← most" }</span> } } else { html!{} } }
|
||||
</li>
|
||||
}
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user