From 603b4f2a29454ee38077b670d4502a453ac799a9 Mon Sep 17 00:00:00 2001 From: Thomas Koch Date: Sun, 12 Jan 2025 20:12:27 +0200 Subject: [PATCH] get authors also from feed data + some css --- planet.css | 69 ++++++++++++++++++++++++++++++++---------- src/feed_store.rs | 27 ++++++++++------- src/main.rs | 2 ++ src/template_engine.rs | 61 +++++++++++++++++++++++++++++++++++-- templates/index.html | 20 ++++++------ 5 files changed, 140 insertions(+), 39 deletions(-) diff --git a/planet.css b/planet.css index aca682f..31c5ca5 100644 --- a/planet.css +++ b/planet.css @@ -1,5 +1,5 @@ p, h1, h2, h3, h4, h5, h6, small { - max-width: 48em; + max-width: 48em; } h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { @@ -9,34 +9,71 @@ h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { ul, ol { /* account for the 1em -webkit-margin-start for the list icon */ - max-width: 45em; + max-width: 45em; } ul,ol,dl, p { - line-height: 1.4; + margin-top: 0.3em; + margin-bottom: 0.3em; + line-height: 1.2; } -body { - margin-top: 1em; - margin-bottom: 1em; +ul, ol { + padding-inline-start: 1.5em; } +#bodydiv { + margin: auto; + max-width: 80em; +} + +#maincontainer aside img { + max-width: 10em; +} + +#maincontainer main blockquote { + margin-left: 0; + margin-right: 10px; + box-shadow: 10px 0px 0px 0px #C4C4C4; +} + +blockquote, pre code { + padding: 0.5ex 0; + display: block; + background-color: #EEE; +} + +#maincontainer main * { + max-width: 100%; +} + +#maincontainer main pre { + overflow-x: auto; +} + +.entry_meta { + margin-bottom: 1em; +} + +@media only screen and (min-width: 1024px) { #maincontainer { display: flex; - max-width: 80em; } #maincontainer main { max-width: 50em; -} - -#maincontainer main * { - max-width: 50em; + flex: 5; } #maincontainer aside { margin-left: 5em; - max-width: 25em; + max-width: 15em; + flex: 1; +} + +#maincontainer aside img { + margin: auto; + display: block; } article > h2.entry_header { @@ -44,7 +81,6 @@ article > h2.entry_header { } .entry_meta { - border: 1px thin; padding: 3px 0; background-color: LightBlue; } @@ -53,7 +89,8 @@ hr.entry_sep { border: none; } hr.entry_sep::before { - content: '* * *'; - display: block; - text-align: center; + content: '* * *'; + display: block; + text-align: center; +} } diff --git a/src/feed_store.rs b/src/feed_store.rs index 621b06d..ac05723 100644 --- a/src/feed_store.rs +++ b/src/feed_store.rs @@ -4,6 +4,7 @@ use feed_rs::model::Entry; use feed_rs::model::Feed; use ron::ser::{to_string_pretty, PrettyConfig}; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use std::convert::AsRef; use std::fs; use std::io::BufReader; @@ -13,9 +14,6 @@ use ureq::http::Response; use ureq::Body; use url::Url; -/// How many feed entries should be included in the planet -const ENTRIES_LEN: usize = 10; - #[derive(Deserialize, Serialize, Default)] pub struct FetchData { pub etag: String, @@ -135,8 +133,12 @@ impl FeedStore { Ok(Some(parser.parse(BufReader::new(file))?)) } - pub fn collect(&self, feed_configs: &Vec) -> (Vec, Vec) { - let mut feeds = Vec::new(); + pub fn collect( + &self, + feed_configs: &Vec, + max_entries: usize, + ) -> (HashMap, Vec) { + let mut feeds = HashMap::new(); let mut entries = Vec::new(); for feed_config in feed_configs { @@ -154,21 +156,24 @@ impl FeedStore { Ok(None) => continue, Ok(Some(f)) => f, }; + for entry in &mut feed.entries { + entry.source = Some(feed_config.url.clone()); + } entries.append(&mut std::mem::take(&mut feed.entries)); - feeds.push(feed); + feeds.insert(feed_config.url.clone(), feed); // optimization to reduce memory usage - if entries.len() > 4 * ENTRIES_LEN { - entries = trim_entries(entries); + if entries.len() > 4 * max_entries { + entries = trim_entries(entries, max_entries); } } - (feeds, trim_entries(entries)) + (feeds, trim_entries(entries, max_entries)) } } -fn trim_entries(mut entries: Vec) -> Vec { +fn trim_entries(mut entries: Vec, max_entries: usize) -> Vec { entries.sort_by_key(|e| std::cmp::Reverse(e.updated.or(e.published).unwrap_or_default())); - entries.truncate(ENTRIES_LEN); + entries.truncate(max_entries); entries } diff --git a/src/main.rs b/src/main.rs index ccbb064..42ce8c3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -66,6 +66,8 @@ struct Config { out_dir: String, /// templates folder templates_dir: String, + /// How many feed entries should be included in the planet + max_entries: usize, } pub fn to_checked_pathbuf(dir: &str) -> PathBuf { diff --git a/src/template_engine.rs b/src/template_engine.rs index 827ac23..aecead9 100644 --- a/src/template_engine.rs +++ b/src/template_engine.rs @@ -2,21 +2,25 @@ use crate::feed_store::FeedStore; use crate::to_checked_pathbuf; use crate::Config; use anyhow::Result; +use feed_rs::model::Feed; +use std::collections::HashMap; use std::fs::File; -use tera::Tera; +use tera::{from_value, Tera}; pub fn build(config: &Config, feed_store: &FeedStore) -> Result<()> { - let tera = create_tera(&config.templates_dir)?; + let mut tera = create_tera(&config.templates_dir)?; let out_dir = to_checked_pathbuf(&config.out_dir); let mut context = tera::Context::new(); - let (feeds, entries) = feed_store.collect(&config.feeds); + let (feeds, entries): (HashMap, _) = + feed_store.collect(&config.feeds, config.max_entries); context.insert("feeds", &feeds); context.insert("entries", &entries); context.insert("PKG_AUTHORS", env!("CARGO_PKG_AUTHORS")); context.insert("PKG_HOMEPAGE", env!("CARGO_PKG_HOMEPAGE")); context.insert("PKG_NAME", env!("CARGO_PKG_NAME")); context.insert("PKG_VERSION", env!("CARGO_PKG_VERSION")); + tera.register_function("get_author", GetAuthorFunction { feeds }); for name in tera.get_template_names() { debug!("Processing template {name}"); @@ -33,3 +37,54 @@ fn create_tera(templates_dir: &str) -> Result { tera.autoescape_on(vec![]); Ok(tera) } + +struct GetAuthorFunction { + feeds: HashMap, +} + +impl tera::Function for GetAuthorFunction { + fn call(&self, args: &HashMap) -> Result { + let entry_val: tera::Map<_, _> = match args.get("entry") { + None => { + return Err(tera::Error::msg( + "No argument of name 'entry' given to function.", + )) + } + Some(val) => from_value(val.clone())?, + }; + + let feed_url: String = from_value(entry_val.get("source").unwrap().clone())?; + let authors_val: Vec> = + from_value(entry_val.get("authors").unwrap().clone())?; + + let mut authors: Vec = Vec::new(); + for author_val in authors_val { + let name: String = from_value(author_val.get("name").unwrap().clone())?; + if is_valid_name(&name) { + authors.push(name.clone()); + } + } + + if authors.is_empty() { + authors.append(&mut self.find_authors_from_feed(&feed_url)); + } + Ok(tera::Value::String(authors.join(", "))) + } +} + +impl GetAuthorFunction { + fn find_authors_from_feed(&self, feed_url: &str) -> Vec { + let feed = self.feeds.get(feed_url).unwrap(); + + feed.authors + .clone() + .into_iter() + .map(|x| x.name) + .filter(is_valid_name) + .collect() + } +} + +fn is_valid_name(n: &String) -> bool { + !n.is_empty() && n != "unknown" && n != "author" +} diff --git a/templates/index.html b/templates/index.html index 767bfa3..d72f10f 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,5 +1,4 @@ {% set dateformat = "%d.%m.%Y %H:%M" -%} - Planet TVL @@ -10,6 +9,7 @@ +

Planet TVL

@@ -42,20 +42,20 @@ {{ entry.published | date(format=dateformat) }} {% endif -%} - {% if entry.authors -%} - — + {% set author = get_author(entry=entry) -%} + {% if author -%} + — {% endif -%}
- {% if entry.summary -%} -
- {{ entry.summary.content }} -
- {% endif -%} {% if entry.content -%}
{{ entry.content.body }}
+ {% elif entry.summary -%} +
+ {{ entry.summary.content }} +
{% endif -%} {% endfor -%} @@ -65,7 +65,7 @@

Last updated: {{now()|date(format="%Y-%m-%d %H:%M")}}

+