From 1869b36e090d394ff730a0f533975d5ff3a64bdf Mon Sep 17 00:00:00 2001 From: philipp Date: Mon, 15 Apr 2024 17:17:54 +0200 Subject: [PATCH 1/4] add notification on canceled trips; add explicit 'cancel trip' button --- src/model/trip.rs | 49 ++++++++++++++++++++++++++++++------- templates/planned.html.tera | 18 +++++++++++++- 2 files changed, 57 insertions(+), 10 deletions(-) diff --git a/src/model/trip.rs b/src/model/trip.rs index 0cdfaad..cee6606 100644 --- a/src/model/trip.rs +++ b/src/model/trip.rs @@ -34,6 +34,20 @@ pub struct TripWithUserAndType { trip_type: Option, } +impl TripWithUserAndType { + async fn from(db: &SqlitePool, trip: Trip) -> Self { + let mut trip_type = None; + if let Some(trip_type_id) = trip.trip_type_id { + trip_type = TripType::find_by_id(db, trip_type_id).await; + } + Self { + rower: Registration::all_rower(db, trip.trip_details_id.unwrap()).await, + trip, + trip_type, + } + } +} + impl Trip { /// Cox decides to create own trip. pub async fn new_own(db: &SqlitePool, cox: &CoxUser, trip_details: TripDetails) { @@ -154,15 +168,7 @@ WHERE day=? let mut ret = Vec::new(); for trip in trips { - let mut trip_type = None; - if let Some(trip_type_id) = trip.trip_type_id { - trip_type = TripType::find_by_id(db, trip_type_id).await; - } - ret.push(TripWithUserAndType { - rower: Registration::all_rower(db, trip.trip_details_id.unwrap()).await, - trip, - trip_type, - }); + ret.push(TripWithUserAndType::from(db, trip).await); } ret } @@ -199,6 +205,31 @@ WHERE day=? .await .unwrap(); //Okay, as trip_details can only be created with proper DB backing + if max_people == 0 { + let rowers = TripWithUserAndType::from(db, trip.clone()).await.rower; + for user in rowers { + if let Some(user) = User::find_by_name(db, &user.name).await { + let notes = if let Some(notes) = notes { + format!(": {notes}") + } else { + String::from(".") + }; + + Notification::create( + db, + &user, + &format!( + "Die Ausfahrt von {} am {} um {} wurde abgesagt{}", + cox.user.name, trip.day, trip.planned_starting_time, notes + ), + "Absage Ausfahrt", + None, + ) + .await; + } + } + } + Ok(()) } diff --git a/templates/planned.html.tera b/templates/planned.html.tera index d39f4d2..d5ef0f1 100644 --- a/templates/planned.html.tera +++ b/templates/planned.html.tera @@ -310,7 +310,7 @@

Ausfahrt bearbeiten

- {{ macros::input(label='Anzahl Ruderer', name='max_people', type='number', required=true, value=trip.max_people, min='0') }} + {{ macros::input(label='Anzahl Ruderer', name='max_people', type='number', required=true, value=trip.max_people, min=trip.rower | length) }} {{ macros::input(label='Anmerkungen', name='notes', type='input', value=trip.notes) }} {{ macros::checkbox(label='Immer anzeigen', name='always_show', id=trip.id,checked=trip.always_show) }} {{ macros::checkbox(label='Gesperrt', name='is_locked', id=trip.id,checked=trip.is_locked) }} @@ -326,6 +326,22 @@ Termin löschen
+ {% else %} + {% if trip.max_people == 0 %} + Wenn du deine Absage absagen (:^)) willst, einfach entsprechende Anzahl an Ruderer oben eintragen. + {% else %} +
+

Ausfahrt absagen

