CSV generation in the webserver

This commit is contained in:
Niklas Kapelle 2025-04-16 23:02:28 +02:00
parent 880e8f24a7
commit 0f189b8e98
Signed by: niklas
GPG Key ID: 4EB651B36D841D16
3 changed files with 30 additions and 15 deletions

View File

@ -58,6 +58,7 @@ impl IDStore {
} }
pub fn export_csv(&self) -> Result<String, Box<dyn Error>> { pub fn export_csv(&self) -> Result<String, Box<dyn Error>> {
let mut csv = String::new();
let seperator = ";"; let seperator = ";";
let mut user_ids: HashSet<TallyID> = HashSet::new(); let mut user_ids: HashSet<TallyID> = HashSet::new();
@ -74,23 +75,22 @@ impl IDStore {
days.sort(); days.sort();
let header = days.join(seperator); let header = days.join(seperator);
println!("ID,{}", header); csv.push_str(&format!("ID{}{}\n", seperator, header));
for user_id in user_ids.iter() { for user_id in user_ids.iter() {
print!("{},", user_id.0); csv.push_str(&user_id.0.to_string());
for day in days.iter() { for day in days.iter() {
let was_there: bool = self.days.get(day).unwrap().ids.contains(user_id); let was_there: bool = self.days.get(day).ok_or("Failed to access day")?.ids.contains(user_id);
if was_there { if was_there {
print!("{}x", seperator); csv.push_str(&format!("{}x", seperator));
} else { } else {
print!("{}", seperator); csv.push_str(seperator);
} }
} }
println!(); csv.push('\n');
} }
Ok(csv)
Ok("".to_owned())
} }
} }

View File

@ -26,15 +26,15 @@ async fn main() {
}); });
let store:Arc<Mutex<IDStore>> = Arc::new(Mutex::new(id_store::IDStore::new())); let store:Arc<Mutex<IDStore>> = Arc::new(Mutex::new(id_store::IDStore::new()));
let channel_store = store.clone();
tokio::spawn(async move { tokio::spawn(async move {
while let Some(tally_id_string) = rx.recv().await { while let Some(tally_id_string) = rx.recv().await {
println!("Got from channel: {}", tally_id_string); channel_store.lock().await.add_id(id_store::TallyID(tally_id_string));
store.lock().await.add_id(id_store::TallyID(tally_id_string));
} }
}); });
match start_webserver().await { match start_webserver(store.clone()).await {
Ok(()) => {} Ok(()) => {}
Err(e) => { Err(e) => {
eprintln!("Failed to start webserver: {}", e); eprintln!("Failed to start webserver: {}", e);

View File

@ -1,15 +1,19 @@
use rocket::Config; use rocket::http::Status;
use rocket::{Config, State};
use rocket::{get, http::ContentType, response::content::RawHtml, routes}; use rocket::{get, http::ContentType, response::content::RawHtml, routes};
use rust_embed::Embed; use rust_embed::Embed;
use std::borrow::Cow; use std::borrow::Cow;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::sync::Arc;
use tokio::sync::Mutex;
use crate::id_store::IDStore;
#[derive(Embed)] #[derive(Embed)]
#[folder = "web/dist"] #[folder = "web/dist"]
struct Asset; struct Asset;
pub async fn start_webserver() -> Result<(), rocket::Error> { pub async fn start_webserver(store: Arc<Mutex<IDStore>>) -> Result<(), rocket::Error> {
let config = Config { let config = Config {
address: "0.0.0.0".parse().unwrap(), // Listen on all interfaces address: "0.0.0.0".parse().unwrap(), // Listen on all interfaces
port: 8000, port: 8000,
@ -17,13 +21,13 @@ pub async fn start_webserver() -> Result<(), rocket::Error> {
}; };
rocket::custom(config) rocket::custom(config)
.mount("/", routes![static_files,index]) .mount("/", routes![static_files, index, export_csv])
.manage(store)
.launch() .launch()
.await?; .await?;
Ok(()) Ok(())
} }
#[get("/")] #[get("/")]
fn index() -> Option<RawHtml<Cow<'static, [u8]>>> { fn index() -> Option<RawHtml<Cow<'static, [u8]>>> {
let asset = Asset::get("index.html")?; let asset = Asset::get("index.html")?;
@ -42,3 +46,14 @@ fn static_files(file: std::path::PathBuf) -> Option<(ContentType, Vec<u8>)> {
Some((content_type, asset.data.into_owned())) Some((content_type, asset.data.into_owned()))
} }
#[get("/api/csv")]
async fn export_csv(manager: &State<Arc<Mutex<IDStore>>>) -> Result<String, Status> {
match manager.lock().await.export_csv() {
Ok(csv) => Ok(csv),
Err(e) => {
eprintln!("Failed to generate csv: {}", e);
Err(Status::InternalServerError)
}
}
}