diff --git a/web/src/endpoints/export/get_events_data.rs b/web/src/endpoints/export/get_events_data.rs index 0a6a9272..12d9d978 100644 --- a/web/src/endpoints/export/get_events_data.rs +++ b/web/src/endpoints/export/get_events_data.rs @@ -1,9 +1,10 @@ +use crate::models::{ExportEventRow, Function}; use actix_http::header::CONTENT_DISPOSITION; use actix_web::{http::header::ContentDisposition, web, HttpResponse, Responder}; use chrono::{Datelike, NaiveDate}; -use rust_xlsxwriter::{workbook::Workbook, Format}; +use rust_xlsxwriter::workbook::Workbook; use serde::Deserialize; -use sqlx::{query, PgPool}; +use sqlx::PgPool; use crate::{ models::{Role, User}, @@ -30,64 +31,23 @@ struct EventExportEntry { assigned_function: Option, } -async fn read(pool: &PgPool) -> Result, ApplicationError> { - let results = query!( - "select - event.starttimestamp, - event.endtimestamp, - event.amountofposten, - event.voluntaryfuehrungsassistent, - event.voluntarywachhabender, - location.name as locationName, - event.name as eventName, - array ( - select - user_.name || ' --- ' || assignment.function - from - assignment - join availability on - assignment.availabilityid = availability.id - join user_ on - availability.userid = user_.id - where - assignment.eventId = event.id) as assignments, - array ( - select - vehicle.station || ' ' || vehicle.radiocallname - from - vehicleassignment - join vehicle on - vehicleassignment.vehicleId = vehicle.id - where - vehicleassignment.eventId = event.id) as vehicles -from - event -join location on - event.locationId = location.id - order by event.starttimestamp" - ) - .fetch_all(pool) - .await?; - +fn read(rows: Vec) -> Vec { let mut entries = Vec::new(); - for r in results { + for r in rows { let date = r - .starttimestamp - .date_naive() + .start_timestamp .format(DateTimeFormat::DayMonthYear.into()); - let weekday = r.starttimestamp.date_naive().weekday(); + let weekday = r.start_timestamp.weekday(); let start_time = r - .starttimestamp + .start_timestamp .time() .format(DateTimeFormat::HourMinute.into()); let end_time = r - .endtimestamp + .end_timestamp .time() .format(DateTimeFormat::HourMinute.into()); - let hours = (r.endtimestamp - r.starttimestamp).as_seconds_f32() / 3600.0 + 1.0; - let location = r.locationname; - let name = r.eventname; + let hours = (r.end_timestamp - r.start_timestamp).as_seconds_f32() / 3600.0 + 1.0; let mut event_new: Vec = Vec::new(); @@ -95,41 +55,34 @@ join location on let mut assigned_wachhabender = false; let mut assigned_fuehrungassistent = false; - if let Some(assignments) = r.assignments { - for a in assignments { - let (assigned_name, assigned_function) = a.split_once("---").unwrap(); - let assigned_function = match assigned_function.trim() { - "posten" => { - assigned_posten += 1; - "PO" - } - "wachhabender" => { - assigned_wachhabender = true; - "WH" - } - "fuehrungsassistent" => { - assigned_fuehrungassistent = true; - "FüAss" - } - _ => assigned_function.trim(), - }; + for (assigned_name, assigned_function) in r.assignments { + match assigned_function { + Function::Posten => { + assigned_posten += 1; + } + Function::Wachhabender => { + assigned_wachhabender = true; + } + Function::Fuehrungsassistent => { + assigned_fuehrungassistent = true; + } + }; - event_new.push(EventExportEntry { - date: date.to_string(), - weekday: weekday.to_string(), - start_time: start_time.to_string(), - end_time: end_time.to_string(), - hours, - location: location.to_string(), - name: name.to_string(), - assigned_name: Some(assigned_name.trim().to_string()), - assigned_function: Some(assigned_function.to_string()), - }); - } + event_new.push(EventExportEntry { + date: date.to_string(), + weekday: weekday.to_string(), + start_time: start_time.to_string(), + end_time: end_time.to_string(), + hours, + location: r.location_name.to_string(), + name: r.event_name.to_string(), + assigned_name: Some(assigned_name.trim().to_string()), + assigned_function: Some(assigned_function.to_string()), + }); } if !assigned_wachhabender { - let function = if r.voluntarywachhabender { + let function = if r.voluntary_wachhabender { "WH" } else { "BF-WH" @@ -141,63 +94,61 @@ join location on start_time: start_time.to_string(), end_time: end_time.to_string(), hours, - location: location.to_string(), - name: name.to_string(), + location: r.location_name.to_string(), + name: r.event_name.to_string(), assigned_name: None, assigned_function: Some(function.to_string()), }); } - if !assigned_fuehrungassistent && r.voluntaryfuehrungsassistent { + if !assigned_fuehrungassistent && r.voluntary_fuehrungsassistent { event_new.push(EventExportEntry { date: date.to_string(), weekday: weekday.to_string(), start_time: start_time.to_string(), end_time: end_time.to_string(), hours, - location: location.to_string(), - name: name.to_string(), + location: r.location_name.to_string(), + name: r.event_name.to_string(), assigned_name: None, assigned_function: Some("FüAss".to_string()), }); } - if assigned_posten < r.amountofposten { - for _ in 0..(r.amountofposten - assigned_posten) { + if assigned_posten < r.amount_of_posten { + for _ in 0..(r.amount_of_posten - assigned_posten) { event_new.push(EventExportEntry { date: date.to_string(), weekday: weekday.to_string(), start_time: start_time.to_string(), end_time: end_time.to_string(), hours, - location: location.to_string(), - name: name.to_string(), + location: r.location_name.to_string(), + name: r.event_name.to_string(), assigned_name: None, assigned_function: Some("PO".to_string()), }); } } - if let Some(vehicles) = r.vehicles { - for v in vehicles { - event_new.push(EventExportEntry { - date: date.to_string(), - weekday: weekday.to_string(), - start_time: start_time.to_string(), - end_time: end_time.to_string(), - hours, - location: location.to_string(), - name: name.to_string(), - assigned_name: Some(v.to_string()), - assigned_function: Some(String::from("Fzg")), - }); - } + for v in r.vehicles { + event_new.push(EventExportEntry { + date: date.to_string(), + weekday: weekday.to_string(), + start_time: start_time.to_string(), + end_time: end_time.to_string(), + hours, + location: r.location_name.to_string(), + name: r.event_name.to_string(), + assigned_name: Some(v.to_string()), + assigned_function: Some(String::from("Fzg")), + }); } entries.append(&mut event_new); } - Ok(entries) + entries } #[actix_web::get("/export/eventsdata")] @@ -210,7 +161,8 @@ pub async fn get( return Err(ApplicationError::Unauthorized); } - let entries = read(pool.get_ref()).await?; + let rows_to_export = ExportEventRow::read(pool.get_ref()).await?; + let entries = read(rows_to_export); let mut workbook = Workbook::new(); let worksheet = workbook.add_worksheet(); diff --git a/web/src/models/export_event_row.rs b/web/src/models/export_event_row.rs new file mode 100644 index 00000000..f5c88c09 --- /dev/null +++ b/web/src/models/export_event_row.rs @@ -0,0 +1,77 @@ +use chrono::NaiveDateTime; +use sqlx::{query, PgPool}; + +use crate::utils::ApplicationError; + +use super::Function; + +pub struct ExportEventRow { + pub start_timestamp: NaiveDateTime, + pub end_timestamp: NaiveDateTime, + pub amount_of_posten: i16, + pub voluntary_fuehrungsassistent: bool, + pub voluntary_wachhabender: bool, + pub location_name: String, + pub event_name: String, + pub assignments: Vec<(String, Function)>, + pub vehicles: Vec, +} + +impl ExportEventRow { + pub async fn read(pool: &PgPool) -> Result, ApplicationError> { + let rows = query!( + "select + event.starttimestamp, + event.endtimestamp, + event.amountofposten, + event.voluntaryfuehrungsassistent, + event.voluntarywachhabender, + location.name as locationName, + event.name as eventName, + array ( + select + row (user_.name, assignment.function) + from + assignment + join availability on + assignment.availabilityid = availability.id + join user_ on + availability.userid = user_.id + where + assignment.eventId = event.id) as \"assignments: Vec<(String, Function)>\", + array ( + select + vehicle.station || ' ' || vehicle.radiocallname + from + vehicleassignment + join vehicle on + vehicleassignment.vehicleId = vehicle.id + where + vehicleassignment.eventId = event.id) as vehicles +from + event +join location on + event.locationId = location.id + order by event.starttimestamp" + ) + .fetch_all(pool) + .await?; + + let export_rows = rows + .into_iter() + .map(|r| ExportEventRow { + start_timestamp: r.starttimestamp.naive_utc(), + end_timestamp: r.endtimestamp.naive_utc(), + amount_of_posten: r.amountofposten, + voluntary_fuehrungsassistent: r.voluntaryfuehrungsassistent, + voluntary_wachhabender: r.voluntarywachhabender, + location_name: r.locationname, + event_name: r.eventname, + assignments: r.assignments.unwrap_or(Vec::new()), + vehicles: r.vehicles.unwrap_or(Vec::new()), + }) + .collect(); + + Ok(export_rows) + } +} diff --git a/web/src/models/function.rs b/web/src/models/function.rs index 12c5dcd2..4be83532 100644 --- a/web/src/models/function.rs +++ b/web/src/models/function.rs @@ -42,3 +42,13 @@ impl Default for Function { Self::Posten } } + +impl Function { + pub fn short_display(&self) -> &'static str { + match self { + Function::Posten => "PO", + Function::Fuehrungsassistent => "FüAss", + Function::Wachhabender => "WH", + } + } +} diff --git a/web/src/models/mod.rs b/web/src/models/mod.rs index acf360d9..8bc6a698 100644 --- a/web/src/models/mod.rs +++ b/web/src/models/mod.rs @@ -7,6 +7,7 @@ mod availability_changeset; mod clothing; mod event; mod event_changeset; +mod export_event_row; mod function; mod location; mod password_reset; @@ -29,6 +30,7 @@ pub use availability_changeset::{ pub use clothing::Clothing; pub use event::Event; pub use event_changeset::{EventChangeset, EventContext}; +pub use export_event_row::ExportEventRow; pub use function::Function; pub use location::Location; pub use password_reset::{NoneToken, PasswordReset, Token};