Merge pull request 'restructure' (#1050) from restructure into staging
Reviewed-on: #1050
This commit is contained in:
commit
29e9911653
@ -1,10 +1,7 @@
|
||||
use std::io::Write;
|
||||
|
||||
use chrono::{Duration, NaiveDate, NaiveTime};
|
||||
use ics::{
|
||||
ICalendar,
|
||||
properties::{DtEnd, DtStart, Summary},
|
||||
};
|
||||
use chrono::NaiveDate;
|
||||
use ics::ICalendar;
|
||||
use serde::Serialize;
|
||||
use sqlx::{FromRow, Row, SqlitePool};
|
||||
|
||||
@ -142,7 +139,7 @@ WHERE planned_event.id like ?
|
||||
.ok()
|
||||
}
|
||||
|
||||
async fn trip_type(&self, db: &SqlitePool) -> Option<TripType> {
|
||||
pub(crate) async fn trip_type(&self, db: &SqlitePool) -> Option<TripType> {
|
||||
if let Some(trip_type_id) = self.trip_type_id {
|
||||
TripType::find_by_id(db, trip_type_id).await
|
||||
} else {
|
||||
@ -474,66 +471,6 @@ WHERE trip_details.id=?
|
||||
String::from_utf8(buf).unwrap()
|
||||
}
|
||||
|
||||
pub(crate) async fn get_vevent(self, db: &SqlitePool) -> ics::Event {
|
||||
let mut vevent = ics::Event::new(
|
||||
format!("event-{}@rudernlinz.at", self.id),
|
||||
"19900101T180000",
|
||||
);
|
||||
let time_str = self.planned_starting_time.replace(':', "");
|
||||
let formatted_time = if time_str.len() == 3 {
|
||||
format!("0{}", time_str)
|
||||
} else {
|
||||
time_str.clone() // TODO: remove again
|
||||
};
|
||||
vevent.push(DtStart::new(format!(
|
||||
"{}T{}00",
|
||||
self.day.replace('-', ""),
|
||||
formatted_time
|
||||
)));
|
||||
|
||||
let original_time = NaiveTime::parse_from_str(&self.planned_starting_time, "%H:%M")
|
||||
.expect("Failed to parse time");
|
||||
|
||||
let long_trip = match self.trip_type(db).await {
|
||||
Some(a) if a.name == "Lange Ausfahrt" => true,
|
||||
_ => false,
|
||||
};
|
||||
let later_time = if long_trip {
|
||||
original_time + Duration::hours(6)
|
||||
} else {
|
||||
original_time + Duration::hours(3)
|
||||
};
|
||||
if later_time > original_time {
|
||||
// Check if no day-overflow
|
||||
let time_three_hours_later = later_time.format("%H%M").to_string();
|
||||
vevent.push(DtEnd::new(format!(
|
||||
"{}T{}00",
|
||||
self.day.replace('-', ""),
|
||||
time_three_hours_later
|
||||
)));
|
||||
}
|
||||
|
||||
let tripdetails = self.trip_details(db).await;
|
||||
let mut name = String::new();
|
||||
if self.is_cancelled() {
|
||||
name.push_str("ABGESAGT");
|
||||
if let Some(notes) = &tripdetails.notes {
|
||||
if !notes.is_empty() {
|
||||
name.push_str(&format!(" (Grund: {notes})"))
|
||||
}
|
||||
}
|
||||
|
||||
name.push_str("! :-( ");
|
||||
}
|
||||
name.push_str(&format!("{} ", self.name));
|
||||
|
||||
if let Some(triptype) = tripdetails.triptype(db).await {
|
||||
name.push_str(&format!("• {} ", triptype.name))
|
||||
}
|
||||
vevent.push(Summary::new(name));
|
||||
vevent
|
||||
}
|
||||
|
||||
pub async fn trip_details(&self, db: &SqlitePool) -> TripDetails {
|
||||
TripDetails::find_by_id(db, self.trip_details_id)
|
||||
.await
|
||||
|
@ -1,9 +1,14 @@
|
||||
use std::io::Write;
|
||||
|
||||
use ics::{ICalendar, components::Property};
|
||||
use ics::{
|
||||
components::Property,
|
||||
properties::{DtEnd, DtStart, Summary},
|
||||
ICalendar,
|
||||
};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
use crate::model::{event::Event, trip::Trip, user::User};
|
||||
use chrono::{Duration, NaiveTime};
|
||||
|
||||
pub(crate) async fn get_personal_cal(db: &SqlitePool, user: &User) -> String {
|
||||
let mut calendar = ICalendar::new("2.0", "ics-rs");
|
||||
@ -25,3 +30,125 @@ pub(crate) async fn get_personal_cal(db: &SqlitePool, user: &User) -> String {
|
||||
write!(&mut buf, "{}", calendar).unwrap();
|
||||
String::from_utf8(buf).unwrap()
|
||||
}
|
||||
|
||||
impl Trip {
|
||||
pub(crate) async fn get_vevent<'a>(self, db: &'a SqlitePool, user: &'a User) -> ics::Event<'a> {
|
||||
let mut vevent =
|
||||
ics::Event::new(format!("trip-{}@rudernlinz.at", self.id), "19900101T180000");
|
||||
let time_str = self.planned_starting_time.replace(':', "");
|
||||
let formatted_time = if time_str.len() == 3 {
|
||||
format!("0{}", time_str)
|
||||
} else {
|
||||
time_str
|
||||
};
|
||||
|
||||
vevent.push(DtStart::new(format!(
|
||||
"{}T{}00",
|
||||
self.day.replace('-', ""),
|
||||
formatted_time
|
||||
)));
|
||||
|
||||
let original_time = NaiveTime::parse_from_str(&self.planned_starting_time, "%H:%M")
|
||||
.expect("Failed to parse time");
|
||||
let long_trip = match self.trip_type(db).await {
|
||||
Some(a) if a.name == "Lange Ausfahrt" => true,
|
||||
_ => false,
|
||||
};
|
||||
let later_time = if long_trip {
|
||||
original_time + Duration::hours(6)
|
||||
} else {
|
||||
original_time + Duration::hours(3)
|
||||
};
|
||||
if later_time > original_time {
|
||||
// Check if no day-overflow
|
||||
let time_three_hours_later = later_time.format("%H%M").to_string();
|
||||
vevent.push(DtEnd::new(format!(
|
||||
"{}T{}00",
|
||||
self.day.replace('-', ""),
|
||||
time_three_hours_later
|
||||
)));
|
||||
}
|
||||
|
||||
let mut name = String::new();
|
||||
if self.is_cancelled() {
|
||||
name.push_str("ABGESAGT");
|
||||
if let Some(notes) = &self.notes {
|
||||
if !notes.is_empty() {
|
||||
name.push_str(&format!(" (Grund: {notes})"))
|
||||
}
|
||||
}
|
||||
|
||||
name.push_str("! :-( ");
|
||||
}
|
||||
if self.cox_id == user.id {
|
||||
name.push_str("Ruderausfahrt (selber ausgeschrieben)");
|
||||
} else {
|
||||
name.push_str(&format!("Ruderausfahrt mit {} ", self.cox_name));
|
||||
}
|
||||
|
||||
vevent.push(Summary::new(name));
|
||||
vevent
|
||||
}
|
||||
}
|
||||
|
||||
impl Event {
|
||||
pub(crate) async fn get_vevent(self, db: &SqlitePool) -> ics::Event {
|
||||
let mut vevent = ics::Event::new(
|
||||
format!("event-{}@rudernlinz.at", self.id),
|
||||
"19900101T180000",
|
||||
);
|
||||
let time_str = self.planned_starting_time.replace(':', "");
|
||||
let formatted_time = if time_str.len() == 3 {
|
||||
format!("0{}", time_str)
|
||||
} else {
|
||||
time_str.clone() // TODO: remove again
|
||||
};
|
||||
vevent.push(DtStart::new(format!(
|
||||
"{}T{}00",
|
||||
self.day.replace('-', ""),
|
||||
formatted_time
|
||||
)));
|
||||
|
||||
let original_time = NaiveTime::parse_from_str(&self.planned_starting_time, "%H:%M")
|
||||
.expect("Failed to parse time");
|
||||
|
||||
let long_trip = match self.trip_type(db).await {
|
||||
Some(a) if a.name == "Lange Ausfahrt" => true,
|
||||
_ => false,
|
||||
};
|
||||
let later_time = if long_trip {
|
||||
original_time + Duration::hours(6)
|
||||
} else {
|
||||
original_time + Duration::hours(3)
|
||||
};
|
||||
if later_time > original_time {
|
||||
// Check if no day-overflow
|
||||
let time_three_hours_later = later_time.format("%H%M").to_string();
|
||||
vevent.push(DtEnd::new(format!(
|
||||
"{}T{}00",
|
||||
self.day.replace('-', ""),
|
||||
time_three_hours_later
|
||||
)));
|
||||
}
|
||||
|
||||
let tripdetails = self.trip_details(db).await;
|
||||
let mut name = String::new();
|
||||
if self.is_cancelled() {
|
||||
name.push_str("ABGESAGT");
|
||||
if let Some(notes) = &tripdetails.notes {
|
||||
if !notes.is_empty() {
|
||||
name.push_str(&format!(" (Grund: {notes})"))
|
||||
}
|
||||
}
|
||||
|
||||
name.push_str("! :-( ");
|
||||
}
|
||||
name.push_str(&format!("{} ", self.name));
|
||||
|
||||
if let Some(triptype) = tripdetails.triptype(db).await {
|
||||
name.push_str(&format!("• {} ", triptype.name))
|
||||
}
|
||||
vevent.push(Summary::new(name));
|
||||
vevent
|
||||
}
|
||||
}
|
||||
|
77
src/model/trip/create.rs
Normal file
77
src/model/trip/create.rs
Normal file
@ -0,0 +1,77 @@
|
||||
use super::Trip;
|
||||
use crate::model::{
|
||||
notification::Notification,
|
||||
tripdetails::TripDetails,
|
||||
triptype::TripType,
|
||||
user::{ErgoUser, SteeringUser, User},
|
||||
};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
impl Trip {
|
||||
/// Cox decides to create own trip.
|
||||
pub async fn new_own(db: &SqlitePool, cox: &SteeringUser, trip_details: TripDetails) {
|
||||
Self::perform_new(db, &cox.user, trip_details).await
|
||||
}
|
||||
|
||||
/// ErgoUser decides to create ergo 'trip'. Returns false, if trip is not a ergo-session (and
|
||||
/// thus User is not allowed to create such a trip)
|
||||
pub async fn new_own_ergo(db: &SqlitePool, ergo: &ErgoUser, trip_details: TripDetails) -> bool {
|
||||
if let Some(typ) = trip_details.triptype(db).await {
|
||||
let allowed_type = TripType::find_by_id(db, 4).await.unwrap();
|
||||
if typ == allowed_type {
|
||||
Self::perform_new(db, &ergo.user, trip_details).await;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
async fn perform_new(db: &SqlitePool, user: &User, trip_details: TripDetails) {
|
||||
let _ = sqlx::query!(
|
||||
"INSERT INTO trip (cox_id, trip_details_id) VALUES(?, ?)",
|
||||
user.id,
|
||||
trip_details.id
|
||||
)
|
||||
.execute(db)
|
||||
.await;
|
||||
|
||||
Self::notify_trips_same_datetime(db, trip_details, user).await;
|
||||
}
|
||||
|
||||
async fn notify_trips_same_datetime(db: &SqlitePool, trip_details: TripDetails, user: &User) {
|
||||
let same_starting_datetime = TripDetails::find_by_startingdatetime(
|
||||
db,
|
||||
trip_details.day,
|
||||
trip_details.planned_starting_time,
|
||||
)
|
||||
.await;
|
||||
|
||||
for notify in same_starting_datetime {
|
||||
// don't notify oneself
|
||||
if notify.id == trip_details.id {
|
||||
continue;
|
||||
}
|
||||
|
||||
// don't notify people who have cancelled their trip
|
||||
if notify.cancelled() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(trip) = Trip::find_by_trip_details(db, notify.id).await {
|
||||
let user_earlier_trip = User::find_by_id(db, trip.cox_id as i32).await.unwrap();
|
||||
Notification::create(
|
||||
db,
|
||||
&user_earlier_trip,
|
||||
&format!(
|
||||
"{user} hat eine Ausfahrt zur selben Zeit ({} um {}) wie du erstellt",
|
||||
trip.day, trip.planned_starting_time
|
||||
),
|
||||
"Neue Ausfahrt zur selben Zeit",
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,25 +1,26 @@
|
||||
use chrono::{Duration, Local, NaiveDate, NaiveTime};
|
||||
use ics::properties::{DtEnd, DtStart, Summary};
|
||||
use chrono::{Local, NaiveDate};
|
||||
use serde::Serialize;
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
mod create;
|
||||
|
||||
use super::{
|
||||
event::{Event, Registration},
|
||||
log::Log,
|
||||
notification::Notification,
|
||||
tripdetails::TripDetails,
|
||||
triptype::TripType,
|
||||
user::{ErgoUser, SteeringUser, User},
|
||||
user::{SteeringUser, User},
|
||||
usertrip::UserTrip,
|
||||
};
|
||||
|
||||
#[derive(Serialize, Clone, Debug)]
|
||||
pub struct Trip {
|
||||
id: i64,
|
||||
pub id: i64,
|
||||
pub cox_id: i64,
|
||||
cox_name: String,
|
||||
pub cox_name: String,
|
||||
trip_details_id: Option<i64>,
|
||||
planned_starting_time: String,
|
||||
pub planned_starting_time: String,
|
||||
pub max_people: i64,
|
||||
pub day: String,
|
||||
pub notes: Option<String>,
|
||||
@ -69,65 +70,6 @@ impl TripWithDetails {
|
||||
}
|
||||
|
||||
impl Trip {
|
||||
/// Cox decides to create own trip.
|
||||
pub async fn new_own(db: &SqlitePool, cox: &SteeringUser, trip_details: TripDetails) {
|
||||
Self::perform_new(db, &cox.user, trip_details).await
|
||||
}
|
||||
|
||||
pub async fn new_own_ergo(db: &SqlitePool, ergo: &ErgoUser, trip_details: TripDetails) {
|
||||
let typ = trip_details.triptype(db).await;
|
||||
if let Some(typ) = typ {
|
||||
let allowed_type = TripType::find_by_id(db, 4).await.unwrap();
|
||||
if typ == allowed_type {
|
||||
Self::perform_new(db, &ergo.user, trip_details).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn perform_new(db: &SqlitePool, user: &User, trip_details: TripDetails) {
|
||||
let _ = sqlx::query!(
|
||||
"INSERT INTO trip (cox_id, trip_details_id) VALUES(?, ?)",
|
||||
user.id,
|
||||
trip_details.id
|
||||
)
|
||||
.execute(db)
|
||||
.await;
|
||||
|
||||
let same_starting_datetime = TripDetails::find_by_startingdatetime(
|
||||
db,
|
||||
trip_details.day,
|
||||
trip_details.planned_starting_time,
|
||||
)
|
||||
.await;
|
||||
for notify in same_starting_datetime {
|
||||
// don't notify oneself
|
||||
if notify.id == trip_details.id {
|
||||
continue;
|
||||
}
|
||||
|
||||
// don't notify people who have cancelled their trip
|
||||
if notify.cancelled() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(trip) = Trip::find_by_trip_details(db, notify.id).await {
|
||||
let user_earlier_trip = User::find_by_id(db, trip.cox_id as i32).await.unwrap();
|
||||
Notification::create(
|
||||
db,
|
||||
&user_earlier_trip,
|
||||
&format!(
|
||||
"{} hat eine Ausfahrt zur selben Zeit ({} um {}) wie du erstellt",
|
||||
user.name, trip.day, trip.planned_starting_time
|
||||
),
|
||||
"Neue Ausfahrt zur selben Zeit",
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn find_by_trip_details(db: &SqlitePool, tripdetails_id: i64) -> Option<Self> {
|
||||
sqlx::query_as!(
|
||||
Self,
|
||||
@ -145,7 +87,7 @@ WHERE trip_details.id=?
|
||||
.ok()
|
||||
}
|
||||
|
||||
async fn trip_type(&self, db: &SqlitePool) -> Option<TripType> {
|
||||
pub(crate) async fn trip_type(&self, db: &SqlitePool) -> Option<TripType> {
|
||||
if let Some(trip_type_id) = self.trip_type_id {
|
||||
TripType::find_by_id(db, trip_type_id).await
|
||||
} else {
|
||||
@ -153,64 +95,6 @@ WHERE trip_details.id=?
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn get_vevent<'a>(self, db: &'a SqlitePool, user: &'a User) -> ics::Event<'a> {
|
||||
let mut vevent =
|
||||
ics::Event::new(format!("trip-{}@rudernlinz.at", self.id), "19900101T180000");
|
||||
let time_str = self.planned_starting_time.replace(':', "");
|
||||
let formatted_time = if time_str.len() == 3 {
|
||||
format!("0{}", time_str)
|
||||
} else {
|
||||
time_str
|
||||
};
|
||||
|
||||
vevent.push(DtStart::new(format!(
|
||||
"{}T{}00",
|
||||
self.day.replace('-', ""),
|
||||
formatted_time
|
||||
)));
|
||||
|
||||
let original_time = NaiveTime::parse_from_str(&self.planned_starting_time, "%H:%M")
|
||||
.expect("Failed to parse time");
|
||||
let long_trip = match self.trip_type(db).await {
|
||||
Some(a) if a.name == "Lange Ausfahrt" => true,
|
||||
_ => false,
|
||||
};
|
||||
let later_time = if long_trip {
|
||||
original_time + Duration::hours(6)
|
||||
} else {
|
||||
original_time + Duration::hours(3)
|
||||
};
|
||||
if later_time > original_time {
|
||||
// Check if no day-overflow
|
||||
let time_three_hours_later = later_time.format("%H%M").to_string();
|
||||
vevent.push(DtEnd::new(format!(
|
||||
"{}T{}00",
|
||||
self.day.replace('-', ""),
|
||||
time_three_hours_later
|
||||
)));
|
||||
}
|
||||
|
||||
let mut name = String::new();
|
||||
if self.is_cancelled() {
|
||||
name.push_str("ABGESAGT");
|
||||
if let Some(notes) = &self.notes {
|
||||
if !notes.is_empty() {
|
||||
name.push_str(&format!(" (Grund: {notes})"))
|
||||
}
|
||||
}
|
||||
|
||||
name.push_str("! :-( ");
|
||||
}
|
||||
if self.cox_id == user.id {
|
||||
name.push_str("Ruderausfahrt (selber ausgeschrieben)");
|
||||
} else {
|
||||
name.push_str(&format!("Ruderausfahrt mit {} ", self.cox_name));
|
||||
}
|
||||
|
||||
vevent.push(Summary::new(name));
|
||||
vevent
|
||||
}
|
||||
|
||||
pub async fn all(db: &SqlitePool) -> Vec<Self> {
|
||||
sqlx::query_as!(
|
||||
Self,
|
||||
@ -491,7 +375,7 @@ WHERE day=?
|
||||
trips
|
||||
}
|
||||
|
||||
fn is_cancelled(&self) -> bool {
|
||||
pub(crate) fn is_cancelled(&self) -> bool {
|
||||
self.max_people == -1
|
||||
}
|
||||
}
|
||||
@ -583,11 +467,9 @@ mod test {
|
||||
|
||||
let last_notification = &Notification::for_user(&pool, &cox).await[0];
|
||||
|
||||
assert!(
|
||||
last_notification
|
||||
.message
|
||||
.starts_with("cox2 hat eine Ausfahrt zur selben Zeit")
|
||||
);
|
||||
assert!(last_notification
|
||||
.message
|
||||
.starts_with("cox2 hat eine Ausfahrt zur selben Zeit"));
|
||||
}
|
||||
|
||||
#[sqlx::test]
|
@ -1,9 +1,8 @@
|
||||
use rocket::{
|
||||
FromForm, Route, State,
|
||||
form::Form,
|
||||
get, post,
|
||||
response::{Flash, Redirect},
|
||||
routes,
|
||||
routes, FromForm, Route, State,
|
||||
};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
@ -23,21 +22,13 @@ async fn create_ergo(
|
||||
) -> Flash<Redirect> {
|
||||
let trip_details_id = TripDetails::create(db, data.into_inner()).await;
|
||||
let trip_details = TripDetails::find_by_id(db, trip_details_id).await.unwrap(); //Okay, bc just
|
||||
//created
|
||||
//created
|
||||
Trip::new_own_ergo(db, &cox, trip_details).await; //TODO: fix
|
||||
|
||||
//Log::create(
|
||||
// db,
|
||||
// format!(
|
||||
// "Cox {} created trip on {} @ {} for {} rower",
|
||||
// cox.name, trip_details.day, trip_details.planned_starting_time, trip_details.max_people,
|
||||
// ),
|
||||
//)
|
||||
//.await;
|
||||
|
||||
Flash::success(Redirect::to("/planned"), "Ausfahrt erfolgreich erstellt.")
|
||||
}
|
||||
|
||||
/// SteeringUser created new trip
|
||||
#[post("/trip", data = "<data>")]
|
||||
async fn create(
|
||||
db: &State<SqlitePool>,
|
||||
@ -46,18 +37,9 @@ async fn create(
|
||||
) -> Flash<Redirect> {
|
||||
let trip_details_id = TripDetails::create(db, data.into_inner()).await;
|
||||
let trip_details = TripDetails::find_by_id(db, trip_details_id).await.unwrap(); //Okay, bc just
|
||||
//created
|
||||
//created
|
||||
Trip::new_own(db, &cox, trip_details).await; //TODO: fix
|
||||
|
||||
//Log::create(
|
||||
// db,
|
||||
// format!(
|
||||
// "Cox {} created trip on {} @ {} for {} rower",
|
||||
// cox.name, trip_details.day, trip_details.planned_starting_time, trip_details.max_people,
|
||||
// ),
|
||||
//)
|
||||
//.await;
|
||||
|
||||
Flash::success(Redirect::to("/planned"), "Ausfahrt erfolgreich erstellt.")
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user