Merge branch 'main' of gitlab.com:PhilippHofer/rot

This commit is contained in:
Philipp 2023-04-29 19:09:19 +02:00
commit d741b6c9ab
12 changed files with 96 additions and 29 deletions

View File

@ -21,6 +21,7 @@ CREATE TABLE IF NOT EXISTS "trip_details" (
"planned_starting_time" text NOT NULL, "planned_starting_time" text NOT NULL,
"max_people" INTEGER NOT NULL, "max_people" INTEGER NOT NULL,
"day" TEXT NOT NULL, "day" TEXT NOT NULL,
"allow_guests" boolean NOT NULL default false,
"notes" TEXT, "notes" TEXT,
"trip_type_id" INTEGER, "trip_type_id" INTEGER,
FOREIGN KEY(trip_type_id) REFERENCES trip_type(id) FOREIGN KEY(trip_type_id) REFERENCES trip_type(id)
@ -30,7 +31,6 @@ CREATE TABLE IF NOT EXISTS "planned_event" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" text NOT NULL, "name" text NOT NULL,
"planned_amount_cox" INTEGER unsigned NOT NULL, "planned_amount_cox" INTEGER unsigned NOT NULL,
"allow_guests" boolean NOT NULL default false,
"trip_details_id" INTEGER NOT NULL, "trip_details_id" INTEGER NOT NULL,
"created_at" text NOT NULL DEFAULT CURRENT_TIMESTAMP, "created_at" text NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(trip_details_id) REFERENCES trip_details(id) ON DELETE CASCADE FOREIGN KEY(trip_details_id) REFERENCES trip_details(id) ON DELETE CASCADE

View File

@ -30,4 +30,21 @@ impl Day {
trips: Trip::get_for_day(db, day).await, trips: Trip::get_for_day(db, day).await,
} }
} }
pub async fn new_guest(db: &SqlitePool, day: NaiveDate) -> Self {
let mut day = Self::new(db, day).await;
day.planned_events = day
.planned_events
.into_iter()
.filter(|e| e.planned_event.allow_guests)
.collect();
day.trips = day
.trips
.into_iter()
.filter(|t| t.trip.allow_guests)
.collect();
day
}
} }

View File

