add upstream calendars page

This commit is contained in:
_ 2025-09-11 01:49:26 +00:00
parent 830f45423f
commit ef0d32f0b7
4 changed files with 103 additions and 33 deletions

View file

@ -51,6 +51,26 @@ impl Config {
) )
.chain(self.icals.iter().map(|ical| ical.dl.clone())) .chain(self.icals.iter().map(|ical| ical.dl.clone()))
} }
fn upstreams(&self) -> Vec<CalendarUi> {
let Self {
campfires,
common_ninjas,
icals,
output: _,
} = self;
let mut upstreams: Vec<_> = campfires
.iter()
.map(|cfg| &cfg.ui)
.cloned()
.chain(common_ninjas.iter().map(|cfg| &cfg.ui).cloned())
.chain(icals.iter().map(|cfg| &cfg.ui).cloned())
.collect();
upstreams.sort_by_key(|ui| ui.short_name.clone());
upstreams
}
} }
#[derive(clap::Parser)] #[derive(clap::Parser)]
@ -238,7 +258,7 @@ async fn do_everything(cli: &CliAuto) -> Result<()> {
let data = read_data_from_disk(&config)?; let data = read_data_from_disk(&config)?;
let instances = process_data(&data, &config.output, now)?; let instances = process_data(&data, &config.output, now)?;
output::write_html(&config.output, &instances, now)?; output::write_html(&config.output, &config.upstreams(), &instances, now)?;
Ok(()) Ok(())
} }
@ -276,7 +296,8 @@ fn main_debug_output(cli: CliDebugOutput) -> Result<()> {
let tz = &config.output.timezone; let tz = &config.output.timezone;
let now = Utc::now().with_timezone(tz); let now = Utc::now().with_timezone(tz);
let instances = process_data(&data, &config.output, now).context("Failed to process data")?; let instances = process_data(&data, &config.output, now).context("Failed to process data")?;
output::write_html(&config.output, &instances, now).context("Failed to output HTML")?; output::write_html(&config.output, &config.upstreams(), &instances, now)
.context("Failed to output HTML")?;
Ok(()) Ok(())
} }

View file

