staging #374

Merged
philipp merged 5 commits from staging into main 2024-04-15 18:49:53 +02:00
4 changed files with 124 additions and 11 deletions

View File

@ -30,10 +30,24 @@ pub struct Trip {
pub struct TripWithUserAndType { pub struct TripWithUserAndType {
#[serde(flatten)] #[serde(flatten)]
pub trip: Trip, pub trip: Trip,
rower: Vec<Registration>, pub rower: Vec<Registration>,
trip_type: Option<TripType>, trip_type: Option<TripType>,
} }
impl TripWithUserAndType {
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;
}
Self {
rower: Registration::all_rower(db, trip.trip_details_id.unwrap()).await,
trip,
trip_type,
}
}
}
impl Trip { impl Trip {
/// Cox decides to create own trip. /// Cox decides to create own trip.
pub async fn new_own(db: &SqlitePool, cox: &CoxUser, trip_details: TripDetails) { pub async fn new_own(db: &SqlitePool, cox: &CoxUser, trip_details: TripDetails) {
@ -154,15 +168,7 @@ WHERE day=?
let mut ret = Vec::new(); let mut ret = Vec::new();
for trip in trips { for trip in trips {
let mut trip_type = None; ret.push(TripWithUserAndType::from(db, trip).await);
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 ret
} }
@ -199,6 +205,34 @@ WHERE day=?
.await .await
.unwrap(); //Okay, as trip_details can only be created with proper DB backing .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;
}
}
}
let trip_details = TripDetails::find_by_id(db, trip_details_id).await.unwrap();
trip_details.check_free_spaces(db).await;
Ok(()) Ok(())
} }

View File

@ -4,6 +4,11 @@ use rocket::FromForm;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::{FromRow, SqlitePool}; use sqlx::{FromRow, SqlitePool};
use super::{
notification::Notification,
trip::{Trip, TripWithUserAndType},
};
#[derive(FromRow, Debug, Serialize, Deserialize)] #[derive(FromRow, Debug, Serialize, Deserialize)]
pub struct TripDetails { pub struct TripDetails {
pub id: i64, pub id: i64,
@ -64,6 +69,62 @@ WHERE day = ? AND planned_starting_time = ?
.await.unwrap() .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;
}
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;
};
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. /// Creates a new entry in `trip_details` and returns its id.
pub async fn create(db: &SqlitePool, tripdetails: TripDetailsToAdd<'_>) -> i64 { pub async fn create(db: &SqlitePool, tripdetails: TripDetailsToAdd<'_>) -> i64 {
let query = sqlx::query!( let query = sqlx::query!(

View File

@ -79,6 +79,8 @@ impl UserTrip {
None, None,
) )
.await; .await;
trip_details.check_free_spaces(db).await;
} }
Ok(name_newly_registered_person) Ok(name_newly_registered_person)

View File

@ -310,7 +310,7 @@
<div class="bg-gray-100 dark:bg-primary-900 p-3 mt-4 rounded-md"> <div class="bg-gray-100 dark:bg-primary-900 p-3 mt-4 rounded-md">
<h3 class="text-primary-950 dark:text-white font-bold uppercase tracking-wide mb-2">Ausfahrt bearbeiten</h3> <h3 class="text-primary-950 dark:text-white font-bold uppercase tracking-wide mb-2">Ausfahrt bearbeiten</h3>
<form action="/cox/trip/{{ trip.id }}" method="post" class="grid gap-3"> <form action="/cox/trip/{{ trip.id }}" method="post" class="grid gap-3">
{{ 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::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='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) }} {{ macros::checkbox(label='Gesperrt', name='is_locked', id=trip.id,checked=trip.is_locked) }}
@ -326,6 +326,22 @@
Termin löschen Termin löschen
</a> </a>
</div> </div>
{% else %}
{% if trip.max_people == 0 %}
Wenn du deine Absage absagen (:^)) willst, einfach entsprechende Anzahl an Ruderer oben eintragen.
{% else %}
<div class="bg-gray-100 dark:bg-primary-900 p-3 mt-4 rounded-md">
<h3 class="text-primary-950 dark:text-white font-bold uppercase tracking-wide mb-2">Ausfahrt absagen</h3>
<form action="/cox/trip/{{ trip.id }}" method="post" class="grid">
{{ 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) }}
<input value="Ausfahrt absagen" class="btn btn-alert" type="submit" />
</form>
</div>
{% endif %}
{% endif %} {% endif %}
{% endif %} {% endif %}
{# --- END Edit Form --- #} {# --- END Edit Form --- #}