+ + {{ macros::input(label='', name='max_people', type='hidden', value=0) }} + {{ macros::input(label='', name='notes', type='hidden', value=trip.notes) }} + {{ macros::input(label='', name='always_show', type='hidden', value=trip.always_show) }} + {{ macros::input(label='', name='is_locked', type='hidden', value=trip.is_locked) }} + {{ macros::input(label='', name='trip_type', type='hidden', value=trip.trip_type_id) }} + + +
+ {% endif %} {% endif %} {% endif %} {# --- END Edit Form --- #} From 74505c1554f3b4e982c07152f6e2ef956ffe3710 Mon Sep 17 00:00:00 2001 From: philipp Date: Mon, 15 Apr 2024 18:16:43 +0200 Subject: [PATCH 2/4] add notification to all pot. coxes if trip is full --- src/model/trip.rs | 7 +++-- src/model/tripdetails.rs | 56 ++++++++++++++++++++++++++++++++++++++++ src/model/usertrip.rs | 9 ++++++- 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/model/trip.rs b/src/model/trip.rs index cee6606..a8d4e16 100644 --- a/src/model/trip.rs +++ b/src/model/trip.rs @@ -30,12 +30,12 @@ pub struct Trip { pub struct TripWithUserAndType { #[serde(flatten)] pub trip: Trip, - rower: Vec, + pub rower: Vec, trip_type: Option, } impl TripWithUserAndType { - async fn from(db: &SqlitePool, trip: Trip) -> Self { + pub async fn from(db: &SqlitePool, trip: Trip) -> Self { let mut trip_type = None; if let Some(trip_type_id) = trip.trip_type_id { trip_type = TripType::find_by_id(db, trip_type_id).await; @@ -230,6 +230,9 @@ WHERE day=? } } + let trip_details = TripDetails::find_by_id(db, trip_details_id).await.unwrap(); + trip_details.check_free_spaces(db).await; + Ok(()) } diff --git a/src/model/tripdetails.rs b/src/model/tripdetails.rs index 6eafa88..ec59386 100644 --- a/src/model/tripdetails.rs +++ b/src/model/tripdetails.rs @@ -4,6 +4,11 @@ use rocket::FromForm; use serde::{Deserialize, Serialize}; use sqlx::{FromRow, SqlitePool}; +use super::{ + notification::Notification, + trip::{Trip, TripWithUserAndType}, +}; + #[derive(FromRow, Debug, Serialize, Deserialize)] pub struct TripDetails { pub id: i64, @@ -64,6 +69,57 @@ WHERE day = ? AND planned_starting_time = ? .await.unwrap() } + /// This function is called when a person registers to a trip or when the cox changes the + /// amount of free places. + pub async fn check_free_spaces(&self, db: &SqlitePool) { + if !self.is_full(db).await { + // We still have space for new people, no need to do anything + return; + } + + let other_trips_same_time = Self::find_by_startingdatetime( + db, + self.day.clone(), + self.planned_starting_time.clone(), + ) + .await; + + for trip in &other_trips_same_time { + if !trip.is_full(db).await { + // There are trips on the same time, with open places + return; + } + } + + // We just got fully booked and there are no other trips with remaining rower places. Send + // notification to all coxes which are registered as non-cox. + for trip_details in other_trips_same_time { + let Some(trip) = Trip::find_by_trip_details(db, trip_details.id).await else { + // This trip_details belongs to a planned_event, no need to do anything + continue; + }; + let pot_coxes = TripWithUserAndType::from(db, trip.clone()).await; + let pot_coxes = pot_coxes.rower; + for user in pot_coxes { + let cox = User::find_by_id(db, trip.cox_id as i32).await.unwrap(); + let Some(user) = User::find_by_name(db, &user.name).await else { + // User is a guest, no need to bother. + continue; + }; + if !user.has_role(db, "cox").await { + // User is no cox, no need to bother + continue; + } + if user.id == cox.id { + // User already offers a trip, no need to bother + continue; + } + + Notification::create(db, &user, &format!("Du hast dich als Ruderer bei der Ausfahrt von {} am {} um {} angemeldet. Bei allen Ausfahrten zu dieser Zeit sind nun alle Plätze ausgebucht. Damit noch mehr (Nicht-Steuerleute) mitfahren können, wäre es super, wenn du eine eigene Ausfahrt zur selben Zeit ausschreiben könntest.", cox.name, self.day, self.planned_starting_time), "Volle Ausfahrt", None).await; + } + } + } + /// Creates a new entry in `trip_details` and returns its id. pub async fn create(db: &SqlitePool, tripdetails: TripDetailsToAdd<'_>) -> i64 { let query = sqlx::query!( diff --git a/src/model/usertrip.rs b/src/model/usertrip.rs index e2a510c..ff12fbf 100644 --- a/src/model/usertrip.rs +++ b/src/model/usertrip.rs @@ -1,6 +1,11 @@ use sqlx::SqlitePool; -use super::{notification::Notification, trip::Trip, tripdetails::TripDetails, user::User}; +use super::{ + notification::Notification, + trip::{Trip, TripWithUserAndType}, + tripdetails::TripDetails, + user::User, +}; use crate::model::tripdetails::{Action, CoxAtTrip::Yes}; pub struct UserTrip {} @@ -79,6 +84,8 @@ impl UserTrip { None, ) .await; + + trip_details.check_free_spaces(db).await; } Ok(name_newly_registered_person) From 0bc00472d7b104d20855fe54ff4eca1bab16300b Mon Sep 17 00:00:00 2001 From: philipp Date: Mon, 15 Apr 2024 18:22:58 +0200 Subject: [PATCH 3/4] don't create any notification if we are working with planned_event --- src/model/tripdetails.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/model/tripdetails.rs b/src/model/tripdetails.rs index ec59386..0667766 100644 --- a/src/model/tripdetails.rs +++ b/src/model/tripdetails.rs @@ -77,6 +77,14 @@ WHERE day = ? AND planned_starting_time = ? return; } + if Trip::find_by_trip_details(db, trip_details.id) + .await + .is_none() + { + // This trip_details belongs to a planned_event, no need to do anything + return; + }; + let other_trips_same_time = Self::find_by_startingdatetime( db, self.day.clone(), From 4b07c11bc377c15209ab813bb6072a8591bb64d3 Mon Sep 17 00:00:00 2001 From: philipp Date: Mon, 15 Apr 2024 18:24:18 +0200 Subject: [PATCH 4/4] fix ci --- src/model/tripdetails.rs | 5 +---- src/model/usertrip.rs | 7 +------ 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/model/tripdetails.rs b/src/model/tripdetails.rs index 0667766..109eefe 100644 --- a/src/model/tripdetails.rs +++ b/src/model/tripdetails.rs @@ -77,10 +77,7 @@ WHERE day = ? AND planned_starting_time = ? return; } - if Trip::find_by_trip_details(db, trip_details.id) - .await - .is_none() - { + if Trip::find_by_trip_details(db, self.id).await.is_none() { // This trip_details belongs to a planned_event, no need to do anything return; }; diff --git a/src/model/usertrip.rs b/src/model/usertrip.rs index ff12fbf..647f7cd 100644 --- a/src/model/usertrip.rs +++ b/src/model/usertrip.rs @@ -1,11 +1,6 @@ use sqlx::SqlitePool; -use super::{ - notification::Notification, - trip::{Trip, TripWithUserAndType}, - tripdetails::TripDetails, - user::User, -}; +use super::{notification::Notification, trip::Trip, tripdetails::TripDetails, user::User}; use crate::model::tripdetails::{Action, CoxAtTrip::Yes}; pub struct UserTrip {}