use chrono::NaiveDate; use serde::Serialize; use sqlx::{FromRow, SqlitePool}; use super::{tripdetails::TripDetails, triptype::TripType, user::User}; #[derive(Serialize, Clone, FromRow)] pub struct PlannedEvent { pub id: i64, pub name: String, planned_amount_cox: i64, trip_details_id: i64, pub planned_starting_time: String, max_people: i64, pub day: String, notes: Option, pub allow_guests: bool, trip_type_id: Option, } #[derive(Serialize)] pub struct PlannedEventWithUserAndTriptype { #[serde(flatten)] pub planned_event: PlannedEvent, trip_type: Option, cox_needed: bool, cox: Vec, rower: Vec, } //TODO: move to appropriate place #[derive(Serialize)] pub struct Registration { pub name: String, pub registered_at: String, pub is_guest: bool, } impl PlannedEvent { pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option { sqlx::query_as!( Self, " 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 INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id WHERE planned_event.id like ? ", id ) .fetch_one(db) .await .ok() } pub async fn get_for_day( db: &SqlitePool, day: NaiveDate, ) -> Vec { let day = format!("{day}"); let events = sqlx::query_as!( PlannedEvent, "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 INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id WHERE day=?", day ) .fetch_all(db) .await .unwrap(); //TODO: fixme let mut ret = Vec::new(); for event in events { let cox = event.get_all_cox(db).await; 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 { cox_needed: event.planned_amount_cox > cox.len() as i64, cox, rower: event.get_all_rower(db).await, planned_event: event, trip_type, }); } ret } pub async fn all(db: &SqlitePool) -> Vec { sqlx::query_as!( PlannedEvent, "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 INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id", ) .fetch_all(db) .await .unwrap() //TODO: fixme } async fn get_all_cox(&self, db: &SqlitePool) -> Vec { //TODO: switch to join sqlx::query_as!( Registration, " SELECT (SELECT name FROM user WHERE cox_id = id) as name, (SELECT created_at FROM user WHERE cox_id = id) as registered_at, (SELECT is_guest FROM user WHERE cox_id = id) as is_guest FROM trip WHERE planned_event_id = ? ", self.id ) .fetch_all(db) .await .unwrap() //Okay, as PlannedEvent can only be created with proper DB backing } async fn get_all_rower(&self, db: &SqlitePool) -> Vec { //TODO: switch to join sqlx::query_as!( Registration, " SELECT (SELECT name FROM user WHERE user_trip.user_id = user.id) as name, (SELECT created_at FROM user WHERE user_trip.user_id = user.id) as registered_at, (SELECT is_guest FROM user WHERE user_trip.user_id = user.id) as is_guest FROM user_trip WHERE trip_details_id = (SELECT trip_details_id FROM planned_event WHERE id = ?) ", self.id ) .fetch_all(db) .await .unwrap() //Okay, as PlannedEvent can only be created with proper DB backing } //TODO: add tests pub async fn is_rower_registered(&self, db: &SqlitePool, user: &User) -> bool { let is_rower = sqlx::query!( "SELECT count(*) as amount FROM user_trip WHERE trip_details_id = (SELECT trip_details_id FROM planned_event WHERE id = ?) AND user_id = ?", self.id, user.id ) .fetch_one(db) .await .unwrap(); //Okay, bc planned_event can only be created with proper DB backing is_rower.amount > 0 } pub async fn create( db: &SqlitePool, name: &str, planned_amount_cox: i32, trip_details: TripDetails, ) { sqlx::query!( "INSERT INTO planned_event(name, planned_amount_cox, trip_details_id) VALUES(?, ?, ?)", name, planned_amount_cox, trip_details.id ) .execute(db) .await .unwrap(); //Okay, as TripDetails can only be created with proper DB backing } //TODO: create unit test pub async fn update( &self, db: &SqlitePool, planned_amount_cox: i32, max_people: i32, notes: Option<&str>, ) { sqlx::query!( "UPDATE planned_event SET planned_amount_cox = ? WHERE id = ?", planned_amount_cox, self.id ) .execute(db) .await .unwrap(); //Okay, as planned_event can only be created with proper DB backing sqlx::query!( "UPDATE trip_details SET max_people = ?, notes = ? WHERE id = ?", max_people, notes, self.trip_details_id ) .execute(db) .await .unwrap(); //Okay, as planned_event can only be created with proper DB backing } pub async fn delete(&self, db: &SqlitePool) { sqlx::query!("DELETE FROM planned_event WHERE id = ?", self.id) .execute(db) .await .unwrap(); //Okay, as PlannedEvent can only be created with proper DB backing } } #[cfg(test)] mod test { use crate::{model::tripdetails::TripDetails, testdb}; use super::PlannedEvent; use chrono::NaiveDate; use sqlx::SqlitePool; #[sqlx::test] fn test_get_day() { let pool = testdb!(); let res = PlannedEvent::get_for_day(&pool, NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()).await; assert_eq!(res.len(), 1); } #[sqlx::test] fn test_create() { let pool = testdb!(); let trip_details = TripDetails::find_by_id(&pool, 1).await.unwrap(); PlannedEvent::create(&pool, "new-event".into(), 2, trip_details).await; let res = PlannedEvent::get_for_day(&pool, NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()).await; assert_eq!(res.len(), 2); } #[sqlx::test] fn test_delete() { let pool = testdb!(); let planned_event = PlannedEvent::find_by_id(&pool, 1).await.unwrap(); planned_event.delete(&pool).await; let res = PlannedEvent::get_for_day(&pool, NaiveDate::from_ymd_opt(1970, 1, 1).unwrap()).await; assert_eq!(res.len(), 0); } }