From f12b6ffa59f62b72d551c7587cc0b4401e074f7b Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sat, 2 Sep 2023 18:36:27 +0200 Subject: [PATCH] Add timestamps to bar --- src/web.rs | 59 +++++++++++++++++++++++++++++++++----------- templates/index.html | 39 ++++++++++++++++++++++++----- 2 files changed, 77 insertions(+), 21 deletions(-) diff --git a/src/web.rs b/src/web.rs index fd0039b..0ccf15e 100644 --- a/src/web.rs +++ b/src/web.rs @@ -14,6 +14,16 @@ use sqlx::{Pool, Sqlite}; use crate::{client::CheckState, db::Check}; +trait RenderDate { + fn render_nicely(&self) -> String; +} + +impl RenderDate for chrono::DateTime { + fn render_nicely(&self) -> String { + self.to_rfc3339_opts(chrono::SecondsFormat::Millis, /*use_z*/ true) + } +} + pub async fn axum_server(db: Arc>) -> Result<()> { let app = Router::new().route("/", get(root)).with_state(db); @@ -66,7 +76,7 @@ fn compute_status(checks: Vec) -> Vec { let mut count_ok = 0; const BAR_ELEMS: usize = 100; - let bar_classes = checks_to_classes(&checks, BAR_ELEMS); + let bar_info = checks_to_classes(&checks, BAR_ELEMS); let len = checks.len(); checks.into_iter().for_each(|(time, result)| { @@ -79,20 +89,20 @@ fn compute_status(checks: Vec) -> Vec { let ok_ratio = (count_ok as f32) / (len as f32); let ok_ratio = format!("{:.2}%", ok_ratio * 100.0); - let last_ok = last_ok - .map(|utc| utc.to_rfc3339_opts(chrono::SecondsFormat::Millis, /*use_z*/ true)); + let last_ok = last_ok.map(|utc| utc.render_nicely()); WebsiteStatus { website, last_ok, ok_ratio, count_ok, total_requests: len, - bar_classes, + bar_info, } }) .collect() } +#[derive(Debug)] enum BarClass { Green, Orange, @@ -111,28 +121,39 @@ impl BarClass { } } +#[derive(Debug)] +struct BarInfo { + elems: Vec, + first_time: Option>, + last_time: Option>, +} + /// Converts a list of (sorted by time) checks at arbitrary dates into a list of boxes for the /// frontend, in a fixed sensical timeline. /// We slice the time from the first check to the last check (maybe something like last check-30d /// in the future) into slices and aggregate all checks from these times into these slices. -fn checks_to_classes(checks: &[(DateTime, CheckState)], classes: usize) -> Vec { +fn checks_to_classes(checks: &[(DateTime, CheckState)], classes: usize) -> BarInfo { assert_ne!(classes, 0); let Some(first) = checks.first() else { - return vec![]; + return BarInfo { + elems: Vec::new(), + first_time: None, + last_time: None, + }; }; let last = checks.last().unwrap(); let mut bins = vec![vec![]; classes]; - let first = first.0.timestamp_millis(); - let last = last.0.timestamp_millis(); + let first_m = first.0.timestamp_millis(); + let last_m = last.0.timestamp_millis(); - let last_rel = last - first; - assert!(last.is_positive(), "checks not ordered correctly"); + let last_rel = last_m - first_m; + assert!(last_m.is_positive(), "checks not ordered correctly"); for check in checks { - let time_rel = check.0.timestamp_millis() - first; - assert!(first.is_positive(), "checks not ordered correctly"); + let time_rel = check.0.timestamp_millis() - first_m; + assert!(first_m.is_positive(), "checks not ordered correctly"); /* 5 bins: @@ -147,7 +168,8 @@ fn checks_to_classes(checks: &[(DateTime, CheckState)], classes: usize) -> bins[bin].push(check); } - bins.iter() + let elems = bins + .iter() .map(|checks| { let ok = checks .iter() @@ -167,16 +189,23 @@ fn checks_to_classes(checks: &[(DateTime, CheckState)], classes: usize) -> unreachable!("i dont think logic works like this") } }) - .collect() + .collect(); + + BarInfo { + elems, + first_time: Some(first.0), + last_time: Some(last.0), + } } +#[derive(Debug)] struct WebsiteStatus { website: String, last_ok: Option, ok_ratio: String, total_requests: usize, count_ok: usize, - bar_classes: Vec, + bar_info: BarInfo, } #[derive(Template)] diff --git a/templates/index.html b/templates/index.html index dc1944a..b6c0d00 100644 --- a/templates/index.html +++ b/templates/index.html @@ -10,13 +10,32 @@ font-family: Arial, Helvetica, sans-serif; } + .check-result-bar-container { + display: flex; + flex-direction: column; + width: 800px; + } + + /* This is a bit hacky and ensures that the bar doesn't go through the right. */ + @media only screen and (max-width: 810px) { + .check-result-bar-container { + width: 100%; + } + } + .check-result-bar { display: flex; } + .check-result-bar-labels { + margin-top: 5px; + display: flex; + justify-content: space-between; + } + .check-result { height: 10px; - width: 5px; + width: 100vw; /* It will be squashed. */ } .check-result-red { @@ -46,13 +65,21 @@

Last OK: {{ check.last_ok.as_deref().unwrap() }}

{% endif %} -
- {% for result in check.bar_classes %} -
- {% endfor %} +
+
+ {% for result in check.bar_info.elems %} +
+ {% endfor %} +
+ {% if check.bar_info.first_time.is_some() && check.bar_info.last_time.is_some() %} +
+ {{ check.bar_info.first_time.unwrap().render_nicely() }} + {{ check.bar_info.last_time.unwrap().render_nicely() }} +
+ {% endif %}
- {% endfor %} + {% endfor %}