@ -9,19 +9,19 @@ pub struct PlannedEvent {
pub id: i64, pub id: i64,
name: String, name: String,
planned_amount_cox: i64, planned_amount_cox: i64,
allow_guests: bool,
trip_details_id: i64, trip_details_id: i64,
planned_starting_time: String, planned_starting_time: String,
max_people: i64, max_people: i64,
day: String, day: String,
notes: Option<String>, notes: Option<String>,
pub allow_guests: bool,
trip_type_id: Option<i64>, trip_type_id: Option<i64>,
} }
#[derive(Serialize)] #[derive(Serialize)]
pub struct PlannedEventWithUserAndTriptype { pub struct PlannedEventWithUserAndTriptype {
#[serde(flatten)] #[serde(flatten)]
planned_event: PlannedEvent, pub planned_event: PlannedEvent,
trip_type: Option<TripType>, trip_type: Option<TripType>,
cox_needed: bool, cox_needed: bool,
cox: Vec<Registration>, cox: Vec<Registration>,
@ -42,7 +42,7 @@ impl PlannedEvent {
Self, Self,
" "
SELECT SELECT
planned_event.id, planned_event.name, planned_amount_cox, allow_guests, trip_details_id, planned_starting_time, max_people, day, notes, trip_type_id planned_event.id, planned_event.name, planned_amount_cox, trip_details_id, planned_starting_time, max_people, day, notes, allow_guests, trip_type_id
FROM planned_event FROM planned_event
INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id
WHERE planned_event.id like ? WHERE planned_event.id like ?
@ -61,7 +61,7 @@ WHERE planned_event.id like ?
let day = format!("{day}"); let day = format!("{day}");
let events = sqlx::query_as!( let events = sqlx::query_as!(
PlannedEvent, PlannedEvent,
"SELECT planned_event.id, planned_event.name, planned_amount_cox, allow_guests, trip_details_id, planned_starting_time, max_people, day, notes, trip_type_id "SELECT planned_event.id, planned_event.name, planned_amount_cox, trip_details_id, planned_starting_time, max_people, day, notes, allow_guests, trip_type_id
FROM planned_event FROM planned_event
INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id
WHERE day=?", WHERE day=?",
@ -127,12 +127,13 @@ FROM user_trip WHERE trip_details_id = (SELECT trip_details_id FROM planned_even
db: &SqlitePool, db: &SqlitePool,
name: String, name: String,
planned_amount_cox: i32, planned_amount_cox: i32,
allow_guests: bool,
trip_details: TripDetails, trip_details: TripDetails,
) { ) {
sqlx::query!( sqlx::query!(
"INSERT INTO planned_event(name, planned_amount_cox, allow_guests, trip_details_id) VALUES(?, ?, ?, ?)", "INSERT INTO planned_event(name, planned_amount_cox, trip_details_id) VALUES(?, ?, ?)",
name, planned_amount_cox, allow_guests, trip_details.id name,
planned_amount_cox,
trip_details.id
) )
.execute(db) .execute(db)
.await .await
@ -170,7 +171,7 @@ mod test {
let trip_details = TripDetails::find_by_id(&pool, 1).await.unwrap(); let trip_details = TripDetails::find_by_id(&pool, 1).await.unwrap();
PlannedEvent::create(&pool, "new-event".into(), 2, false, trip_details).await; PlannedEvent::create(&pool, "new-event".into(), 2, trip_details).await;
let res = let res =
PlannedEvent::get_for_day(&pool, NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()).await; PlannedEvent::get_for_day(&pool, NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()).await;

View File

@ -19,13 +19,14 @@ pub struct Trip {
max_people: i64, max_people: i64,
day: String, day: String,
notes: Option<String>, notes: Option<String>,
pub allow_guests: bool,
trip_type_id: Option<i64>, trip_type_id: Option<i64>,
} }
#[derive(Serialize)] #[derive(Serialize)]
pub struct TripWithUserAndType { pub struct TripWithUserAndType {
#[serde(flatten)] #[serde(flatten)]
trip: Trip, pub trip: Trip,
rower: Vec<Registration>, rower: Vec<Registration>,
trip_type: Option<TripType>, trip_type: Option<TripType>,
} }
@ -47,7 +48,7 @@ impl Trip {
sqlx::query_as!( sqlx::query_as!(
Self, Self,
" "
SELECT trip.id, cox_id, user.name as cox_name, trip_details_id, planned_starting_time, max_people, day, notes, trip_type_id SELECT trip.id, cox_id, user.name as cox_name, trip_details_id, planned_starting_time, max_people, day, notes, allow_guests, trip_type_id
FROM trip FROM trip
INNER JOIN trip_details ON trip.trip_details_id = trip_details.id INNER JOIN trip_details ON trip.trip_details_id = trip_details.id
INNER JOIN user ON trip.cox_id = user.id INNER JOIN user ON trip.cox_id = user.id
@ -100,7 +101,7 @@ WHERE trip.id=?
let trips = sqlx::query_as!( let trips = sqlx::query_as!(
Trip, Trip,
" "
SELECT trip.id, cox_id, user.name as cox_name, trip_details_id, planned_starting_time, max_people, day, notes, trip_type_id SELECT trip.id, cox_id, user.name as cox_name, trip_details_id, planned_starting_time, max_people, day, notes, allow_guests, trip_type_id
FROM trip FROM trip
INNER JOIN trip_details ON trip.trip_details_id = trip_details.id INNER JOIN trip_details ON trip.trip_details_id = trip_details.id
INNER JOIN user ON trip.cox_id = user.id INNER JOIN user ON trip.cox_id = user.id

View File

@ -8,6 +8,7 @@ pub struct TripDetails {
max_people: i64, max_people: i64,
day: String, day: String,
notes: Option<String>, notes: Option<String>,
pub allow_guests: bool,
trip_type_id: Option<i64>, trip_type_id: Option<i64>,
} }
@ -16,7 +17,7 @@ impl TripDetails {
sqlx::query_as!( sqlx::query_as!(
TripDetails, TripDetails,
" "
SELECT id, planned_starting_time, max_people, day, notes, trip_type_id SELECT id, planned_starting_time, max_people, day, notes, allow_guests, trip_type_id
FROM trip_details FROM trip_details
WHERE id like ? WHERE id like ?
", ",
@ -34,14 +35,16 @@ WHERE id like ?
max_people: i32, max_people: i32,
day: String, day: String,
notes: Option<String>, notes: Option<String>,
allow_guests: bool,
trip_type_id: Option<i64>, trip_type_id: Option<i64>,
) -> i64 { ) -> i64 {
let query = sqlx::query!( let query = sqlx::query!(
"INSERT INTO trip_details(planned_starting_time, max_people, day, notes, trip_type_id) VALUES(?, ?, ?, ?, ?)" , "INSERT INTO trip_details(planned_starting_time, max_people, day, notes, allow_guests, trip_type_id) VALUES(?, ?, ?, ?, ?, ?)" ,
planned_starting_time, planned_starting_time,
max_people, max_people,
day, day,
notes, notes,
allow_guests,
trip_type_id trip_type_id
) )
.execute(db) .execute(db)
@ -97,11 +100,29 @@ mod test {
let pool = testdb!(); let pool = testdb!();
assert_eq!( assert_eq!(
TripDetails::create(&pool, "10:00".into(), 2, "1970-01-01".into(), None, None).await, TripDetails::create(
&pool,
"10:00".into(),
2,
"1970-01-01".into(),
None,
false,
None
)
.await,
3, 3,
); );
assert_eq!( assert_eq!(
TripDetails::create(&pool, "10:00".into(), 2, "1970-01-01".into(), None, None).await, TripDetails::create(
&pool,
"10:00".into(),
2,
"1970-01-01".into(),
None,
false,
None
)
.await,
4, 4,
); );
} }

View File

@ -17,7 +17,7 @@ pub struct User {
pw: Option<String>, pw: Option<String>,
pub is_cox: bool, pub is_cox: bool,
pub is_admin: bool, pub is_admin: bool,
is_guest: bool, pub is_guest: bool,
#[serde(default = "bool::default")] #[serde(default = "bool::default")]
deleted: bool, deleted: bool,
} }

View File

@ -14,6 +14,10 @@ impl UserTrip {
return Err(UserTripError::EventAlreadyFull); return Err(UserTripError::EventAlreadyFull);
} }
if user.is_guest && !trip_details.allow_guests {
return Err(UserTripError::GuestNotAllowedForThisEvent);
}
//check if cox if own event //check if cox if own event
let is_cox = sqlx::query!( let is_cox = sqlx::query!(
"SELECT count(*) as amount "SELECT count(*) as amount
@ -80,6 +84,7 @@ pub enum UserTripError {
AlreadyRegisteredAsCox, AlreadyRegisteredAsCox,
EventAlreadyFull, EventAlreadyFull,
CantRegisterAtOwnEvent, CantRegisterAtOwnEvent,
GuestNotAllowedForThisEvent,
} }
#[cfg(test)] #[cfg(test)]
@ -180,4 +185,19 @@ mod test {
assert_eq!(result, UserTripError::AlreadyRegisteredAsCox); assert_eq!(result, UserTripError::AlreadyRegisteredAsCox);
} }
#[sqlx::test]
fn test_fail_create_guest() {
let pool = testdb!();
let user = User::find_by_name(&pool, "guest".into()).await.unwrap();
let trip_details = TripDetails::find_by_id(&pool, 1).await.unwrap();
let result = UserTrip::create(&pool, &user, &trip_details)
.await
.expect_err("Not allowed for guests");
assert_eq!(result, UserTripError::GuestNotAllowedForThisEvent);
}
} }

View File

@ -34,6 +34,7 @@ async fn create(
data.max_people, data.max_people,
data.day.clone(), data.day.clone(),
data.notes.clone(), data.notes.clone(),
data.allow_guests,
data.trip_type, data.trip_type,
) )
.await; .await;
@ -43,14 +44,7 @@ async fn create(
//the object //the object
//TODO: fix clone() //TODO: fix clone()
PlannedEvent::create( PlannedEvent::create(db, data.name.clone(), data.planned_amount_cox, trip_details).await;
db,
data.name.clone(),
data.planned_amount_cox,
data.allow_guests,
trip_details,
)
.await;
Flash::success(Redirect::to("/"), "Successfully planned the event") Flash::success(Redirect::to("/"), "Successfully planned the event")
} }

View File

@ -22,6 +22,7 @@ struct AddTripForm {
max_people: i32, max_people: i32,
notes: Option<String>, notes: Option<String>,
trip_type: Option<i64>, trip_type: Option<i64>,
allow_guests: bool,
} }
#[post("/trip", data = "<data>")] #[post("/trip", data = "<data>")]
@ -33,6 +34,7 @@ async fn create(db: &State<SqlitePool>, data: Form<AddTripForm>, cox: CoxUser) -
data.max_people, data.max_people,
data.day.clone(), data.day.clone(),
data.notes.clone(), data.notes.clone(),
data.allow_guests,
data.trip_type, data.trip_type,
) )
.await; .await;

View File

@ -37,6 +37,7 @@ async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_
.num_days() .num_days()
+ 1; + 1;
} }
if user.is_cox || user.is_admin { if user.is_cox || user.is_admin {
let triptypes = TripType::all(db).await; let triptypes = TripType::all(db).await;
context.insert("trip_types", &triptypes); context.insert("trip_types", &triptypes);
@ -44,7 +45,12 @@ async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_
for i in 0..show_next_n_days { for i in 0..show_next_n_days {
let date = (Local::now() + Duration::days(i)).date_naive(); let date = (Local::now() + Duration::days(i)).date_naive();
days.push(Day::new(db, date).await);
if user.is_guest {
days.push(Day::new_guest(db, date).await);
} else {
days.push(Day::new(db, date).await);
}
} }
if let Some(msg) = flash { if let Some(msg) = flash {
@ -86,6 +92,10 @@ async fn join(db: &State<SqlitePool>, trip_details_id: i64, user: User) -> Flash
Redirect::to("/"), Redirect::to("/"),
"Du kannst bei einer selbst ausgeschriebenen Fahrt nicht mitrudern ;)", "Du kannst bei einer selbst ausgeschriebenen Fahrt nicht mitrudern ;)",
), ),
Err(UserTripError::GuestNotAllowedForThisEvent) => Flash::error(
Redirect::to("/"),
"Bei dieser Ausfahrt können leider keine Gäste mitfahren.",
),
} }
} }

