- {% 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
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 %}
-
+ {% if "manage_events" in loggedin_user.roles or loggedin_user.allowed_to_steer or "ergo" in loggedin_user.roles %}
+