cal #736
							
								
								
									
										2
									
								
								TODO.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								TODO.md
									
									
									
									
									
										Normal 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 | ||||||
| @@ -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,38 +405,41 @@ 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"); |  | ||||||
|             vevent.push(DtStart::new(format!( |  | ||||||
|                 "{}T{}00", |  | ||||||
|                 event.day.replace('-', ""), |  | ||||||
|                 event.planned_starting_time.replace(':', "") |  | ||||||
|             ))); |  | ||||||
|             let tripdetails = event.trip_details(db).await; |  | ||||||
|             let mut name = String::new(); |  | ||||||
|             if event.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!("{} ", event.name)); |  | ||||||
|  |  | ||||||
|             if let Some(triptype) = tripdetails.triptype(db).await { |  | ||||||
|                 name.push_str(&format!("• {} ", triptype.name)) |  | ||||||
|             } |  | ||||||
|             vevent.push(Summary::new(name)); |  | ||||||
|             calendar.add_event(vevent); |  | ||||||
|         } |         } | ||||||
|         let mut buf = Vec::new(); |         let mut buf = Vec::new(); | ||||||
|         write!(&mut buf, "{}", calendar).unwrap(); |         write!(&mut buf, "{}", calendar).unwrap(); | ||||||
|         String::from_utf8(buf).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!( | ||||||
|  |             "{}T{}00", | ||||||
|  |             self.day.replace('-', ""), | ||||||
|  |             self.planned_starting_time.replace(':', "") | ||||||
|  |         ))); | ||||||
|  |         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 { |     pub async fn trip_details(&self, db: &SqlitePool) -> TripDetails { | ||||||
|         TripDetails::find_by_id(db, self.trip_details_id) |         TripDetails::find_by_id(db, self.trip_details_id) | ||||||
|             .await |             .await | ||||||
|   | |||||||
							
								
								
									
										23
									
								
								src/model/personal/cal.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/model/personal/cal.rs
									
									
									
									
									
										Normal 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() | ||||||
|  | } | ||||||
| @@ -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; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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)] | ||||||
|   | |||||||
| @@ -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)] | ||||||
|   | |||||||
| @@ -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"> 📅 </span> 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" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user