From 7251320e9928f898d478aa4e5f0551450264cb6a Mon Sep 17 00:00:00 2001 From: cos Date: Thu, 30 May 2024 12:18:38 +0200 Subject: Initial commit, used for meetup#46 --- .gitignore | 1 + Cargo.toml | 11 +++++++ src/main.rs | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..898f90c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "cphrs-attendance" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.82" +html-escape = "0.2.13" +svg = "0.15.0" diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..b92a0eb --- /dev/null +++ b/src/main.rs @@ -0,0 +1,103 @@ +use { + anyhow::{ + anyhow, + Result, + }, + std::{ + env::args, + fs::File, + io::prelude::*, + }, + svg::{ + Document, + node::{ + Text as TextNode, + element::{ + Rectangle, + Text, + } + } + } +}; + +const COL_WIDTH: f64 = 350.0; +const ROWS: usize = 35; + +fn main() -> Result<()> { + let first_arg = args().nth(1).ok_or_else(|| anyhow!("Missing meetup csv file argument."))?; + let fullpath = first_arg.clone(); + let filename = fullpath.split('/').last().unwrap_or(&fullpath); + let (basename, _) = filename.split_once('.') + .ok_or_else(|| anyhow!("Unexpected filename. Was it downloaded from meetup.com?"))?; + let (event_name, _) = filename.split_once("_sponsored") + .ok_or_else(|| anyhow!("Unexpected filename. Was it downloaded from meetup.com?"))?; + + let mut csv_file = File::open(first_arg)?; + let mut contents = String::new(); + csv_file.read_to_string(&mut contents)?; + + let mut attendees: Vec = vec![]; + let mut waitlisted: Vec = vec![]; + + for (n, row) in contents.split('\n').enumerate() { + let fields: Vec<&str> = row.split('\t').collect(); + match (fields.get(1), fields.get(4)) { + (Some(name), Some(rsvp)) => { }, + _ => eprintln!("Unparsable row {n}: {}", row), + // fields.get(5) "Waiting List" + } + } + + let page_height = "297mm"; + let page_width = "210mm"; + let x_crop = 60.047f64; + let y_crop = 110.717f64; + // let fontfamily = "Liberation Serif"; + let fontfamily = "Iosevka"; + let fontsize = 22.2; + + let title_text = Text::new() + .set("x", 1.2 * x_crop) + .set("y", 1.2 * y_crop / 2.0) + .add(TextNode::new(event_name.chars().map(|c| if c == '_' { ' '} else { c }).collect::())) + .set("style", format!("font-size:{fontsize}px;line-height:1.25;font-family:'{fontfamily}'")); + + let mut document = Document::new() + .set("viewBox", (0, 0, page_width, page_height)) + .set("width", page_width) + .set("height", page_height) + .set("xmlns:inkscape", "http://www.inkscape.org/namespaces/inkscape") + .add(title_text); + + for (n, attendee) in contents.split('\n').enumerate() { + if let Some((name, _)) = attendee.split_once('\t') { + let row = n % ROWS; + let col = n / ROWS; + + let attendee_box = Rectangle::new() + .set("x", 1.2 * x_crop + col as f64 * COL_WIDTH) + .set("y", 1.2 * y_crop + row as f64 * fontsize * 1.2) + .set("style", "fill:#ffffff;stroke:#000000;fill-opacity=0.4;opacity=0.4") + .set("height", fontsize * 1.2) + .set("width", COL_WIDTH); + + let attendee_text = Text::new() + .set("x", 1.2 * x_crop + col as f64 * COL_WIDTH + 40.0) + .set("y", 1.2 * y_crop + fontsize + row as f64 * fontsize * 1.2) + .add(TextNode::new(name)) + .set("style", format!("font-size:{fontsize}px;line-height:1.25;font-family:'{fontfamily}'")); + + document = document + .add(attendee_box) + .add(attendee_text); + } else { + if attendee.is_empty() { + continue; + } + return Err(anyhow!("Could not parse: '{attendee}'")); + } + } + + svg::save(format!("{basename}.svg"), &document).unwrap(); + Ok(()) +} -- cgit v1.2.3