Add an accommodations reservations module #188
13
geneit_backend/Cargo.lock
generated
13
geneit_backend/Cargo.lock
generated
@ -727,8 +727,10 @@ checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"android-tzdata",
|
"android-tzdata",
|
||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
|
"js-sys",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"serde",
|
"serde",
|
||||||
|
"wasm-bindgen",
|
||||||
"windows-targets 0.52.5",
|
"windows-targets 0.52.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1417,12 +1419,14 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bcrypt",
|
"bcrypt",
|
||||||
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"diesel",
|
"diesel",
|
||||||
"diesel_migrations",
|
"diesel_migrations",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"httpdate",
|
"httpdate",
|
||||||
|
"ical",
|
||||||
"image",
|
"image",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"lettre",
|
"lettre",
|
||||||
@ -1776,6 +1780,15 @@ dependencies = [
|
|||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ical"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b7cab7543a8b7729a19e2c04309f902861293dcdae6558dfbeb634454d279f6"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ident_case"
|
name = "ident_case"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -38,3 +38,5 @@ zip = "2.0.0"
|
|||||||
mime_guess = "2.0.4"
|
mime_guess = "2.0.4"
|
||||||
tempfile = "3.10.1"
|
tempfile = "3.10.1"
|
||||||
base64 = "0.22.0"
|
base64 = "0.22.0"
|
||||||
|
ical = { version = "0.11.0", features = ["generator", "ical", "vcard"] }
|
||||||
|
chrono = "0.4.38"
|
@ -1,10 +1,17 @@
|
|||||||
|
use ical::{generator::*, *};
|
||||||
|
|
||||||
|
use actix_web::{web, HttpResponse};
|
||||||
|
use chrono::DateTime;
|
||||||
|
|
||||||
use crate::constants::StaticConstraints;
|
use crate::constants::StaticConstraints;
|
||||||
use crate::controllers::HttpResult;
|
use crate::controllers::HttpResult;
|
||||||
use crate::extractors::accommodation_reservation_calendar_extractor::FamilyAndAccommodationReservationCalendarInPath;
|
use crate::extractors::accommodation_reservation_calendar_extractor::FamilyAndAccommodationReservationCalendarInPath;
|
||||||
use crate::extractors::family_extractor::FamilyInPath;
|
use crate::extractors::family_extractor::FamilyInPath;
|
||||||
use crate::models::AccommodationID;
|
use crate::models::{AccommodationID, ReservationStatus};
|
||||||
use crate::services::{accommodations_list_service, accommodations_reservations_calendars_service};
|
use crate::services::{
|
||||||
use actix_web::{web, HttpResponse};
|
accommodations_list_service, accommodations_reservations_calendars_service,
|
||||||
|
accommodations_reservations_service, families_service,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
pub struct CreateCalendarQuery {
|
pub struct CreateCalendarQuery {
|
||||||
@ -65,3 +72,86 @@ pub async fn delete(resa: FamilyAndAccommodationReservationCalendarInPath) -> Ht
|
|||||||
accommodations_reservations_calendars_service::delete(resa.to_reservation()).await?;
|
accommodations_reservations_calendars_service::delete(resa.to_reservation()).await?;
|
||||||
Ok(HttpResponse::Ok().json("Calendar successfully deleted"))
|
Ok(HttpResponse::Ok().json("Calendar successfully deleted"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fmt_date(time: i64) -> String {
|
||||||
|
let res = DateTime::from_timestamp(time, 0).expect("Failed to parse date");
|
||||||
|
|
||||||
|
/*format!(
|
||||||
|
"{:0>4}{:0>2}{:0>2}T{:0>2}{:0>2}",
|
||||||
|
res.year(),
|
||||||
|
res.month(),
|
||||||
|
res.day(),
|
||||||
|
res.minute(),
|
||||||
|
res.second()
|
||||||
|
)*/
|
||||||
|
|
||||||
|
res.format("%Y%m%dT%H%M%S").to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct AnonymousAccessURL {
|
||||||
|
token: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the content of the calendar
|
||||||
|
pub async fn anonymous_access(req: web::Path<AnonymousAccessURL>) -> HttpResult {
|
||||||
|
let calendar =
|
||||||
|
match accommodations_reservations_calendars_service::get_by_token(&req.token).await {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Calendar information could not be retrieved: {e}");
|
||||||
|
return Ok(HttpResponse::NotFound().body("Calendar not found!"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let accommodations =
|
||||||
|
accommodations_list_service::get_all_of_family(calendar.family_id()).await?;
|
||||||
|
let members = families_service::get_memberships_of_family(calendar.family_id()).await?;
|
||||||
|
|
||||||
|
// Get calendar associated events
|
||||||
|
let events = match calendar.accommodation_id() {
|
||||||
|
None => {
|
||||||
|
accommodations_reservations_service::get_all_of_family(calendar.family_id()).await?
|
||||||
|
}
|
||||||
|
Some(a) => accommodations_reservations_service::get_all_of_accommodation(a).await?,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut cal = IcalCalendarBuilder::version("2.0")
|
||||||
|
.gregorian()
|
||||||
|
.prodid("-//geneit//")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
for ev in events {
|
||||||
|
let accommodation = accommodations
|
||||||
|
.iter()
|
||||||
|
.find(|a| a.id() == ev.accommodation_id())
|
||||||
|
.unwrap();
|
||||||
|
let member_name = members
|
||||||
|
.iter()
|
||||||
|
.find(|a| a.membership.user_id() == ev.user_id())
|
||||||
|
.map(|m| m.user_name.as_str())
|
||||||
|
.unwrap_or("other user");
|
||||||
|
|
||||||
|
let event = IcalEventBuilder::tzid("Europe/Paris")
|
||||||
|
.uid(format!("resa-{}", ev.id().0))
|
||||||
|
.changed(fmt_date(ev.time_update))
|
||||||
|
.start(fmt_date(ev.reservation_start))
|
||||||
|
.end(fmt_date(ev.reservation_end))
|
||||||
|
.set(ical_property!("SUMMARY", member_name))
|
||||||
|
.set(ical_property!("LOCATION", &accommodation.name))
|
||||||
|
.set(ical_property!(
|
||||||
|
"STATUS",
|
||||||
|
match ev.status() {
|
||||||
|
ReservationStatus::Pending => "TENTATIVE",
|
||||||
|
ReservationStatus::Accepted => "CONFIRMED",
|
||||||
|
ReservationStatus::Rejected => "CANCELLED",
|
||||||
|
}
|
||||||
|
))
|
||||||
|
.build();
|
||||||
|
cal.events.push(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok()
|
||||||
|
.content_type("text/calendar")
|
||||||
|
.body(cal.generate()))
|
||||||
|
}
|
||||||
|
@ -270,7 +270,10 @@ async fn main() -> std::io::Result<()> {
|
|||||||
"/family/{id}/accommodations/reservations_calendars/{cal_id}",
|
"/family/{id}/accommodations/reservations_calendars/{cal_id}",
|
||||||
web::delete().to(accommodations_reservations_calendars_controller::delete),
|
web::delete().to(accommodations_reservations_calendars_controller::delete),
|
||||||
)
|
)
|
||||||
// TODO : anonymous URL access
|
.route(
|
||||||
|
"/acccommodations_calendar/{token}",
|
||||||
|
web::get().to(accommodations_reservations_calendars_controller::anonymous_access),
|
||||||
|
)
|
||||||
// Photos controller
|
// Photos controller
|
||||||
.route(
|
.route(
|
||||||
"/photo/{id}",
|
"/photo/{id}",
|
||||||
|
@ -485,6 +485,12 @@ pub struct NewAccommodation {
|
|||||||
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash)]
|
||||||
pub struct AccommodationReservationID(pub i32);
|
pub struct AccommodationReservationID(pub i32);
|
||||||
|
|
||||||
|
pub enum ReservationStatus {
|
||||||
|
Pending,
|
||||||
|
Accepted,
|
||||||
|
Rejected,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Queryable, Debug, serde::Serialize)]
|
#[derive(Queryable, Debug, serde::Serialize)]
|
||||||
pub struct AccommodationReservation {
|
pub struct AccommodationReservation {
|
||||||
id: i32,
|
id: i32,
|
||||||
@ -514,6 +520,14 @@ impl AccommodationReservation {
|
|||||||
pub fn user_id(&self) -> UserID {
|
pub fn user_id(&self) -> UserID {
|
||||||
UserID(self.user_id)
|
UserID(self.user_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn status(&self) -> ReservationStatus {
|
||||||
|
match self.validated {
|
||||||
|
None => ReservationStatus::Pending,
|
||||||
|
Some(true) => ReservationStatus::Accepted,
|
||||||
|
Some(false) => ReservationStatus::Rejected,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Insertable)]
|
#[derive(Insertable)]
|
||||||
|
@ -61,6 +61,15 @@ pub async fn get_by_id(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a single calendar by its token
|
||||||
|
pub async fn get_by_token(token: &str) -> anyhow::Result<AccommodationReservationCalendar> {
|
||||||
|
db_connection::execute(|conn| {
|
||||||
|
accommodations_reservations_cals_urls::table
|
||||||
|
.filter(accommodations_reservations_cals_urls::dsl::token.eq(token))
|
||||||
|
.get_result(conn)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Delete a calendar
|
/// Delete a calendar
|
||||||
pub async fn delete(r: AccommodationReservationCalendar) -> anyhow::Result<()> {
|
pub async fn delete(r: AccommodationReservationCalendar) -> anyhow::Result<()> {
|
||||||
db_connection::execute(|conn| {
|
db_connection::execute(|conn| {
|
||||||
|
@ -129,9 +129,9 @@ pub async fn update_membership(membership: &Membership) -> anyhow::Result<()> {
|
|||||||
#[derive(serde::Serialize)]
|
#[derive(serde::Serialize)]
|
||||||
pub struct FamilyMember {
|
pub struct FamilyMember {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
membership: Membership,
|
pub membership: Membership,
|
||||||
user_name: String,
|
pub user_name: String,
|
||||||
user_mail: String,
|
pub user_mail: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get information about the users of a family
|
/// Get information about the users of a family
|
||||||
|
Loading…
Reference in New Issue
Block a user