cal #735

Merged
philipp merged 4 commits from cal into staging 2024-09-10 23:47:37 +02:00
7 changed files with 151 additions and 29 deletions
Showing only changes of commit d404636261 - Show all commits

2
TODO.md Normal file
View File

@ -0,0 +1,2 @@
- create new field in user table -> user\_token
- change in misc.rs personal calendar function on not require User, but user\_token

View File

@ -183,6 +183,17 @@ INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id",
.unwrap() //TODO: fixme .unwrap() //TODO: fixme
} }
pub async fn all_with_user(db: &SqlitePool, user: &User) -> Vec<Event> {
let mut ret = Vec::new();
let events = Self::all(db).await;
for event in events {
if event.is_rower_registered(db, user).await {
ret.push(event);
}
}
ret
}
//TODO: add tests //TODO: add tests
pub async fn is_rower_registered(&self, db: &SqlitePool, user: &User) -> bool { pub async fn is_rower_registered(&self, db: &SqlitePool, user: &User) -> bool {
let is_rower = sqlx::query!( let is_rower = sqlx::query!(
@ -394,16 +405,23 @@ WHERE trip_details.id=?
let events = Event::all(db).await; let events = Event::all(db).await;
for event in events { for event in events {
let mut vevent = calendar.add_event(event.get_vevent(db).await);
ics::Event::new(format!("{}@rudernlinz.at", event.id), "19900101T180000"); }
let mut buf = Vec::new();
write!(&mut buf, "{}", calendar).unwrap();
String::from_utf8(buf).unwrap()
}
pub(crate) async fn get_vevent(self, db: &SqlitePool) -> ics::Event {
let mut vevent = ics::Event::new(format!("{}@rudernlinz.at", self.id), "19900101T180000");
vevent.push(DtStart::new(format!( vevent.push(DtStart::new(format!(
"{}T{}00", "{}T{}00",
event.day.replace('-', ""), self.day.replace('-', ""),
event.planned_starting_time.replace(':', "") self.planned_starting_time.replace(':', "")
))); )));
let tripdetails = event.trip_details(db).await; let tripdetails = self.trip_details(db).await;
let mut name = String::new(); let mut name = String::new();
if event.is_cancelled() { if self.is_cancelled() {
name.push_str("ABGESAGT"); name.push_str("ABGESAGT");
if let Some(notes) = &tripdetails.notes { if let Some(notes) = &tripdetails.notes {
if !notes.is_empty() { if !notes.is_empty() {
@ -413,17 +431,13 @@ WHERE trip_details.id=?
name.push_str("! :-( "); name.push_str("! :-( ");
} }
name.push_str(&format!("{} ", event.name)); name.push_str(&format!("{} ", self.name));
if let Some(triptype) = tripdetails.triptype(db).await { if let Some(triptype) = tripdetails.triptype(db).await {
name.push_str(&format!("{} ", triptype.name)) name.push_str(&format!("{} ", triptype.name))
} }
vevent.push(Summary::new(name)); vevent.push(Summary::new(name));
calendar.add_event(vevent); vevent
}
let mut buf = Vec::new();
write!(&mut buf, "{}", calendar).unwrap();
String::from_utf8(buf).unwrap()
} }
pub async fn trip_details(&self, db: &SqlitePool) -> TripDetails { pub async fn trip_details(&self, db: &SqlitePool) -> TripDetails {

23
src/model/personal/cal.rs Normal file
View File

@ -0,0 +1,23 @@
use std::io::Write;
use ics::ICalendar;
use sqlx::SqlitePool;
use crate::model::{event::Event, trip::Trip, user::User};
pub(crate) async fn get_personal_cal(db: &SqlitePool, user: &User) -> String {
let mut calendar = ICalendar::new("2.0", "ics-rs");
let events = Event::all_with_user(db, user).await;
for event in events {
calendar.add_event(event.get_vevent(db).await);
}
let trips = Trip::all_with_user(db, user).await;
for trip in trips {
calendar.add_event(trip.get_vevent(db).await);
}
let mut buf = Vec::new();
write!(&mut buf, "{}", calendar).unwrap();
String::from_utf8(buf).unwrap()
}

View File

@ -5,6 +5,7 @@ use sqlx::SqlitePool;
use super::{logbook::Logbook, stat::Stat, user::User}; use super::{logbook::Logbook, stat::Stat, user::User};
pub(crate) mod cal;
pub(crate) mod equatorprice; pub(crate) mod equatorprice;
pub(crate) mod rowingbadge; pub(crate) mod rowingbadge;

View File

@ -1,4 +1,5 @@
use chrono::{Local, NaiveDate}; use chrono::{Local, NaiveDate};
use ics::properties::{DtStart, Summary};
use serde::Serialize; use serde::Serialize;
use sqlx::SqlitePool; use sqlx::SqlitePool;
@ -9,6 +10,7 @@ use super::{
tripdetails::TripDetails, tripdetails::TripDetails,
triptype::TripType, triptype::TripType,
user::{CoxUser, User}, user::{CoxUser, User},
usertrip::UserTrip,
}; };
#[derive(Serialize, Clone, Debug)] #[derive(Serialize, Clone, Debug)]
@ -123,6 +125,61 @@ WHERE trip_details.id=?
.ok() .ok()
} }
pub(crate) async fn get_vevent(self, db: &SqlitePool) -> ics::Event {
let mut vevent = ics::Event::new(format!("{}@rudernlinz.at", self.id), "19900101T180000");
vevent.push(DtStart::new(format!(
"{}T{}00",
self.day.replace('-', ""),
self.planned_starting_time.replace(':', "")
)));
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("! :-( ");
}
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,
"
SELECT trip.id, cox_id, user.name as cox_name, trip_details_id, planned_starting_time, max_people, day, trip_details.notes, allow_guests, trip_type_id, always_show, is_locked
FROM trip
INNER JOIN trip_details ON trip.trip_details_id = trip_details.id
INNER JOIN user ON trip.cox_id = user.id
",
)
.fetch_all(db)
.await
.unwrap() //TODO: fixme
}
pub async fn all_with_user(db: &SqlitePool, user: &User) -> Vec<Self> {
let mut ret = Vec::new();
let trips = Self::all(db).await;
for trip in trips {
if let Some(trip_details_id) = trip.trip_details_id {
if UserTrip::find_by_userid_and_trip_detail_id(db, user.id, trip_details_id)
.await
.is_some()
{
ret.push(trip);
}
}
}
ret
}
pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> { pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> {
sqlx::query_as!( sqlx::query_as!(
Self, Self,
@ -370,6 +427,10 @@ WHERE day=?
trips.retain(|e| e.trip.always_show); trips.retain(|e| e.trip.always_show);
trips trips
} }
fn is_cancelled(&self) -> bool {
self.max_people == 0
}
} }
#[derive(Debug)] #[derive(Debug)]

View File

@ -1,7 +1,7 @@
use rocket::{get, http::ContentType, routes, Route, State}; use rocket::{get, http::ContentType, routes, Route, State};
use sqlx::SqlitePool; use sqlx::SqlitePool;
use crate::model::event::Event; use crate::model::{event::Event, personal::cal::get_personal_cal, user::User};
#[get("/cal")] #[get("/cal")]
async fn cal(db: &State<SqlitePool>) -> (ContentType, String) { async fn cal(db: &State<SqlitePool>) -> (ContentType, String) {
@ -9,8 +9,14 @@ async fn cal(db: &State<SqlitePool>) -> (ContentType, String) {
(ContentType::Calendar, Event::get_ics_feed(db).await) (ContentType::Calendar, Event::get_ics_feed(db).await)
} }
#[get("/cal/registered")]
async fn cal_registered(db: &State<SqlitePool>, user: User) -> (ContentType, String) {
//TODO: add unit test once proper functionality is there
(ContentType::Calendar, get_personal_cal(db, &user).await)
}
pub fn routes() -> Vec<Route> { pub fn routes() -> Vec<Route> {
routes![cal] routes![cal, cal_registered]
} }
#[cfg(test)] #[cfg(test)]

View File

@ -103,7 +103,7 @@
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5" <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"
role="alert"> role="alert">
<h2 class="h2"> <h2 class="h2">
Deine Ruderkarriere Dein Ruderleben
<span class="text-xl" <span class="text-xl"
onclick="document.getElementById('call-for-action').showModal()">💡</span> onclick="document.getElementById('call-for-action').showModal()">💡</span>
</h2> </h2>
@ -213,6 +213,21 @@
</div> </div>
</details> </details>
</div> </div>
<div class="py-3">
<p>
<details>
<summary><span class="text-xl">&nbsp;&#128197;&nbsp;</span>&nbsp;Kalender</summary>
<p class="mt-3">
Du möchtest immer up-to-date mit den Events und Ausfahrten bleiben? Wir bieten 3 verschiedene Arten von Kalender an:</p>
<ol class="list-decimal ml-5 my-3">
<li><strong>Alle Events und Ausfahrten</strong>, zu denen du dich angemeldet hast: <a class="underline" href="https://app.rudernlinz.at/cal/personal?my-secrect-key">https://app.rudernlinz.at/cal/personal?my-secrect-key</a></li>
<li><strong>Allgemeiner Kalender</strong>, zB save-the-dates (Wanderfahrten, ...): <a href="https://rudernlinz.at/cal" class="underline">https://rudernlinz.at/cal</a></li>
<li><strong>Alle Events</strong>: <a class="underline" href="https://app.rudernlinz.at/cal">https://app.rudernlinz.at/cal</a></li>
</ol>
Du kannst die Kalender einfach in deinen Kalender als "externen Kalender" synchronisieren. Die genauen Schritte hängen von deiner verwendeten Software ab.
</details>
</p>
</div>
</div> </div>
</div> </div>
<div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5" <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5"