Compare commits

..

4 Commits

Author SHA1 Message Date
97dc9308bc Merge pull request 'clippy' (#526) from clippy into staging
Some checks failed
CI/CD Pipeline / test (push) Failing after 1m2s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #526
2024-05-22 00:18:35 +02:00
db5e0873a6 Merge branch 'staging' into clippy
Some checks failed
CI/CD Pipeline / test (push) Failing after 1m9s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-22 00:18:17 +02:00
40f97f18a9 clean code with clippy
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
2024-05-22 00:13:23 +02:00
6b911f242a Merge pull request 'fix html duplicate tags' (#525) from fix-html into main
Some checks failed
CI/CD Pipeline / test (push) Successful in 10m7s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been cancelled
Reviewed-on: #525
2024-05-21 23:46:17 +02:00
11 changed files with 151 additions and 124 deletions

View File

@ -118,7 +118,7 @@ pub enum LogbookCreateError {
BoatLocked, BoatLocked,
BoatNotFound, BoatNotFound,
TooManyRowers(usize, usize), TooManyRowers(usize, usize),
RowerAlreadyOnWater(User), RowerAlreadyOnWater(Box<User>),
RowerCreateError(i64, String), RowerCreateError(i64, String),
ArrivalNotAfterDeparture, ArrivalNotAfterDeparture,
SteeringPersonNotInRowers, SteeringPersonNotInRowers,
@ -386,7 +386,7 @@ ORDER BY departure DESC
let user = User::find_by_id(db, *rower as i32).await.unwrap(); let user = User::find_by_id(db, *rower as i32).await.unwrap();
if user.on_water(db).await { if user.on_water(db).await {
return Err(LogbookCreateError::RowerAlreadyOnWater(user)); return Err(LogbookCreateError::RowerAlreadyOnWater(Box::new(user)));
} }
} }

View File

@ -98,6 +98,15 @@ FROM trip WHERE planned_event_id = ?
} }
} }
pub struct EventUpdate<'a> {
pub name: &'a str,
pub planned_amount_cox: i32,
pub max_people: i32,
pub notes: Option<&'a str>,
pub always_show: bool,
pub is_locked: bool,
}
impl PlannedEvent { impl PlannedEvent {
pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> { pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> {
sqlx::query_as!( sqlx::query_as!(
@ -207,20 +216,11 @@ INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id",
} }
//TODO: create unit test //TODO: create unit test
pub async fn update( pub async fn update(&self, db: &SqlitePool, update: &EventUpdate<'_>) {
&self,
db: &SqlitePool,
name: &str,
planned_amount_cox: i32,
max_people: i32,
notes: Option<&str>,
always_show: bool,
is_locked: bool,
) {
sqlx::query!( sqlx::query!(
"UPDATE planned_event SET name = ?, planned_amount_cox = ? WHERE id = ?", "UPDATE planned_event SET name = ?, planned_amount_cox = ? WHERE id = ?",
name, update.name,
planned_amount_cox, update.planned_amount_cox,
self.id self.id
) )
.execute(db) .execute(db)
@ -232,10 +232,10 @@ INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id",
sqlx::query!( sqlx::query!(
"UPDATE trip_details SET max_people = ?, notes = ?, always_show = ?, is_locked = ? WHERE id = ?", "UPDATE trip_details SET max_people = ?, notes = ?, always_show = ?, is_locked = ? WHERE id = ?",
max_people, update.max_people,
notes, update.notes,
always_show, update.always_show,
is_locked, update.is_locked,
self.trip_details_id self.trip_details_id
) )
.execute(db) .execute(db)
@ -307,16 +307,18 @@ INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id",
} }
pub async fn delete(&self, db: &SqlitePool) -> Result<(), String> { pub async fn delete(&self, db: &SqlitePool) -> Result<(), String> {
if Registration::all_rower(db, self.trip_details_id) if !Registration::all_rower(db, self.trip_details_id)
.await .await
.len() .is_empty()
> 0
{ {
return Err( return Err(
"Event kann nicht gelöscht werden, weil mind. 1 Ruderer angemeldet ist.".into(), "Event kann nicht gelöscht werden, weil mind. 1 Ruderer angemeldet ist.".into(),
); );
} }
if Registration::all_cox(db, self.id).await.len() > 0 { if !Registration::all_cox(db, self.trip_details_id)
.await
.is_empty()
{
return Err( return Err(
"Event kann nicht gelöscht werden, weil mind. 1 Steuerperson angemeldet ist." "Event kann nicht gelöscht werden, weil mind. 1 Steuerperson angemeldet ist."
.into(), .into(),

View File

@ -34,6 +34,16 @@ pub struct TripWithUserAndType {
trip_type: Option<TripType>, trip_type: Option<TripType>,
} }
pub struct TripUpdate<'a> {
pub cox: &'a CoxUser,
pub trip: &'a Trip,
pub max_people: i32,
pub notes: Option<&'a str>,
pub trip_type: Option<i64>, //TODO: Move to `TripType`
pub always_show: bool,
pub is_locked: bool,
}
impl TripWithUserAndType { impl TripWithUserAndType {
pub async fn from(db: &SqlitePool, trip: Trip) -> Self { pub async fn from(db: &SqlitePool, trip: Trip) -> Self {
let mut trip_type = None; let mut trip_type = None;
@ -181,19 +191,13 @@ WHERE day=?
/// Cox decides to update own trip. /// Cox decides to update own trip.
pub async fn update_own( pub async fn update_own(
db: &SqlitePool, db: &SqlitePool,
cox: &CoxUser, update: &TripUpdate<'_>,
trip: &Trip,
max_people: i32,
notes: Option<&str>,
trip_type: Option<i64>, //TODO: Move to `TripType`
always_show: bool,
is_locked: bool,
) -> Result<(), TripUpdateError> { ) -> Result<(), TripUpdateError> {
if !trip.is_trip_from_user(cox.id) { if !update.trip.is_trip_from_user(update.cox.id) {
return Err(TripUpdateError::NotYourTrip); return Err(TripUpdateError::NotYourTrip);
} }
let Some(trip_details_id) = trip.trip_details_id else { let Some(trip_details_id) = update.trip.trip_details_id else {
return Err(TripUpdateError::TripDetailsDoesNotExist); //TODO: Remove? return Err(TripUpdateError::TripDetailsDoesNotExist); //TODO: Remove?
}; };
@ -202,22 +206,24 @@ WHERE day=?
sqlx::query!( sqlx::query!(
"UPDATE trip_details SET max_people = ?, notes = ?, trip_type_id = ?, always_show = ?, is_locked = ? WHERE id = ?", "UPDATE trip_details SET max_people = ?, notes = ?, trip_type_id = ?, always_show = ?, is_locked = ? WHERE id = ?",
max_people, update.max_people,
notes, update.notes,
trip_type, update.trip_type,
always_show, update.always_show,
is_locked, update.is_locked,
trip_details_id trip_details_id
) )
.execute(db) .execute(db)
.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 && !was_already_cancelled { if update.max_people == 0 && !was_already_cancelled {
let rowers = TripWithUserAndType::from(db, trip.clone()).await.rower; let rowers = TripWithUserAndType::from(db, update.trip.clone())
.await
.rower;
for user in rowers { for user in rowers {
if let Some(user) = User::find_by_name(db, &user.name).await { if let Some(user) = User::find_by_name(db, &user.name).await {
let notes = match notes { let notes = match update.notes {
Some(n) if !n.is_empty() => n, Some(n) if !n.is_empty() => n,
_ => ".", _ => ".",
}; };
@ -227,7 +233,10 @@ WHERE day=?
&user, &user,
&format!( &format!(
"Die Ausfahrt von {} am {} um {} wurde abgesagt{}", "Die Ausfahrt von {} am {} um {} wurde abgesagt{}",
cox.user.name, trip.day, trip.planned_starting_time, notes update.cox.user.name,
update.trip.day,
update.trip.planned_starting_time,
notes
), ),
"Absage Ausfahrt", "Absage Ausfahrt",
None, None,
@ -247,7 +256,7 @@ WHERE day=?
.await; .await;
} }
if max_people > 0 && was_already_cancelled { if update.max_people > 0 && was_already_cancelled {
Notification::delete_by_action( Notification::delete_by_action(
db, db,
&format!("remove_user_trip_with_trip_details_id:{}", trip_details_id), &format!("remove_user_trip_with_trip_details_id:{}", trip_details_id),
@ -365,7 +374,7 @@ mod test {
use crate::{ use crate::{
model::{ model::{
planned_event::PlannedEvent, planned_event::PlannedEvent,
trip::TripDeleteError, trip::{self, TripDeleteError},
tripdetails::TripDetails, tripdetails::TripDetails,
user::{CoxUser, User}, user::{CoxUser, User},
usertrip::UserTrip, usertrip::UserTrip,
@ -450,11 +459,17 @@ mod test {
let trip = Trip::find_by_id(&pool, 1).await.unwrap(); let trip = Trip::find_by_id(&pool, 1).await.unwrap();
assert!( let update = trip::TripUpdate {
Trip::update_own(&pool, &cox, &trip, 10, None, None, false, false) cox: &cox,
.await trip: &trip,
.is_ok() max_people: 10,
); notes: None,
trip_type: None,
always_show: false,
is_locked: false,
};
assert!(Trip::update_own(&pool, &update).await.is_ok());
let trip = Trip::find_by_id(&pool, 1).await.unwrap(); let trip = Trip::find_by_id(&pool, 1).await.unwrap();
assert_eq!(trip.max_people, 10); assert_eq!(trip.max_people, 10);
@ -473,11 +488,16 @@ mod test {
let trip = Trip::find_by_id(&pool, 1).await.unwrap(); let trip = Trip::find_by_id(&pool, 1).await.unwrap();
assert!( let update = trip::TripUpdate {
Trip::update_own(&pool, &cox, &trip, 10, None, Some(1), false, false) cox: &cox,
.await trip: &trip,
.is_ok() max_people: 10,
); notes: None,
trip_type: Some(1),
always_show: false,
is_locked: false,
};
assert!(Trip::update_own(&pool, &update).await.is_ok());
let trip = Trip::find_by_id(&pool, 1).await.unwrap(); let trip = Trip::find_by_id(&pool, 1).await.unwrap();
assert_eq!(trip.max_people, 10); assert_eq!(trip.max_people, 10);
@ -497,11 +517,16 @@ mod test {
let trip = Trip::find_by_id(&pool, 1).await.unwrap(); let trip = Trip::find_by_id(&pool, 1).await.unwrap();
assert!( let update = trip::TripUpdate {
Trip::update_own(&pool, &cox, &trip, 10, None, None, false, false) cox: &cox,
.await trip: &trip,
.is_err() max_people: 10,
); notes: None,
trip_type: None,
always_show: false,
is_locked: false,
};
assert!(Trip::update_own(&pool, &update).await.is_err());
assert_eq!(trip.max_people, 1); assert_eq!(trip.max_people, 1);
} }

View File

@ -80,7 +80,7 @@ pub enum LoginError {
NotACox, NotACox,
NotATech, NotATech,
GuestNotAllowed, GuestNotAllowed,
NoPasswordSet(User), NoPasswordSet(Box<User>),
DeserializationError, DeserializationError,
} }
@ -706,7 +706,7 @@ ORDER BY last_access DESC
Err(LoginError::InvalidAuthenticationCombo) Err(LoginError::InvalidAuthenticationCombo)
} else { } else {
info!("User {name} has no PW set"); info!("User {name} has no PW set");
Err(LoginError::NoPasswordSet(user)) Err(LoginError::NoPasswordSet(Box::new(user)))
} }
} }

View File

@ -17,6 +17,17 @@ pub struct Waterlevel {
pub tumittel: i64, pub tumittel: i64,
} }
pub struct Create {
pub day: NaiveDate,
pub time: String,
pub max: i64,
pub min: i64,
pub mittel: i64,
pub tumax: i64,
pub tumin: i64,
pub tumittel: i64,
}
impl Waterlevel { impl Waterlevel {
pub async fn find_by_id(db: &SqlitePool, id: i32) -> Option<Self> { pub async fn find_by_id(db: &SqlitePool, id: i32) -> Option<Self> {
sqlx::query_as!(Self, "SELECT * FROM waterlevel WHERE id like ?", id) sqlx::query_as!(Self, "SELECT * FROM waterlevel WHERE id like ?", id)
@ -31,20 +42,10 @@ impl Waterlevel {
.ok() .ok()
} }
pub async fn create( pub async fn create(db: &mut Transaction<'_, Sqlite>, create: &Create) -> Result<(), String> {
db: &mut Transaction<'_, Sqlite>,
day: NaiveDate,
time: String,
max: i64,
min: i64,
mittel: i64,
tumax: i64,
tumin: i64,
tumittel: i64,
) -> Result<(), String> {
sqlx::query!( sqlx::query!(
"INSERT INTO waterlevel(day, time, max, min, mittel, tumax, tumin, tumittel) VALUES (?,?,?,?,?,?,?,?)", "INSERT INTO waterlevel(day, time, max, min, mittel, tumax, tumin, tumittel) VALUES (?,?,?,?,?,?,?,?)",
day, time, max, min, mittel, tumax, tumin, tumittel create.day, create.time, create.max, create.min, create.mittel, create.tumax, create.tumin, create.tumittel
) )
.execute(db.deref_mut()) .execute(db.deref_mut())
.await .await

View File

@ -2,7 +2,7 @@ use chrono::{DateTime, FixedOffset, NaiveDate, NaiveTime};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::SqlitePool; use sqlx::SqlitePool;
use crate::model::waterlevel::Waterlevel; use crate::model::waterlevel::{self, Waterlevel};
pub async fn update(db: &SqlitePool) -> Result<(), String> { pub async fn update(db: &SqlitePool) -> Result<(), String> {
let mut tx = db.begin().await.unwrap(); let mut tx = db.begin().await.unwrap();
@ -29,10 +29,18 @@ pub async fn update(db: &SqlitePool) -> Result<(), String> {
let time: NaiveTime = datetime.naive_utc().time(); let time: NaiveTime = datetime.naive_utc().time();
let time_str = time.format("%H:%M").to_string(); let time_str = time.format("%H:%M").to_string();
Waterlevel::create( let create = waterlevel::Create {
&mut tx, date, time_str, max, min, mittel, tumax, tumin, tumittel, day: date,
) time: time_str,
.await? max,
min,
mittel,
tumax,
tumin,
tumittel,
};
Waterlevel::create(&mut tx, &create).await?
} }
// 3. Save in DB // 3. Save in DB
@ -77,25 +85,23 @@ fn fetch() -> Result<Station, String> {
if let Ok(data) = forecast { if let Ok(data) = forecast {
if data.len() == 1 { if data.len() == 1 {
return Ok(data[0].clone()); Ok(data[0].clone())
} else { } else {
return Err(format!( Err(format!(
"Expected 1 station (Linz); got {} while fetching from {url}. Maybe the hydro data format changed?", "Expected 1 station (Linz); got {} while fetching from {url}. Maybe the hydro data format changed?",
data.len() data.len()
)); ))
} }
} else { } else {
return Err(format!( Err(format!(
"Failed to parse the json received by {url}: {}", "Failed to parse the json received by {url}: {}",
forecast.err().unwrap() forecast.err().unwrap()
)); ))
} }
} }
Err(_) => { Err(_) => Err(format!(
return Err(format!(
"Could not fetch {url}, do you have internet? Maybe their server is down?" "Could not fetch {url}, do you have internet? Maybe their server is down?"
)); )),
}
} }
} }

View File

@ -103,18 +103,16 @@ fn fetch(api_key: &str) -> Result<Data, String> {
let data: Result<Data, _> = response.into_json(); let data: Result<Data, _> = response.into_json();
if let Ok(data) = data { if let Ok(data) = data {
return Ok(data); Ok(data)
} else { } else {
return Err(format!( Err(format!(
"Failed to parse the json received by {url}: {}", "Failed to parse the json received by {url}: {}",
data.err().unwrap() data.err().unwrap()
)); ))
} }
} }
Err(_) => { Err(_) => Err(format!(
return Err(format!(
"Could not fetch {url}, do you have internet? Maybe their server is down?" "Could not fetch {url}, do you have internet? Maybe their server is down?"
)); )),
}
} }
} }

View File

@ -8,7 +8,7 @@ use serde::Serialize;
use sqlx::SqlitePool; use sqlx::SqlitePool;
use crate::model::{ use crate::model::{
planned_event::PlannedEvent, planned_event::{self, PlannedEvent},
tripdetails::{TripDetails, TripDetailsToAdd}, tripdetails::{TripDetails, TripDetailsToAdd},
user::PlannedEventUser, user::PlannedEventUser,
}; };
@ -57,19 +57,17 @@ async fn update(
data: Form<UpdatePlannedEventForm<'_>>, data: Form<UpdatePlannedEventForm<'_>>,
_admin: PlannedEventUser, _admin: PlannedEventUser,
) -> Flash<Redirect> { ) -> Flash<Redirect> {
let update = planned_event::EventUpdate {
name: data.name,
planned_amount_cox: data.planned_amount_cox,
max_people: data.max_people,
notes: data.notes,
always_show: data.always_show,
is_locked: data.is_locked,
};
match PlannedEvent::find_by_id(db, data.id).await { match PlannedEvent::find_by_id(db, data.id).await {
Some(planned_event) => { Some(planned_event) => {
planned_event planned_event.update(db, &update).await;
.update(
db,
data.name,
data.planned_amount_cox,
data.max_people,
data.notes,
data.always_show,
data.is_locked,
)
.await;
Flash::success(Redirect::to("/planned"), "Event erfolgreich bearbeitet") Flash::success(Redirect::to("/planned"), "Event erfolgreich bearbeitet")
} }
None => Flash::error(Redirect::to("/planned"), "Planned event id not found"), None => Flash::error(Redirect::to("/planned"), "Planned event id not found"),

View File

@ -39,7 +39,6 @@ struct LoginForm<'r> {
password: &'r str, password: &'r str,
} }
#[derive(Debug)]
pub struct UserAgent(String); pub struct UserAgent(String);
#[rocket::async_trait] #[rocket::async_trait]
@ -83,8 +82,8 @@ async fn login(
Log::create( Log::create(
db, db,
format!( format!(
"Succ login of {} with this useragent: {:?}", "Succ login of {} with this useragent: {}",
login.name, agent login.name, agent.0
), ),
) )
.await; .await;

View File

@ -9,7 +9,7 @@ use sqlx::SqlitePool;
use crate::model::{ use crate::model::{
log::Log, log::Log,
planned_event::PlannedEvent, planned_event::PlannedEvent,
trip::{CoxHelpError, Trip, TripDeleteError, TripHelpDeleteError, TripUpdateError}, trip::{self, CoxHelpError, Trip, TripDeleteError, TripHelpDeleteError, TripUpdateError},
tripdetails::{TripDetails, TripDetailsToAdd}, tripdetails::{TripDetails, TripDetailsToAdd},
user::CoxUser, user::CoxUser,
}; };
@ -54,18 +54,16 @@ async fn update(
cox: CoxUser, cox: CoxUser,
) -> Flash<Redirect> { ) -> Flash<Redirect> {
if let Some(trip) = Trip::find_by_id(db, trip_id).await { if let Some(trip) = Trip::find_by_id(db, trip_id).await {
match Trip::update_own( let update = trip::TripUpdate {
db, cox: &cox,
&cox, trip: &trip,
&trip, max_people: data.max_people,
data.max_people, notes: data.notes,
data.notes, trip_type: data.trip_type,
data.trip_type, always_show: data.always_show,
data.always_show, is_locked: data.is_locked,
data.is_locked, };
) match Trip::update_own(db, &update).await {
.await
{
Ok(_) => Flash::success( Ok(_) => Flash::success(
Redirect::to("/planned"), Redirect::to("/planned"),
"Ausfahrt erfolgreich aktualisiert.", "Ausfahrt erfolgreich aktualisiert.",

View File

@ -27,7 +27,7 @@ use crate::model::{
user::{AdminUser, DonauLinzUser, User, UserWithDetails}, user::{AdminUser, DonauLinzUser, User, UserWithDetails},
}; };
pub struct KioskCookie(String); pub struct KioskCookie(());
#[rocket::async_trait] #[rocket::async_trait]
impl<'r> FromRequest<'r> for KioskCookie { impl<'r> FromRequest<'r> for KioskCookie {
@ -35,7 +35,7 @@ impl<'r> FromRequest<'r> for KioskCookie {
async fn from_request(request: &'r Request<'_>) -> request::Outcome<KioskCookie, Self::Error> { async fn from_request(request: &'r Request<'_>) -> request::Outcome<KioskCookie, Self::Error> {
match request.cookies().get_private("kiosk") { match request.cookies().get_private("kiosk") {
Some(cookie) => request::Outcome::Success(KioskCookie(cookie.value().to_string())), Some(_) => request::Outcome::Success(KioskCookie(())),
None => request::Outcome::Forward(rocket::http::Status::SeeOther), None => request::Outcome::Forward(rocket::http::Status::SeeOther),
} }
} }