use chrono::{NaiveDate, NaiveTime}; use sqlx::{query, PgPool}; use super::{Area, AvailabilityChangeset, Function, Result, Role, User}; #[derive(Clone, Debug)] pub struct Availability { pub id: i32, pub user_id: i32, pub user: Option, pub date: NaiveDate, pub time: AvailabilityTime, pub comment: Option, } #[derive(Clone, Debug, PartialEq)] pub enum AvailabilityTime { WholeDay, Temporarily(NaiveTime, NaiveTime), } impl Availability { // TODO: fix db name pub async fn create( pool: &PgPool, user_id: i32, date: NaiveDate, changeset: AvailabilityChangeset, ) -> Result<()> { let (start, end) = if let AvailabilityTime::Temporarily(s, e) = changeset.time { (Some(s), Some(e)) } else { (None, None) }; query!( r#" INSERT INTO availabillity (userId, date, startTime, endTime, comment) VALUES ($1, $2, $3, $4, $5); "#, user_id, date, start, end, changeset.comment ) .execute(pool) .await?; Ok(()) } pub async fn read_by_date_and_area_including_user( pool: &PgPool, date: NaiveDate, area_id: i32, ) -> Result> { let records = query!( r##" SELECT availabillity.id, availabillity.userId, availabillity.date, availabillity.startTime, availabillity.endTime, availabillity.comment, user_.name, user_.email, user_.password, user_.salt, user_.role AS "role: Role", user_.function AS "function: Function", user_.areaId, user_.locked, user_.lastLogin, user_.receiveNotifications FROM availabillity JOIN user_ ON availabillity.userId = user_.id WHERE availabillity.date = $1 AND user_.areaId = $2; "##, date, area_id ) .fetch_all(pool) .await?; let availabillities = records .iter() .map(|r| Availability { id: r.id, user_id: r.userid, user: Some(User { id: r.userid, name: r.name.clone(), email: r.email.clone(), password: r.password.clone(), salt: r.salt.clone(), role: r.role.clone(), function: r.function.clone(), area_id: r.areaid, area: None, locked: r.locked, last_login: r.lastlogin, receive_notifications: r.receivenotifications, }), date: r.date, time: match (r.starttime, r.endtime) { (Some(start), Some(end)) => AvailabilityTime::Temporarily(start, end), (_, _) => AvailabilityTime::WholeDay, }, comment: r.comment.clone(), }) .collect(); Ok(availabillities) } pub async fn read_by_id_including_user(pool: &PgPool, id: i32) -> Result> { let record = query!( r##" SELECT availabillity.id, availabillity.userId, availabillity.date, availabillity.startTime, availabillity.endTime, availabillity.comment, user_.name, user_.email, user_.password, user_.salt, user_.role AS "role: Role", user_.function AS "function: Function", user_.areaId, user_.locked, user_.lastLogin, user_.receiveNotifications FROM availabillity LEFT JOIN assignment ON availabillity.Id = assignment.availabillityId JOIN user_ ON availabillity.userId = user_.id WHERE availabillity.id = $1; "##, id ) .fetch_optional(pool) .await?; let availabillity = record.and_then(|r| { Some(Availability { id: r.id, user_id: r.userid, user: Some(User { id: r.userid, name: r.name.clone(), email: r.email.clone(), password: r.password.clone(), salt: r.salt.clone(), role: r.role.clone(), function: r.function.clone(), area_id: r.areaid, area: None, locked: r.locked, last_login: r.lastlogin, receive_notifications: r.receivenotifications, }), date: r.date, time: match (r.starttime, r.endtime) { (Some(start), Some(end)) => AvailabilityTime::Temporarily(start, end), (_, _) => AvailabilityTime::WholeDay, }, comment: r.comment.clone(), }) }); Ok(availabillity) } pub async fn read_by_id(pool: &PgPool, id: i32) -> Result> { let record = query!("SELECT * FROM availabillity WHERE id = $1", id) .fetch_optional(pool) .await?; let availabillity = record.and_then(|record| { Some(Availability { id: record.id, user_id: record.userid, user: None, date: record.date, time: match (record.starttime, record.endtime) { (Some(start), Some(end)) => AvailabilityTime::Temporarily(start, end), (_, _) => AvailabilityTime::WholeDay, }, comment: record.comment.clone(), }) }); Ok(availabillity) } pub async fn read_for_export( pool: &PgPool, date_range: (NaiveDate, NaiveDate), area_id: i32, ) -> Result> { let records = query!( r##" SELECT availabillity.id, availabillity.userId, availabillity.date, availabillity.startTime, availabillity.endTime, availabillity.comment, user_.name, user_.email, user_.password, user_.salt, user_.role AS "role: Role", user_.function AS "function: Function", user_.areaId, user_.locked, user_.lastLogin, user_.receiveNotifications, area.name AS areaName FROM availabillity JOIN user_ ON availabillity.userId = user_.id JOIN area ON user_.areaId = area.id WHERE user_.areaId = $1 AND availabillity.date >= $2 AND availabillity.date <= $3; "##, area_id, date_range.0, date_range.1 ) .fetch_all(pool) .await?; let availabillities = records .iter() .map(|r| Availability { id: r.id, user_id: r.userid, user: Some(User { id: r.userid, name: r.name.clone(), email: r.email.clone(), password: r.password.clone(), salt: r.salt.clone(), role: r.role.clone(), function: r.function.clone(), area_id: r.areaid, area: Some(Area { id: r.areaid, name: r.areaname.clone(), }), locked: r.locked, last_login: r.lastlogin, receive_notifications: r.receivenotifications, }), date: r.date, time: match (r.starttime, r.endtime) { (Some(start), Some(end)) => AvailabilityTime::Temporarily(start, end), (_, _) => AvailabilityTime::WholeDay, }, comment: r.comment.clone(), }) .collect(); Ok(availabillities) } pub async fn read_by_user_and_date( pool: &PgPool, user_id: i32, date: &NaiveDate, ) -> Result> { let records = query!( r##" SELECT availabillity.id, availabillity.userId, availabillity.date, availabillity.startTime, availabillity.endTime, availabillity.comment FROM availabillity WHERE availabillity.userId = $1 AND availabillity.date = $2; "##, user_id, date ) .fetch_all(pool) .await?; let availabillities = records .iter() .map(|r| Availability { id: r.id, user_id: r.userid, user: None, date: r.date, time: match (r.starttime, r.endtime) { (Some(start), Some(end)) => AvailabilityTime::Temporarily(start, end), (_, _) => AvailabilityTime::WholeDay, }, comment: r.comment.clone(), }) .collect(); Ok(availabillities) } pub async fn update(pool: &PgPool, id: i32, changeset: AvailabilityChangeset) -> Result<()> { let (start, end) = if let AvailabilityTime::Temporarily(s, e) = changeset.time { (Some(s), Some(e)) } else { (None, None) }; query!( "UPDATE availabillity SET startTime = $1, endTime = $2, comment = $3 WHERE id = $4", start, end, changeset.comment, id ) .execute(pool) .await?; Ok(()) } pub async fn delete(pool: &PgPool, id: i32) -> Result<()> { query!("DELETE FROM availabillity WHERE id = $1", id) .execute(pool) .await?; Ok(()) } }