forked from Ruderverein-Donau-Linz/rowt
		
	Merge branch 'feature/triptypebackend' into 'main'
Feature/triptypebackend See merge request PhilippHofer/rot!4
This commit is contained in:
		@@ -8,12 +8,22 @@ CREATE TABLE IF NOT EXISTS "user" (
 | 
			
		||||
	"deleted" boolean NOT NULL DEFAULT FALSE
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
CREATE TABLE IF NOT EXISTS "trip_type" (
 | 
			
		||||
	"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
 | 
			
		||||
	"name" text NOT NULL UNIQUE,
 | 
			
		||||
	"desc" text NOT NULL,
 | 
			
		||||
	"question" text NOT NULL,
 | 
			
		||||
	"icon" text NOT NULL
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
CREATE TABLE IF NOT EXISTS "trip_details" (
 | 
			
		||||
	"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
 | 
			
		||||
	"planned_starting_time" text NOT NULL,
 | 
			
		||||
	"max_people" INTEGER NOT NULL,
 | 
			
		||||
	"day" TEXT NOT NULL,
 | 
			
		||||
	"notes" TEXT
 | 
			
		||||
	"notes" TEXT,
 | 
			
		||||
	"trip_type_id" INTEGER,
 | 
			
		||||
	FOREIGN KEY(trip_type_id) REFERENCES trip_type(id)
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
CREATE TABLE IF NOT EXISTS "planned_event" (
 | 
			
		||||
@@ -52,3 +62,4 @@ CREATE TABLE IF NOT EXISTS "log" (
 | 
			
		||||
	"msg" text NOT NULL,
 | 
			
		||||
	"created_at" text NOT NULL DEFAULT CURRENT_TIMESTAMP
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -12,3 +12,4 @@ INSERT INTO "planned_event" (name, planned_amount_cox, trip_details_id) VALUES('
 | 
			
		||||
INSERT INTO "trip_details" (planned_starting_time, max_people, day, notes) VALUES('11:00', 1, '1970-01-02', 'trip_details for trip from cox');
 | 
			
		||||
INSERT INTO "trip" (cox_id, trip_details_id) VALUES(4, 2);
 | 
			
		||||
 | 
			
		||||
INSERT INTO "trip_type" (name, desc, question, icon) VALUES ('Regatta', 'Regatta!', 'Kein normales Event. Das ist eine Regatta! Willst du wirklich teilnehmen?', '🏅')
 | 
			
		||||
 
 | 
			
		||||
@@ -3,22 +3,23 @@ use serde::Serialize;
 | 
			
		||||
use sqlx::SqlitePool;
 | 
			
		||||
 | 
			
		||||
use self::{
 | 
			
		||||
    planned_event::{PlannedEvent, PlannedEventWithUser},
 | 
			
		||||
    trip::{Trip, TripWithUser},
 | 
			
		||||
    planned_event::{PlannedEvent, PlannedEventWithUserAndTriptype},
 | 
			
		||||
    trip::{Trip, TripWithUserAndType},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub mod log;
 | 
			
		||||
pub mod planned_event;
 | 
			
		||||
pub mod trip;
 | 
			
		||||
pub mod tripdetails;
 | 
			
		||||
pub mod triptype;
 | 
			
		||||
pub mod user;
 | 
			
		||||
pub mod usertrip;
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize)]
 | 
			
		||||
pub struct Day {
 | 
			
		||||
    day: NaiveDate,
 | 
			
		||||
    planned_events: Vec<PlannedEventWithUser>,
 | 
			
		||||
    trips: Vec<TripWithUser>,
 | 
			
		||||
    planned_events: Vec<PlannedEventWithUserAndTriptype>,
 | 
			
		||||
    trips: Vec<TripWithUserAndType>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Day {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
use chrono::NaiveDate;
 | 
			
		||||
use serde::Serialize;
 | 
			
		||||
use sqlx::SqlitePool;
 | 
			
		||||
use sqlx::{FromRow, SqlitePool};
 | 
			
		||||
 | 
			
		||||
use super::tripdetails::TripDetails;
 | 
			
		||||
use super::{tripdetails::TripDetails, triptype::TripType};
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Clone)]
 | 
			
		||||
#[derive(Serialize, Clone, FromRow)]
 | 
			
		||||
pub struct PlannedEvent {
 | 
			
		||||
    pub id: i64,
 | 
			
		||||
    name: String,
 | 
			
		||||
@@ -15,12 +15,14 @@ pub struct PlannedEvent {
 | 
			
		||||
    max_people: i64,
 | 
			
		||||
    day: String,
 | 
			
		||||
    notes: Option<String>,
 | 
			
		||||
    trip_type_id: Option<i64>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize)]
 | 
			
		||||
pub struct PlannedEventWithUser {
 | 
			
		||||
pub struct PlannedEventWithUserAndTriptype {
 | 
			
		||||
    #[serde(flatten)]
 | 
			
		||||
    planned_event: PlannedEvent,
 | 
			
		||||
    trip_type: Option<TripType>,
 | 
			
		||||
    cox_needed: bool,
 | 
			
		||||
    cox: Vec<Registration>,
 | 
			
		||||
    rower: Vec<Registration>,
 | 
			
		||||
@@ -39,7 +41,8 @@ impl PlannedEvent {
 | 
			
		||||
        sqlx::query_as!(
 | 
			
		||||
            Self,
 | 
			
		||||
            "
 | 
			
		||||
SELECT planned_event.id, name, planned_amount_cox, allow_guests, trip_details_id, planned_starting_time, max_people, day, notes 
 | 
			
		||||
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
 | 
			
		||||
FROM planned_event 
 | 
			
		||||
INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id
 | 
			
		||||
WHERE planned_event.id like ?
 | 
			
		||||
@@ -51,11 +54,14 @@ WHERE planned_event.id like ?
 | 
			
		||||
        .ok()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn get_for_day(db: &SqlitePool, day: NaiveDate) -> Vec<PlannedEventWithUser> {
 | 
			
		||||
    pub async fn get_for_day(
 | 
			
		||||
        db: &SqlitePool,
 | 
			
		||||
        day: NaiveDate,
 | 
			
		||||
    ) -> Vec<PlannedEventWithUserAndTriptype> {
 | 
			
		||||
        let day = format!("{day}");
 | 
			
		||||
        let events = sqlx::query_as!(
 | 
			
		||||
            PlannedEvent,
 | 
			
		||||
            "SELECT planned_event.id, name, planned_amount_cox, allow_guests, trip_details_id, planned_starting_time, max_people, day, notes
 | 
			
		||||
            "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
 | 
			
		||||
FROM planned_event
 | 
			
		||||
INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id
 | 
			
		||||
WHERE day=?",
 | 
			
		||||
@@ -68,11 +74,16 @@ WHERE day=?",
 | 
			
		||||
        let mut ret = Vec::new();
 | 
			
		||||
        for event in events {
 | 
			
		||||
            let cox = event.get_all_cox(db).await;
 | 
			
		||||
            ret.push(PlannedEventWithUser {
 | 
			
		||||
            let mut trip_type = None;
 | 
			
		||||
            if let Some(trip_type_id) = event.trip_type_id {
 | 
			
		||||
                trip_type = TripType::find_by_id(db, trip_type_id).await;
 | 
			
		||||
            }
 | 
			
		||||
            ret.push(PlannedEventWithUserAndTriptype {
 | 
			
		||||
                planned_event: event.clone(),
 | 
			
		||||
                cox_needed: event.planned_amount_cox > cox.len() as i64,
 | 
			
		||||
                cox,
 | 
			
		||||
                rower: event.get_all_rower(db).await,
 | 
			
		||||
                trip_type,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        ret
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ use sqlx::SqlitePool;
 | 
			
		||||
use super::{
 | 
			
		||||
    planned_event::{PlannedEvent, Registration},
 | 
			
		||||
    tripdetails::TripDetails,
 | 
			
		||||
    triptype::TripType,
 | 
			
		||||
    user::CoxUser,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@@ -18,13 +19,15 @@ pub struct Trip {
 | 
			
		||||
    max_people: i64,
 | 
			
		||||
    day: String,
 | 
			
		||||
    notes: Option<String>,
 | 
			
		||||
    trip_type_id: Option<i64>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize)]
 | 
			
		||||
pub struct TripWithUser {
 | 
			
		||||
pub struct TripWithUserAndType {
 | 
			
		||||
    #[serde(flatten)]
 | 
			
		||||
    trip: Trip,
 | 
			
		||||
    rower: Vec<Registration>,
 | 
			
		||||
    trip_type: Option<TripType>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Trip {
 | 
			
		||||
@@ -44,7 +47,7 @@ impl Trip {
 | 
			
		||||
        sqlx::query_as!(
 | 
			
		||||
            Self,
 | 
			
		||||
            "
 | 
			
		||||
SELECT trip.id, cox_id, user.name as cox_name, trip_details_id, planned_starting_time, max_people, day, notes
 | 
			
		||||
SELECT trip.id, cox_id, user.name as cox_name, trip_details_id, planned_starting_time, max_people, day, notes, trip_type_id
 | 
			
		||||
FROM trip 
 | 
			
		||||
INNER JOIN trip_details ON trip.trip_details_id = trip_details.id
 | 
			
		||||
INNER JOIN user ON trip.cox_id = user.id
 | 
			
		||||
@@ -92,12 +95,12 @@ WHERE trip.id=?
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn get_for_day(db: &SqlitePool, day: NaiveDate) -> Vec<TripWithUser> {
 | 
			
		||||
    pub async fn get_for_day(db: &SqlitePool, day: NaiveDate) -> Vec<TripWithUserAndType> {
 | 
			
		||||
        let day = format!("{day}");
 | 
			
		||||
        let trips = sqlx::query_as!(
 | 
			
		||||
            Trip,
 | 
			
		||||
            "
 | 
			
		||||
SELECT trip.id, cox_id, user.name as cox_name, trip_details_id, planned_starting_time, max_people, day, notes
 | 
			
		||||
SELECT trip.id, cox_id, user.name as cox_name, trip_details_id, planned_starting_time, max_people, day, notes, trip_type_id
 | 
			
		||||
FROM trip 
 | 
			
		||||
INNER JOIN trip_details ON trip.trip_details_id = trip_details.id
 | 
			
		||||
INNER JOIN user ON trip.cox_id = user.id
 | 
			
		||||
@@ -110,8 +113,13 @@ WHERE day=?
 | 
			
		||||
        .unwrap(); //TODO: fixme
 | 
			
		||||
        let mut ret = Vec::new();
 | 
			
		||||
        for trip in trips {
 | 
			
		||||
            ret.push(TripWithUser {
 | 
			
		||||
            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;
 | 
			
		||||
            }
 | 
			
		||||
            ret.push(TripWithUserAndType {
 | 
			
		||||
                trip: trip.clone(),
 | 
			
		||||
                trip_type,
 | 
			
		||||
                rower: trip.get_all_rower(db).await,
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ pub struct TripDetails {
 | 
			
		||||
    max_people: i64,
 | 
			
		||||
    day: String,
 | 
			
		||||
    notes: Option<String>,
 | 
			
		||||
    trip_type_id: Option<i64>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TripDetails {
 | 
			
		||||
@@ -15,7 +16,7 @@ impl TripDetails {
 | 
			
		||||
        sqlx::query_as!(
 | 
			
		||||
            TripDetails,
 | 
			
		||||
            "
 | 
			
		||||
SELECT id, planned_starting_time, max_people, day, notes 
 | 
			
		||||
SELECT id, planned_starting_time, max_people, day, notes, trip_type_id
 | 
			
		||||
FROM trip_details 
 | 
			
		||||
WHERE id like ?
 | 
			
		||||
        ",
 | 
			
		||||
@@ -33,13 +34,15 @@ WHERE id like ?
 | 
			
		||||
        max_people: i32,
 | 
			
		||||
        day: String,
 | 
			
		||||
        notes: Option<String>,
 | 
			
		||||
        trip_type_id: Option<i64>,
 | 
			
		||||
    ) -> i64 {
 | 
			
		||||
        let query = sqlx::query!(
 | 
			
		||||
            "INSERT INTO trip_details(planned_starting_time, max_people, day, notes) VALUES(?, ?, ?, ?)" ,
 | 
			
		||||
            "INSERT INTO trip_details(planned_starting_time, max_people, day, notes, trip_type_id) VALUES(?, ?, ?, ?, ?)" ,
 | 
			
		||||
            planned_starting_time,
 | 
			
		||||
            max_people,
 | 
			
		||||
            day,
 | 
			
		||||
            notes
 | 
			
		||||
            notes,
 | 
			
		||||
            trip_type_id
 | 
			
		||||
        )
 | 
			
		||||
        .execute(db)
 | 
			
		||||
        .await
 | 
			
		||||
@@ -94,11 +97,11 @@ mod test {
 | 
			
		||||
        let pool = testdb!();
 | 
			
		||||
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            TripDetails::create(&pool, "10:00".into(), 2, "1970-01-01".into(), None).await,
 | 
			
		||||
            TripDetails::create(&pool, "10:00".into(), 2, "1970-01-01".into(), None, None).await,
 | 
			
		||||
            3,
 | 
			
		||||
        );
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            TripDetails::create(&pool, "10:00".into(), 2, "1970-01-01".into(), None).await,
 | 
			
		||||
            TripDetails::create(&pool, "10:00".into(), 2, "1970-01-01".into(), None, None).await,
 | 
			
		||||
            4,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
@@ -115,4 +118,6 @@ mod test {
 | 
			
		||||
    fn test_true_full() {
 | 
			
		||||
        //TODO: register user for trip_details = 1; check if is_full returns true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //TODO: add new tripdetails test with trip_type != None
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										55
									
								
								src/model/triptype.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/model/triptype.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use sqlx::{FromRow, SqlitePool};
 | 
			
		||||
 | 
			
		||||
#[derive(FromRow, Debug, Serialize, Deserialize, Clone)]
 | 
			
		||||
pub struct TripType {
 | 
			
		||||
    pub id: i64,
 | 
			
		||||
    name: String,
 | 
			
		||||
    desc: String,
 | 
			
		||||
    question: String,
 | 
			
		||||
    icon: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TripType {
 | 
			
		||||
    pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> {
 | 
			
		||||
        sqlx::query_as!(
 | 
			
		||||
            Self,
 | 
			
		||||
            "
 | 
			
		||||
SELECT id, name, desc, question, icon 
 | 
			
		||||
FROM trip_type
 | 
			
		||||
WHERE id like ?
 | 
			
		||||
        ",
 | 
			
		||||
            id
 | 
			
		||||
        )
 | 
			
		||||
        .fetch_one(db)
 | 
			
		||||
        .await
 | 
			
		||||
        .ok()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn all(db: &SqlitePool) -> Vec<Self> {
 | 
			
		||||
        sqlx::query_as!(
 | 
			
		||||
            Self,
 | 
			
		||||
            "
 | 
			
		||||
SELECT id, name, desc, question, icon
 | 
			
		||||
FROM trip_type
 | 
			
		||||
        "
 | 
			
		||||
        )
 | 
			
		||||
        .fetch_all(db)
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap() //TODO: fixme
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use crate::testdb;
 | 
			
		||||
 | 
			
		||||
    use sqlx::SqlitePool;
 | 
			
		||||
 | 
			
		||||
    #[sqlx::test]
 | 
			
		||||
    fn test_find_true() {
 | 
			
		||||
        let pool = testdb!();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //TODO: write tests
 | 
			
		||||
}
 | 
			
		||||
@@ -16,7 +16,7 @@ pub struct User {
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    pw: Option<String>,
 | 
			
		||||
    pub is_cox: bool,
 | 
			
		||||
    is_admin: bool,
 | 
			
		||||
    pub is_admin: bool,
 | 
			
		||||
    is_guest: bool,
 | 
			
		||||
    #[serde(default = "bool::default")]
 | 
			
		||||
    deleted: bool,
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,7 @@ struct AddPlannedEventForm {
 | 
			
		||||
    planned_starting_time: String,
 | 
			
		||||
    max_people: i32,
 | 
			
		||||
    notes: Option<String>,
 | 
			
		||||
    trip_type: Option<i64>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[post("/planned-event", data = "<data>")]
 | 
			
		||||
@@ -33,6 +34,7 @@ async fn create(
 | 
			
		||||
        data.max_people,
 | 
			
		||||
        data.day.clone(),
 | 
			
		||||
        data.notes.clone(),
 | 
			
		||||
        data.trip_type,
 | 
			
		||||
    )
 | 
			
		||||
    .await;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@ struct AddTripForm {
 | 
			
		||||
    planned_starting_time: String,
 | 
			
		||||
    max_people: i32,
 | 
			
		||||
    notes: Option<String>,
 | 
			
		||||
    trip_type: Option<i64>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[post("/trip", data = "<data>")]
 | 
			
		||||
@@ -32,6 +33,7 @@ async fn create(db: &State<SqlitePool>, data: Form<AddTripForm>, cox: CoxUser) -
 | 
			
		||||
        data.max_people,
 | 
			
		||||
        data.day.clone(),
 | 
			
		||||
        data.notes.clone(),
 | 
			
		||||
        data.trip_type,
 | 
			
		||||
    )
 | 
			
		||||
    .await;
 | 
			
		||||
    let trip_details = TripDetails::find_by_id(db, trip_details_id).await.unwrap(); //Okay, bc just
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ use sqlx::SqlitePool;
 | 
			
		||||
use crate::model::{
 | 
			
		||||
    log::Log,
 | 
			
		||||
    tripdetails::TripDetails,
 | 
			
		||||
    triptype::TripType,
 | 
			
		||||
    user::User,
 | 
			
		||||
    usertrip::{UserTrip, UserTripError},
 | 
			
		||||
    Day,
 | 
			
		||||
@@ -26,6 +27,8 @@ mod cox;
 | 
			
		||||
async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_>>) -> Template {
 | 
			
		||||
    let mut days = Vec::new();
 | 
			
		||||
 | 
			
		||||
    let mut context = Context::new();
 | 
			
		||||
 | 
			
		||||
    let mut show_next_n_days = 6;
 | 
			
		||||
    if user.is_cox {
 | 
			
		||||
        let end_of_year = NaiveDate::from_ymd_opt(Local::now().year(), 12, 31).unwrap();
 | 
			
		||||
@@ -34,14 +37,16 @@ async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_
 | 
			
		||||
            .num_days()
 | 
			
		||||
            + 1;
 | 
			
		||||
    }
 | 
			
		||||
    if user.is_cox || user.is_admin {
 | 
			
		||||
        let triptypes = TripType::all(db).await;
 | 
			
		||||
        context.insert("trip_types", &triptypes);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for i in 0..show_next_n_days {
 | 
			
		||||
        let date = (Local::now() + Duration::days(i)).date_naive();
 | 
			
		||||
        days.push(Day::new(db, date).await);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let mut context = Context::new();
 | 
			
		||||
 | 
			
		||||
    if let Some(msg) = flash {
 | 
			
		||||
        context.insert("flash", &msg.into_inner());
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user