113 lines
3.3 KiB
Rust
113 lines
3.3 KiB
Rust
use crate::auth::Authorized;
|
|
use crate::status_json::StatusJson;
|
|
use chrono::{Duration, Local, NaiveDate};
|
|
use itertools::Itertools;
|
|
use rocket::{get, response::content::Html, State};
|
|
use rocket_dyn_templates::Template;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::borrow::Cow;
|
|
use std::iter::{once, repeat};
|
|
|
|
pub struct BirthDate(pub NaiveDate);
|
|
|
|
#[get("/weeks")]
|
|
pub fn weeks(
|
|
_auth: Authorized,
|
|
birth_date: &State<BirthDate>,
|
|
) -> Result<Html<Template>, StatusJson> {
|
|
type Color<'a> = Cow<'a, str>;
|
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
struct Style<'a> {
|
|
border: Color<'a>,
|
|
fill: Option<Color<'a>>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
struct PeriodCtx<'a> {
|
|
style: Style<'a>,
|
|
weeks: Vec<()>,
|
|
}
|
|
|
|
impl<'a> Style<'a> {
|
|
fn border<C>(border: C) -> Self
|
|
where
|
|
C: Into<Cow<'a, str>>,
|
|
{
|
|
Style {
|
|
border: border.into(),
|
|
fill: None,
|
|
}
|
|
}
|
|
|
|
fn fill<C>(border: C, fill: C) -> Self
|
|
where
|
|
C: Into<Cow<'a, str>>,
|
|
{
|
|
Style {
|
|
border: border.into(),
|
|
fill: Some(fill.into()),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
struct Ctx<'a> {
|
|
weeks_left: i64,
|
|
periods: Vec<PeriodCtx<'a>>,
|
|
}
|
|
|
|
let now = Local::now().date();
|
|
let birth_date = birth_date.0;
|
|
|
|
let lived: Duration = now.naive_local() - birth_date;
|
|
let one_year = Duration::days(365);
|
|
|
|
let life_expectancy = (one_year * 81).num_weeks();
|
|
let uncertainty = (one_year * 20).num_weeks();
|
|
|
|
let childhood_end = (one_year * 12).num_weeks();
|
|
let teenage_end = (one_year * 20).num_weeks();
|
|
|
|
let color_birth = "green";
|
|
let color_child = "#ff0";
|
|
let color_teen = "#f33";
|
|
let color_adult = "#f0f";
|
|
let color_today = "wheat";
|
|
let color_future = "#eee";
|
|
|
|
let context = Ctx {
|
|
weeks_left: life_expectancy - lived.num_weeks(),
|
|
periods: once(Style::fill(color_birth, color_birth))
|
|
// childhood
|
|
.chain((1..childhood_end).map(|_| Style::border(color_child)))
|
|
// teens
|
|
.chain((childhood_end..teenage_end).map(|_| Style::border(color_teen)))
|
|
// adulthood
|
|
.chain((teenage_end..).map(|_| Style::border(color_adult)))
|
|
// take from above for lived number of weeks
|
|
.take(lived.num_weeks() as usize - 1)
|
|
// add a week for this week
|
|
.chain(once(Style::fill(color_today, color_today)))
|
|
// fill remaining weeks until death
|
|
.chain(repeat(Style::border(color_future)))
|
|
.take((life_expectancy - uncertainty * 2) as usize)
|
|
// add some fading around expected life span
|
|
.chain((0..uncertainty).rev().map(|i| {
|
|
let m = u8::MAX as i64;
|
|
let alpha = ((m * i) / (uncertainty)) as u8;
|
|
Style::border(format!("#eeeeee{:02x}", alpha))
|
|
}))
|
|
// group weeks by color to save space
|
|
.group_by(|style| style.clone())
|
|
.into_iter()
|
|
.map(|(style, weeks)| PeriodCtx {
|
|
style,
|
|
weeks: vec![(); weeks.count()],
|
|
})
|
|
.collect(),
|
|
};
|
|
|
|
Ok(Html(Template::render("weeks", &context)))
|
|
}
|