View File

@ -7,8 +7,8 @@
{{ macros::input(label='Startzeit', name='planned_starting_time', type='time', required=true) }} {{ macros::input(label='Startzeit', name='planned_starting_time', type='time', required=true) }}
{{ macros::input(label='Anzahl Steuerleute', name='planned_amount_cox', type='number', required=true, min='0') }} {{ macros::input(label='Anzahl Steuerleute', name='planned_amount_cox', type='number', required=true, min='0') }}
{{ macros::input(label='Anzahl Ruderer (ohne Steuerperson)', name='max_people', type='number', required=true, min='0') }} {{ macros::input(label='Anzahl Ruderer (ohne Steuerperson)', name='max_people', type='number', required=true, min='0') }}
{{ macros::checkbox(label='Gäste erlauben', name='max_allow_guestspeople') }} {{ macros::checkbox(label='Gäste erlauben', name='allow_guests') }}
{{ macros::input(label='Anmerkungen', name='notes', type='input') }} {{ macros::input(label='Anmerkungen', name='notes', type='input') }}
<input value="Erstellen" class="w-full btn btn-primary" type="submit" /> <input value="Erstellen" class="w-full btn btn-primary" type="submit" />
</form> </form>
</div> </div>

View File

@ -5,7 +5,8 @@
<input class="day-js" type="hidden" name="day" value="" /> <input class="day-js" type="hidden" name="day" value="" />
{{ macros::input(label='Startzeit (zB "10:00")', name='planned_starting_time', type='time', required=true) }} {{ macros::input(label='Startzeit (zB "10:00")', name='planned_starting_time', type='time', required=true) }}
{{ macros::input(label='Anzahl Ruderer (ohne Steuerperson)', name='max_people', type='number', required=true, min='0') }} {{ macros::input(label='Anzahl Ruderer (ohne Steuerperson)', name='max_people', type='number', required=true, min='0') }}
{{ macros::checkbox(label='Gäste erlauben', name='allow_guests') }}
{{ macros::input(label='Anmerkungen', name='notes', type='input') }} {{ macros::input(label='Anmerkungen', name='notes', type='input') }}
<input value="Erstellen" class="w-full btn btn-primary" type="submit" /> <input value="Erstellen" class="w-full btn btn-primary" type="submit" />
</form> </form>
</div> </div>