@ -5,6 +5,24 @@ use std::{collections::BTreeSet, io::Write as _};
use crate::{EventInstance, prelude::*}; use crate::{EventInstance, prelude::*};
const CSS: &str = r#"
<style>
body {
font-family: sans-serif;
font-size: 14pt;
line-height: 1.6;
max-width: 700px;
}
img {
max-width: 100%;
height: auto;
}
.past {
color: #888;
}
</style>
"#;
#[derive(Deserialize)] #[derive(Deserialize)]
pub(crate) struct Config { pub(crate) struct Config {
/// Used as the OpenGraph description in meta tags /// Used as the OpenGraph description in meta tags
@ -40,8 +58,17 @@ impl Config {
} }
} }
fn calendar_link(calendar_ui: &crate::CalendarUi) -> maud::PreEscaped<String> {
if let Some(html_url) = &calendar_ui.html_url {
maud::html! { a href=(html_url) { (calendar_ui.short_name) } }
} else {
maud::html! { (calendar_ui.short_name)}
}
}
pub(crate) fn write_html( pub(crate) fn write_html(
config: &Config, config: &Config,
upstreams: &[crate::CalendarUi],
instances: &[EventInstance], instances: &[EventInstance],
now: DateTime<chrono_tz::Tz>, now: DateTime<chrono_tz::Tz>,
) -> Result<()> { ) -> Result<()> {
@ -118,12 +145,6 @@ pub(crate) fn write_html(
li class="past" { (time) " - " (summary) } li class="past" { (time) " - " (summary) }
}); });
} else { } else {
let calendar_link = if let Some(html_url) = &ei.calendar_ui.html_url {
maud::html! { a href=(html_url) { (ei.calendar_ui.short_name) } }
} else {
maud::html! { (ei.calendar_ui.short_name)}
};
// This is where the main stuff happens // This is where the main stuff happens
tracing::debug!(uid = ei.uid, summary = ei.summary); tracing::debug!(uid = ei.uid, summary = ei.summary);
@ -131,7 +152,7 @@ pub(crate) fn write_html(
li { details { li { details {
summary { (time) " - " (summary) } summary { (time) " - " (summary) }
ul { ul {
li { (calendar_link) " calendar" } li { (calendar_link(&ei.calendar_ui)) " calendar" }
@if let Some(location) = &ei.location { @if let Some(location) = &ei.location {
li { "Location: " (location) } li { "Location: " (location) }
} }
@ -155,7 +176,49 @@ pub(crate) fn write_html(
let final_path = "output/calendars.html"; let final_path = "output/calendars.html";
let mut f = std::fs::File::create(temp_path)?; let mut f = std::fs::File::create(temp_path)?;
f.write_all("".as_bytes())?; let description = "A list of upstream calendars used by this Wide-Angle Calendar instance";
let title = "Upstream calendars";
let s = maud::html! {
(maud::PreEscaped("<!DOCTYPE html>"))
html lang="en" {
head {
meta http-equiv="Content-Type" content="text/html; charset=utf-8" {}
meta name="viewport" content="width=device-width, initial-scale=1" {}
(maud::PreEscaped(CSS))
meta property="og:locale" content="en" {}
meta property="og:type" content="website" {}
meta name="description" content=(description) {}
meta property="description" content=(description) {}
meta property="og:description" content=(description) {}
title { (title) }
met property="og:title" content=(title) {}
}
body {
h1 { (title) }
p {
a href="index.html" { "Wide-Angle Calendar" }
" / "
a href="calendars.html" { (title) }
}
p { "Written at: " (now.format("%F %T")) }
p { "These are the calendars that Wide-Angle Calendar pulls from." }
ol {
@for upstream in upstreams {
li { (calendar_link(upstream)) }
}
}
}
}
}
.into_string();
f.write_all(s.as_bytes())?;
std::fs::rename(temp_path, final_path)?; std::fs::rename(temp_path, final_path)?;
} }
@ -163,23 +226,6 @@ pub(crate) fn write_html(
let temp_path = "output/index.html.tmp"; let temp_path = "output/index.html.tmp";
let final_path = "output/index.html"; let final_path = "output/index.html";
let mut f = std::fs::File::create(temp_path)?; let mut f = std::fs::File::create(temp_path)?;
let css = r#"
<style>
body {
font-family: sans-serif;
font-size: 14pt;
line-height: 1.6;
max-width: 700px;
}
img {
max-width: 100%;
height: auto;
}
.past {
color: #888;
}
</style>
"#;
let description = &config.description; let description = &config.description;
let title = &config.title; let title = &config.title;
@ -190,7 +236,7 @@ pub(crate) fn write_html(
head { head {
meta http-equiv="Content-Type" content="text/html; charset=utf-8" {} meta http-equiv="Content-Type" content="text/html; charset=utf-8" {}
meta name="viewport" content="width=device-width, initial-scale=1" {} meta name="viewport" content="width=device-width, initial-scale=1" {}
(maud::PreEscaped(css)) (maud::PreEscaped(CSS))
meta property="og:locale" content="en" {} meta property="og:locale" content="en" {}
meta property="og:type" content="website" {} meta property="og:type" content="website" {}
@ -206,6 +252,7 @@ pub(crate) fn write_html(
h1 { (title) } h1 { (title) }
img src="hero.webp" width="700" height="233" {} img src="hero.webp" width="700" height="233" {}
p { "Written at: " (now.format("%F %T")) } p { "Written at: " (now.format("%F %T")) }
p { a href = "calendars.html" { "Upstream calendars" } }
@for entry in html_list { @for entry in html_list {
(entry) (entry)
} }

View file

@ -19,11 +19,12 @@ pub(crate) struct Config {
#[derive(Deserialize)] #[derive(Deserialize)]
struct Event { struct Event {
description: String, #[serde(alias = "description")]
_description: String,
#[serde(alias = "endDate")] #[serde(alias = "endDate")]
end_date: Option<String>, _end_date: Option<String>,
#[serde(alias = "endTime")] #[serde(alias = "endTime")]
end_time: Option<String>, _end_time: Option<String>,
#[serde(alias = "eventName")] #[serde(alias = "eventName")]
event_name: String, event_name: String,
location: String, location: String,

View file

@ -36,10 +36,11 @@ impl Config {
#[derive(Deserialize)] #[derive(Deserialize)]
struct Event { struct Event {
#[serde(alias = "durationInMinutes")] #[serde(alias = "durationInMinutes")]
duration_in_minutes: u32, _duration_in_minutes: u32,
timestamp: i64, timestamp: i64,
title: String, title: String,
description: String, #[serde(alias = "description")]
_description: String,
} }
#[derive(Deserialize)] #[derive(Deserialize)]