use std::time::Instant; use assetserver::Asset; use chrono::TimeDelta; use maud::{Markup, PreEscaped, html}; use serde::Deserialize; use tracing::{debug, warn}; use crate::{ components::{ md::Markdown, misc::{Backlinks, FarLink}, }, page::{Page, PageMetadata}, routes::assets::Image_Icon, }; #[derive(Debug, Deserialize)] struct HandoutEntry { title: String, group: String, handout: String, solutions: Option, } async fn get_index() -> Result, reqwest::Error> { let start = Instant::now(); let res = reqwest::get( "https://git.betalupi.com/api/packages/Mark/generic/ormc-handouts/latest/index.json", ) .await; let res = match res { Ok(x) => x, Err(err) => { warn!("Error while getting index: {err:?}"); return Err(err); } }; let mut res: Vec = res.json().await?; res.sort_by_key(|x| x.title.clone()); debug!( message = "Fetched handout index", n_handouts = res.len(), time_ms = start.elapsed().as_millis() ); return Ok(res); } fn build_list_for_group(handouts: &[HandoutEntry], group: &str) -> Markup { html! { ul class="handout-ul" { @for h in handouts { @if h.group ==group { li { span class="handdout-li-title" { strong { (h.title) } } " " span class="handout-li-links" { "[ " @if let Some(solutions) = &h.solutions { a href=(h.handout) {"handout"} " | " a href=(solutions) {"solutions"} } @else { a href=(h.handout) {"handout"} } " ]" } } } } } } } // // MARK: page // pub fn handouts() -> Page { let md = Markdown::parse(include_str!("handouts.md")); #[expect(clippy::unwrap_used)] let mut meta = PageMetadata::from_markdown_frontmatter(&md) .unwrap() .unwrap(); if meta.image.is_none() { meta.image = Some(Image_Icon::URL.to_owned()); } let html = PreEscaped(md.render()); Page { meta, html_ttl: Some(TimeDelta::seconds(300)), generate_html: Box::new(move |page| { let html = html.clone(); // TODO: find a way to not clone here Box::pin(async move { let handouts = get_index().await; let warmups = match &handouts { Ok(handouts) => build_list_for_group(handouts, "Warm-Ups"), Err(error) => { warn!("Could not load handout index: {error:?}"); html! { span style="color:var(--yellow)" { "Could not load handouts, something broke." } " " ( FarLink( "https://git.betalupi.com/Mark/-/packages/generic/ormc-handouts/latest", "Try this direct link." ) ) } } }; let advanced = match &handouts { Ok(handouts) => build_list_for_group(handouts, "Advanced"), Err(_) => html! { span style="color:var(--yellow)" { "Could not load handouts, something broke." } " " ( FarLink( "https://git.betalupi.com/Mark/-/packages/generic/ormc-handouts/latest", "Try this direct link." ) ) }, }; html! { @if let Some(slug) = &page.meta.slug { (Backlinks(&[("/", "home")], slug)) } (html) (Markdown(concat!( "## Warm-Ups", "\n\n", "Students never show up on time. Some come early, some come late. Warm-ups ", "are my solution to this problem: we hand these out as students walk in, ", "giving them something to do until we can start the lesson.", ))) (warmups) br {} (Markdown(concat!( "## Advanced", "\n\n", "The highest level of the ORMC, and the group I spend most of my time with. ", "Students in ORMC Advanced are in high school, which means ", "they're ~14-18 years old.", ))) (advanced) br {} } }) }), } }