From eb49a829c627ab686e677427b60a258f5646e241 Mon Sep 17 00:00:00 2001 From: Philipp Hofer Date: Mon, 11 Nov 2024 10:27:50 +0100 Subject: [PATCH 01/10] formatting ergo --- templates/ergo/final.html.tera | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/templates/ergo/final.html.tera b/templates/ergo/final.html.tera index 74dd7a9..8dc4731 100644 --- a/templates/ergo/final.html.tera +++ b/templates/ergo/final.html.tera @@ -7,13 +7,15 @@ Dirty Thirty

- {% for stat in thirty %} - {% set names = stat.name | split(pat=" ") %}{% set lastname_index = names | length - 1 %}{% set lastname = names[lastname_index] %}{{ lastname }} - {% for name in names %} +

@@ -21,17 +23,15 @@ Dirty Dozen

- {% for stat in dozen %} - {% set names = stat.name | split(pat=" ") %} - {% set lastname_index = names | length - 1 %} - {% set lastname = names[lastname_index] %} - {{ lastname }}; - {% for name in names %} +

From d5e6371b897bd27d8d260af328300a52e4ef2b57 Mon Sep 17 00:00:00 2001 From: Philipp Hofer Date: Mon, 11 Nov 2024 13:33:23 +0100 Subject: [PATCH 02/10] craete log if user creates a new event --- src/model/event.rs | 18 ++++++++++++++++-- src/tera/admin/event.rs | 3 ++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/model/event.rs b/src/model/event.rs index cf5917d..a48d18e 100644 --- a/src/model/event.rs +++ b/src/model/event.rs @@ -9,8 +9,12 @@ use serde::Serialize; use sqlx::{FromRow, Row, SqlitePool}; use super::{ - notification::Notification, role::Role, tripdetails::TripDetails, triptype::TripType, - user::User, + log::Log, + notification::Notification, + role::Role, + tripdetails::TripDetails, + triptype::TripType, + user::{EventUser, User}, }; #[derive(Serialize, Clone, FromRow, Debug, PartialEq)] @@ -242,6 +246,7 @@ WHERE trip_details.id=? pub async fn create( db: &SqlitePool, + user: &EventUser, name: &str, planned_amount_cox: i32, always_show: bool, @@ -270,6 +275,15 @@ WHERE trip_details.id=? .execute(db) .await .unwrap(); //Okay, as TripDetails can only be created with proper DB backing + + Log::create( + db, + format!( + "{} created event {} on {} at {}.", + user.user.name, name, trip_details.day, trip_details.planned_starting_time + ), + ) + .await; } //TODO: create unit test diff --git a/src/tera/admin/event.rs b/src/tera/admin/event.rs index ef97fc6..e78c465 100644 --- a/src/tera/admin/event.rs +++ b/src/tera/admin/event.rs @@ -26,7 +26,7 @@ struct AddEventForm<'r> { async fn create( db: &State, data: Form>, - _admin: EventUser, + user: EventUser, ) -> Flash { let data = data.into_inner(); @@ -37,6 +37,7 @@ async fn create( Event::create( db, + &user, data.name, data.planned_amount_cox, data.always_show, From d7eaa14e5548014f855a0185a9277d6450d77a88 Mon Sep 17 00:00:00 2001 From: Philipp Hofer Date: Mon, 11 Nov 2024 15:26:33 +0100 Subject: [PATCH 03/10] fix tests --- src/model/event.rs | 13 +++++++++++-- src/model/notification.rs | 7 +++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/model/event.rs b/src/model/event.rs index a48d18e..c8af6ad 100644 --- a/src/model/event.rs +++ b/src/model/event.rs @@ -463,7 +463,13 @@ WHERE trip_details.id=? #[cfg(test)] mod test { - use crate::{model::tripdetails::TripDetails, testdb}; + use crate::{ + model::{ + tripdetails::TripDetails, + user::{EventUser, User}, + }, + testdb, + }; use super::Event; use chrono::Local; @@ -483,7 +489,10 @@ mod test { let trip_details = TripDetails::find_by_id(&pool, 1).await.unwrap(); - Event::create(&pool, "new-event".into(), 2, false, &trip_details).await; + let admin = EventUser::new(&pool, User::find_by_id(&pool, 1).await.unwrap()) + .await + .unwrap(); + Event::create(&pool, &admin, "new-event".into(), 2, false, &trip_details).await; let res = Event::get_for_day(&pool, Local::now().date_naive()).await; assert_eq!(res.len(), 2); diff --git a/src/model/notification.rs b/src/model/notification.rs index ae73ad3..4a3d274 100644 --- a/src/model/notification.rs +++ b/src/model/notification.rs @@ -221,7 +221,7 @@ mod test { notification::Notification, trip::Trip, tripdetails::{TripDetails, TripDetailsToAdd}, - user::{SteeringUser, User}, + user::{EventUser, SteeringUser, User}, usertrip::UserTrip, }, testdb, @@ -247,7 +247,10 @@ mod test { let trip_details = TripDetails::find_by_id(&pool, tripdetails_id) .await .unwrap(); - Event::create(&pool, "new-event".into(), 2, false, &trip_details).await; + let user = EventUser::new(&pool, User::find_by_id(&pool, 1).await.unwrap()) + .await + .unwrap(); + Event::create(&pool, &user, "new-event".into(), 2, false, &trip_details).await; let event = Event::find_by_trip_details(&pool, trip_details.id) .await .unwrap(); From 768a96345e164c4c2afc71d6a9babd204591676b Mon Sep 17 00:00:00 2001 From: Philipp Hofer Date: Mon, 11 Nov 2024 17:58:13 +0100 Subject: [PATCH 04/10] update data --- templates/ergo/index.html.tera | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/templates/ergo/index.html.tera b/templates/ergo/index.html.tera index 1463cd6..8a165cf 100644 --- a/templates/ergo/index.html.tera +++ b/templates/ergo/index.html.tera @@ -20,11 +20,9 @@ Montag → gemeinsames Training; bitte um Anmeldung, damit jeder einen Ergo hat
  • - Offizielle Ergebnisse: Dirty Thirty (rudernlinz.at/dt) / Dirty Dozen (rudernlinz.at/dd), bei Fehlern direkt mit Offizielle Ergebnisse, bei Fehlern direkt mit Christian (Ister) Kontakt aufnehmen
  • From 88c6469154889204e318f43edf387ea04b132eba Mon Sep 17 00:00:00 2001 From: Philipp Hofer Date: Mon, 11 Nov 2024 22:45:32 +0100 Subject: [PATCH 05/10] trim ergo entries --- src/tera/ergo.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/tera/ergo.rs b/src/tera/ergo.rs index a029a7b..317f008 100644 --- a/src/tera/ergo.rs +++ b/src/tera/ergo.rs @@ -209,10 +209,12 @@ async fn new_thirty( if let Err(e) = data.proof.move_copy_to(file_path).await { eprintln!("Failed to persist file: {:?}", e); } + + let result = data.result.trim_start_matches(|c| c == '0' || c == ' '); sqlx::query!( "UPDATE user SET dirty_thirty = ? where id = ?", - data.result, + result, data.user ) .execute(db.inner()) @@ -253,10 +255,11 @@ async fn new_dozen( if let Err(e) = data.proof.move_copy_to(file_path).await { eprintln!("Failed to persist file: {:?}", e); } + let result = data.result.trim_start_matches(|c| c == '0' || c == ' '); sqlx::query!( "UPDATE user SET dirty_dozen = ? where id = ?", - data.result, + result, data.user ) .execute(db.inner()) From 5c8966f34c4d2c5cb0f8aef8bb87fa64c73f451b Mon Sep 17 00:00:00 2001 From: Philipp Hofer Date: Mon, 11 Nov 2024 22:57:46 +0100 Subject: [PATCH 06/10] always show full time --- src/tera/ergo.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/tera/ergo.rs b/src/tera/ergo.rs index 317f008..4e93c4e 100644 --- a/src/tera/ergo.rs +++ b/src/tera/ergo.rs @@ -233,6 +233,32 @@ async fn new_thirty( Flash::success(Redirect::to("/ergo"), "Erfolgreich eingetragen") } +fn format_time(input: &str) -> String { + let mut parts: Vec<&str> = input.split(':').collect(); + + // If there's only seconds (e.g., "24.2"), treat it as "00:00:24.2" + if parts.len() == 1 { + parts.insert(0, "0"); // Add "0" for hours + parts.insert(0, "0"); // Add "0" for minutes + } + + // If there are two parts (e.g., "4:24.2"), treat it as "00:04:24.2" + if parts.len() == 2 { + parts.insert(0, "0"); // Add "0" for hours + } + + // Now parts should have [hours, minutes, seconds] + let hours = if parts[0].len() == 1 { format!("0{}", parts[0]) } else { parts[0].to_string() }; + let minutes = if parts[1].len() == 1 { format!("0{}", parts[1]) } else { parts[1].to_string() }; + let seconds = parts[2]; + + // Split seconds into whole and fractional parts + let (sec_int, sec_frac) = seconds.split_once('.').unwrap_or((seconds, "0")); + + // Format the time as "hh:mm:ss.s" + format!("{}:{}:{}.{:1}", hours, minutes, sec_int, sec_frac.chars().next().unwrap_or('0')) +} + #[post("/dozen", data = "", format = "multipart/form-data")] async fn new_dozen( db: &State, @@ -256,6 +282,7 @@ async fn new_dozen( eprintln!("Failed to persist file: {:?}", e); } let result = data.result.trim_start_matches(|c| c == '0' || c == ' '); + let result = format_time(result); sqlx::query!( "UPDATE user SET dirty_dozen = ? where id = ?", From 50f410d9fd76c2fbd07413382dff191e199b4cab Mon Sep 17 00:00:00 2001 From: Philipp Hofer Date: Mon, 11 Nov 2024 23:06:53 +0100 Subject: [PATCH 07/10] allow m in dd --- src/tera/ergo.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/tera/ergo.rs b/src/tera/ergo.rs index 4e93c4e..1ad9ecf 100644 --- a/src/tera/ergo.rs +++ b/src/tera/ergo.rs @@ -281,8 +281,13 @@ async fn new_dozen( if let Err(e) = data.proof.move_copy_to(file_path).await { eprintln!("Failed to persist file: {:?}", e); } - let result = data.result.trim_start_matches(|c| c == '0' || c == ' '); - let result = format_time(result); + let result = data.result.trim(); + let result = if result.len() == 4 { +result.to_string() + }else{ + let result = result.trim_start_matches(|c| c == '0' || c == ' '); + format_time(result) + }; sqlx::query!( "UPDATE user SET dirty_dozen = ? where id = ?", From 656c0b99ea114c4ec087fa42c31ef8dc30ce0000 Mon Sep 17 00:00:00 2001 From: Philipp Hofer Date: Mon, 11 Nov 2024 23:11:46 +0100 Subject: [PATCH 08/10] allow for smaller m --- src/tera/ergo.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/tera/ergo.rs b/src/tera/ergo.rs index 1ad9ecf..3a7330e 100644 --- a/src/tera/ergo.rs +++ b/src/tera/ergo.rs @@ -281,12 +281,11 @@ async fn new_dozen( if let Err(e) = data.proof.move_copy_to(file_path).await { eprintln!("Failed to persist file: {:?}", e); } - let result = data.result.trim(); - let result = if result.len() == 4 { -result.to_string() - }else{ - let result = result.trim_start_matches(|c| c == '0' || c == ' '); + let result = data.result.trim_start_matches(|c| c == '0' || c == ' '); + let result = if result.contains(":") || result.contains(".") { format_time(result) + }else{ +result.to_string() }; sqlx::query!( From 1f0b74554fc4000ec101f321d00d13c276c0cd5b Mon Sep 17 00:00:00 2001 From: Philipp Hofer Date: Mon, 25 Nov 2024 12:12:36 +0100 Subject: [PATCH 09/10] allow non-cox to create ergo-trips --- src/model/trip.rs | 39 +++++++++++++++++++++-------- src/model/triptype.rs | 2 +- src/model/user.rs | 1 + src/tera/cox.rs | 34 ++++++++++++++++++++++--- src/tera/planned.rs | 5 +++- templates/forms/trip.html.tera | 6 ++++- templates/includes/macros.html.tera | 4 +-- templates/planned.html.tera | 16 +++++++----- 8 files changed, 82 insertions(+), 25 deletions(-) diff --git a/src/model/trip.rs b/src/model/trip.rs index cefb1ce..71d98df 100644 --- a/src/model/trip.rs +++ b/src/model/trip.rs @@ -9,7 +9,7 @@ use super::{ notification::Notification, tripdetails::TripDetails, triptype::TripType, - user::{SteeringUser, User}, + user::{ErgoUser, SteeringUser, User}, usertrip::UserTrip, }; @@ -38,7 +38,7 @@ pub struct TripWithUserAndType { } pub struct TripUpdate<'a> { - pub cox: &'a SteeringUser, + pub cox: &'a User, pub trip: &'a Trip, pub max_people: i32, pub notes: Option<&'a str>, @@ -63,9 +63,23 @@ impl TripWithUserAndType { impl Trip { /// Cox decides to create own trip. pub async fn new_own(db: &SqlitePool, cox: &SteeringUser, trip_details: TripDetails) { + Self::perform_new(db, &cox.user, trip_details).await + } + + pub async fn new_own_ergo(db: &SqlitePool, ergo: &ErgoUser, trip_details: TripDetails) { + let typ = trip_details.triptype(db).await; + if let Some(typ) = typ { + let allowed_type = TripType::find_by_id(db, 4).await.unwrap(); + if typ == allowed_type { + Self::perform_new(db, &ergo.user, trip_details).await; + } + } + } + + async fn perform_new(db: &SqlitePool, user: &User, trip_details: TripDetails) { let _ = sqlx::query!( "INSERT INTO trip (cox_id, trip_details_id) VALUES(?, ?)", - cox.id, + user.id, trip_details.id ) .execute(db) @@ -96,7 +110,7 @@ impl Trip { &user, &format!( "{} hat eine Ausfahrt zur selben Zeit ({} um {}) wie du erstellt", - cox.user.name, trip.day, trip.planned_starting_time + user.name, trip.day, trip.planned_starting_time ), "Neue Ausfahrt zur selben Zeit", None, @@ -273,6 +287,12 @@ WHERE day=? return Err(TripUpdateError::NotYourTrip); } + if update.trip_type != Some(4) { + if !update.cox.allowed_to_steer(db).await { + return Err(TripUpdateError::TripTypeNotAllowed); + } + } + let Some(trip_details_id) = update.trip.trip_details_id else { return Err(TripUpdateError::TripDetailsDoesNotExist); //TODO: Remove? }; @@ -314,7 +334,7 @@ WHERE day=? &user, &format!( "Die Ausfahrt von {} am {} um {} wurde abgesagt. {} Bitte gib Bescheid, dass du die Info erhalten hast indem du auf ✓ klickst.", - update.cox.user.name, + update.cox.name, update.trip.day, update.trip.planned_starting_time, notes @@ -384,11 +404,7 @@ WHERE day=? Ok(()) } - pub(crate) async fn delete( - &self, - db: &SqlitePool, - user: &SteeringUser, - ) -> Result<(), TripDeleteError> { + pub(crate) async fn delete(&self, db: &SqlitePool, user: &User) -> Result<(), TripDeleteError> { let registered_rower = Registration::all_rower(db, self.trip_details_id.unwrap()).await; if !registered_rower.is_empty() { return Err(TripDeleteError::SomebodyAlreadyRegistered); @@ -398,7 +414,7 @@ WHERE day=? return Err(TripDeleteError::NotYourTrip); } - Log::create(db, format!("{} deleted trip: {:#?}", user.user.name, self)).await; + Log::create(db, format!("{} deleted trip: {:#?}", user.name, self)).await; sqlx::query!("DELETE FROM trip WHERE id = ?", self.id) .execute(db) @@ -464,6 +480,7 @@ pub enum TripDeleteError { pub enum TripUpdateError { NotYourTrip, TripDetailsDoesNotExist, + TripTypeNotAllowed, } #[cfg(test)] diff --git a/src/model/triptype.rs b/src/model/triptype.rs index 370fe19..40d29d7 100644 --- a/src/model/triptype.rs +++ b/src/model/triptype.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use sqlx::{FromRow, SqlitePool}; -#[derive(FromRow, Debug, Serialize, Deserialize, Clone)] +#[derive(FromRow, Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct TripType { pub id: i64, pub name: String, diff --git a/src/model/user.rs b/src/model/user.rs index 8634682..f03b0b4 100644 --- a/src/model/user.rs +++ b/src/model/user.rs @@ -1166,6 +1166,7 @@ macro_rules! special_user { } special_user!(TechUser, +"tech"); +special_user!(ErgoUser, +"ergo"); special_user!(SteeringUser, +"cox", +"Bootsführer"); special_user!(AdminUser, +"admin"); special_user!(AllowedForPlannedTripsUser, +"Donau Linz", +"scheckbuch"); diff --git a/src/tera/cox.rs b/src/tera/cox.rs index dbee671..31c791c 100644 --- a/src/tera/cox.rs +++ b/src/tera/cox.rs @@ -11,9 +11,32 @@ use crate::model::{ log::Log, trip::{self, CoxHelpError, Trip, TripDeleteError, TripHelpDeleteError, TripUpdateError}, tripdetails::{TripDetails, TripDetailsToAdd}, - user::{AllowedToUpdateTripToAlwaysBeShownUser, SteeringUser}, + user::{AllowedToUpdateTripToAlwaysBeShownUser, ErgoUser, SteeringUser, User}, }; +#[post("/trip", data = "", rank = 2)] +async fn create_ergo( + db: &State, + data: Form>, + cox: ErgoUser, +) -> Flash { + let trip_details_id = TripDetails::create(db, data.into_inner()).await; + let trip_details = TripDetails::find_by_id(db, trip_details_id).await.unwrap(); //Okay, bc just + //created + Trip::new_own_ergo(db, &cox, trip_details).await; //TODO: fix + + //Log::create( + // db, + // format!( + // "Cox {} created trip on {} @ {} for {} rower", + // cox.name, trip_details.day, trip_details.planned_starting_time, trip_details.max_people, + // ), + //) + //.await; + + Flash::success(Redirect::to("/planned"), "Ausfahrt erfolgreich erstellt.") +} + #[post("/trip", data = "")] async fn create( db: &State, @@ -50,7 +73,7 @@ async fn update( db: &State, data: Form>, trip_id: i64, - cox: SteeringUser, + cox: User, ) -> Flash { if let Some(trip) = Trip::find_by_id(db, trip_id).await { let update = trip::TripUpdate { @@ -69,6 +92,10 @@ async fn update( Err(TripUpdateError::NotYourTrip) => { Flash::error(Redirect::to("/planned"), "Nicht deine Ausfahrt!") } + Err(TripUpdateError::TripTypeNotAllowed) => Flash::error( + Redirect::to("/planned"), + "Du darfst nur Ergo-Events erstellen", + ), Err(TripUpdateError::TripDetailsDoesNotExist) => { Flash::error(Redirect::to("/planned"), "Ausfahrt gibt's nicht") } @@ -130,7 +157,7 @@ async fn join(db: &State, planned_event_id: i64, cox: SteeringUser) } #[get("/remove/trip/")] -async fn remove_trip(db: &State, trip_id: i64, cox: SteeringUser) -> Flash { +async fn remove_trip(db: &State, trip_id: i64, cox: User) -> Flash { let trip = Trip::find_by_id(db, trip_id).await; match trip { None => Flash::error(Redirect::to("/planned"), "Trip gibt's nicht!"), @@ -185,6 +212,7 @@ async fn remove( pub fn routes() -> Vec { routes![ create, + create_ergo, join, remove, remove_trip, diff --git a/src/tera/planned.rs b/src/tera/planned.rs index 184f926..3102818 100644 --- a/src/tera/planned.rs +++ b/src/tera/planned.rs @@ -29,7 +29,10 @@ async fn index( let mut context = Context::new(); - if user.allowed_to_steer(db).await || user.has_role(db, "manage_events").await { + if user.allowed_to_steer(db).await + || user.has_role(db, "manage_events").await + || user.has_role(db, "ergo").await + { let triptypes = TripType::all(db).await; context.insert("trip_types", &triptypes); } diff --git a/templates/forms/trip.html.tera b/templates/forms/trip.html.tera index 0632e59..5cc8dea 100644 --- a/templates/forms/trip.html.tera +++ b/templates/forms/trip.html.tera @@ -6,7 +6,11 @@ {{ macros::input(label='Anzahl Ruderer (ohne Steuerperson)', name='max_people', type='number', required=true, min='0') }} {{ macros::checkbox(label='Scheckbuch-Anmeldungen erlauben', name='allow_guests') }} {{ macros::input(label='Anmerkungen', name='notes', type='input') }} - {{ macros::select(label='Typ', data=trip_types, name='trip_type', default='Reguläre Ausfahrt') }} + {% if loggedin_user.allowed_to_steer %} + {{ macros::select(label='Typ', data=trip_types, name='trip_type', default='Reguläre Ausfahrt') }} + {% else %} + {{ macros::select(label='Typ', data=trip_types, name='trip_type', only_ergo=true) }} + {% endif %} diff --git a/templates/includes/macros.html.tera b/templates/includes/macros.html.tera index e3a0b30..a8550a6 100644 --- a/templates/includes/macros.html.tera +++ b/templates/includes/macros.html.tera @@ -190,7 +190,7 @@ function setChoiceByLabel(choicesInstance, label) { {{ label }} {% endmacro checkbox %} -{% macro select(label, data, name='trip_type', default='', id='', selected_id='', display='', extras='', class='', wrapper_class='', required=false, show_seats=false, new_last_entry='', nonSelectableDefault=false) %} +{% macro select(label, data, name='trip_type', default='', id='', selected_id='', display='', extras='', class='', wrapper_class='', required=false, show_seats=false, new_last_entry='', nonSelectableDefault=false, only_ergo=false) %}
    {% if display == '' %} @@ -203,7 +203,7 @@ function setChoiceByLabel(choicesInstance, label) { {% if default %}{% endif %} {% if nonSelectableDefault %}{% endif %} {% for d in data %} -
    @@ -421,11 +425,11 @@ {% endif %} {# --- START Add Buttons --- #} - {% if "manage_events" in loggedin_user.roles or loggedin_user.allowed_to_steer %} -