Compare commits

..

No commits in common. "97dc9308bcc72ab658efbb07c9c9d3d2b75cef93" and "f4ce748a745b532436ce92974423d4e1d45ee04d" have entirely different histories.

11 changed files with 124 additions and 151 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -103,16 +103,18 @@ fn fetch(api_key: &str) -> Result<Data, String> {
let data: Result<Data, _> = response.into_json();
if let Ok(data) = data {
Ok(data)
return Ok(data);
} else {
Err(format!(
return Err(format!(
"Failed to parse the json received by {url}: {}",
data.err().unwrap()
))
));
}
}
Err(_) => Err(format!(
Err(_) => {
return Err(format!(
"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 crate::model::{
planned_event::{self, PlannedEvent},
planned_event::PlannedEvent,
tripdetails::{TripDetails, TripDetailsToAdd},
user::PlannedEventUser,
};
@ -57,17 +57,19 @@ async fn update(
data: Form<UpdatePlannedEventForm<'_>>,
_admin: PlannedEventUser,
) -> 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 {
Some(planned_event) => {
planned_event.update(db, &update).await;
planned_event
.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")
}
None => Flash::error(Redirect::to("/planned"), "Planned event id not found"),

View File

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

View File

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

View File

@ -27,7 +27,7 @@ use crate::model::{
user::{AdminUser, DonauLinzUser, User, UserWithDetails},
};
pub struct KioskCookie(());
pub struct KioskCookie(String);
#[rocket::async_trait]
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> {
match request.cookies().get_private("kiosk") {
Some(_) => request::Outcome::Success(KioskCookie(())),
Some(cookie) => request::Outcome::Success(KioskCookie(cookie.value().to_string())),
None => request::Outcome::Forward(rocket::http::Status::SeeOther),
}
}