restructure for equatorprice
This commit is contained in:
		| @@ -25,6 +25,7 @@ pub mod logbook; | |||||||
| pub mod logtype; | pub mod logtype; | ||||||
| pub mod mail; | pub mod mail; | ||||||
| pub mod notification; | pub mod notification; | ||||||
|  | pub mod personal; | ||||||
| pub mod role; | pub mod role; | ||||||
| pub mod rower; | pub mod rower; | ||||||
| pub mod stat; | pub mod stat; | ||||||
|   | |||||||
							
								
								
									
										58
									
								
								src/model/personal/equatorprice.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								src/model/personal/equatorprice.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | |||||||
|  | use serde::Serialize; | ||||||
|  |  | ||||||
|  | #[derive(Serialize)] | ||||||
|  | enum Level { | ||||||
|  |     BRONZE, | ||||||
|  |     SILVER, | ||||||
|  |     GOLD, | ||||||
|  |     DIAMOND, | ||||||
|  |     DONE, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Level { | ||||||
|  |     fn required_km(&self) -> i32 { | ||||||
|  |         match self { | ||||||
|  |             Level::BRONZE => 40000, | ||||||
|  |             Level::SILVER => 80000, | ||||||
|  |             Level::GOLD => 100000, | ||||||
|  |             Level::DIAMOND => 200000, | ||||||
|  |             Level::DONE => 0, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn next_level(km: i32) -> Self { | ||||||
|  |         if km < Level::BRONZE.required_km() { | ||||||
|  |             Level::BRONZE | ||||||
|  |         } else if km < Level::SILVER.required_km() { | ||||||
|  |             Level::SILVER | ||||||
|  |         } else if km < Level::GOLD.required_km() { | ||||||
|  |             Level::GOLD | ||||||
|  |         } else if km < Level::DIAMOND.required_km() { | ||||||
|  |             Level::BRONZE | ||||||
|  |         } else { | ||||||
|  |             Level::DONE | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Serialize)] | ||||||
|  | pub(crate) struct Next { | ||||||
|  |     level: Level, | ||||||
|  |     missing_km: i32, | ||||||
|  |     required_km: i32, | ||||||
|  |     rowed_km: i32, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Next { | ||||||
|  |     pub(crate) fn rowed_km(km: i32) -> Self { | ||||||
|  |         let level = Level::next_level(km); | ||||||
|  |         let required_km = level.required_km(); | ||||||
|  |         let missing_km = required_km - km; | ||||||
|  |         Self { | ||||||
|  |             level, | ||||||
|  |             missing_km, | ||||||
|  |             required_km, | ||||||
|  |             rowed_km: km, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										21
									
								
								src/model/personal/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/model/personal/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | use serde::Serialize; | ||||||
|  | use sqlx::SqlitePool; | ||||||
|  |  | ||||||
|  | use super::{stat::Stat, user::User}; | ||||||
|  |  | ||||||
|  | pub(crate) mod equatorprice; | ||||||
|  | pub(crate) mod rowingbadge; | ||||||
|  |  | ||||||
|  | #[derive(Serialize)] | ||||||
|  | pub(crate) struct Achievements { | ||||||
|  |     equatorprice: equatorprice::Next, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Achievements { | ||||||
|  |     pub(crate) async fn for_user(db: &SqlitePool, user: &User) -> Self { | ||||||
|  |         let rowed_km = Stat::person(db, None, user).await.rowed_km; | ||||||
|  |         Self { | ||||||
|  |             equatorprice: equatorprice::Next::rowed_km(rowed_km), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										62
									
								
								src/model/personal/rowingbadge.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/model/personal/rowingbadge.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | |||||||
|  | use chrono::{Datelike, Local, NaiveDate}; | ||||||
|  |  | ||||||
|  | use crate::model::user::User; | ||||||
|  |  | ||||||
|  | enum AgeBracket { | ||||||
|  |     Till14, | ||||||
|  |     From14Till18, | ||||||
|  |     From19Till30, | ||||||
|  |     From31Till60, | ||||||
|  |     From61Till75, | ||||||
|  |     From76, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl TryFrom<&User> for AgeBracket { | ||||||
|  |     type Error = String; | ||||||
|  |  | ||||||
|  |     fn try_from(value: &User) -> Result<Self, Self::Error> { | ||||||
|  |         if let Some(birthdate) = value.birthdate.clone() { | ||||||
|  |             let today = Local::now().date_naive(); | ||||||
|  |             let birthdate = NaiveDate::parse_from_str(&birthdate, "%Y-%m-%d").unwrap(); | ||||||
|  |  | ||||||
|  |             let age = today.year() - birthdate.year(); | ||||||
|  |             if age <= 14 { | ||||||
|  |                 Ok(AgeBracket::Till14) | ||||||
|  |             } else if age <= 18 { | ||||||
|  |                 Ok(AgeBracket::From14Till18) | ||||||
|  |             } else if age <= 30 { | ||||||
|  |                 Ok(AgeBracket::From19Till30) | ||||||
|  |             } else if age <= 60 { | ||||||
|  |                 Ok(AgeBracket::From31Till60) | ||||||
|  |             } else if age <= 75 { | ||||||
|  |                 Ok(AgeBracket::From61Till75) | ||||||
|  |             } else { | ||||||
|  |                 Ok(AgeBracket::From76) | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             Err("User has no birthdate".to_string()) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn cat(value: &AgeBracket) -> &str { | ||||||
|  |     match value { | ||||||
|  |         AgeBracket::Till14 => "Schülerinnen und Schüler bis 14 Jahre", | ||||||
|  |         AgeBracket::From14Till18 => "Juniorinnen und Junioren, Para-Ruderer bis 18 Jahre", | ||||||
|  |         AgeBracket::From19Till30 => "Frauen und Männer, Para-Ruderer bis 30 Jahre", | ||||||
|  |         AgeBracket::From31Till60 => "Frauen und Männer, Para-Ruderer von 31 bis 60 Jahre", | ||||||
|  |         AgeBracket::From61Till75 => "Frauen und Männer, Para-Ruderer von 61 bis 75 Jahre", | ||||||
|  |         AgeBracket::From76 => "Frauen und Männer, Para-Ruderer ab 76 Jahre", | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn dist_in_km(value: &AgeBracket) -> u32 { | ||||||
|  |     match value { | ||||||
|  |         AgeBracket::Till14 => 500, | ||||||
|  |         AgeBracket::From14Till18 => 1000, | ||||||
|  |         AgeBracket::From19Till30 => 1200, | ||||||
|  |         AgeBracket::From31Till60 => 1000, | ||||||
|  |         AgeBracket::From61Till75 => 800, | ||||||
|  |         AgeBracket::From76 => 600, | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -24,71 +24,6 @@ use crate::{ | |||||||
|     SCHECKBUCH, STUDENT_OR_PUPIL, UNTERSTUETZEND, |     SCHECKBUCH, STUDENT_OR_PUPIL, UNTERSTUETZEND, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| mod aequatorpreis { |  | ||||||
|     use chrono::{Datelike, Local, NaiveDate}; |  | ||||||
|  |  | ||||||
|     use super::User; |  | ||||||
|  |  | ||||||
|     enum AgeBracket { |  | ||||||
|         Till14, |  | ||||||
|         From14Till18, |  | ||||||
|         From19Till30, |  | ||||||
|         From31Till60, |  | ||||||
|         From61Till75, |  | ||||||
|         From76, |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     impl TryFrom<&User> for AgeBracket { |  | ||||||
|         type Error = String; |  | ||||||
|  |  | ||||||
|         fn try_from(value: &User) -> Result<Self, Self::Error> { |  | ||||||
|             if let Some(birthdate) = value.birthdate.clone() { |  | ||||||
|                 let today = Local::now().date_naive(); |  | ||||||
|                 let birthdate = NaiveDate::parse_from_str(&birthdate, "%Y-%m-%d").unwrap(); |  | ||||||
|  |  | ||||||
|                 let age = today.year() - birthdate.year(); |  | ||||||
|                 if age <= 14 { |  | ||||||
|                     Ok(AgeBracket::Till14) |  | ||||||
|                 } else if age <= 18 { |  | ||||||
|                     Ok(AgeBracket::From14Till18) |  | ||||||
|                 } else if age <= 30 { |  | ||||||
|                     Ok(AgeBracket::From19Till30) |  | ||||||
|                 } else if age <= 60 { |  | ||||||
|                     Ok(AgeBracket::From31Till60) |  | ||||||
|                 } else if age <= 75 { |  | ||||||
|                     Ok(AgeBracket::From61Till75) |  | ||||||
|                 } else { |  | ||||||
|                     Ok(AgeBracket::From76) |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 Err("User has no birthdate".to_string()) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn cat(value: &AgeBracket) -> &str { |  | ||||||
|         match value { |  | ||||||
|             AgeBracket::Till14 => "Schülerinnen und Schüler bis 14 Jahre", |  | ||||||
|             AgeBracket::From14Till18 => "Juniorinnen und Junioren, Para-Ruderer bis 18 Jahre", |  | ||||||
|             AgeBracket::From19Till30 => "Frauen und Männer, Para-Ruderer bis 30 Jahre", |  | ||||||
|             AgeBracket::From31Till60 => "Frauen und Männer, Para-Ruderer von 31 bis 60 Jahre", |  | ||||||
|             AgeBracket::From61Till75 => "Frauen und Männer, Para-Ruderer von 61 bis 75 Jahre", |  | ||||||
|             AgeBracket::From76 => "Frauen und Männer, Para-Ruderer ab 76 Jahre", |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn dist_in_km(value: &AgeBracket) -> u32 { |  | ||||||
|         match value { |  | ||||||
|             AgeBracket::Till14 => 500, |  | ||||||
|             AgeBracket::From14Till18 => 1000, |  | ||||||
|             AgeBracket::From19Till30 => 1200, |  | ||||||
|             AgeBracket::From31Till60 => 1000, |  | ||||||
|             AgeBracket::From61Till75 => 800, |  | ||||||
|             AgeBracket::From76 => 600, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #[derive(FromRow, Serialize, Deserialize, Clone, Debug, Eq, Hash, PartialEq)] | #[derive(FromRow, Serialize, Deserialize, Clone, Debug, Eq, Hash, PartialEq)] | ||||||
| pub struct User { | pub struct User { | ||||||
|     pub id: i64, |     pub id: i64, | ||||||
|   | |||||||
| @@ -24,6 +24,7 @@ use crate::{ | |||||||
|     model::{ |     model::{ | ||||||
|         logbook::Logbook, |         logbook::Logbook, | ||||||
|         notification::Notification, |         notification::Notification, | ||||||
|  |         personal::Achievements, | ||||||
|         role::Role, |         role::Role, | ||||||
|         user::{User, UserWithDetails}, |         user::{User, UserWithDetails}, | ||||||
|     }, |     }, | ||||||
| @@ -62,6 +63,7 @@ async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_ | |||||||
|         context.insert("last_trips", &last_trips); |         context.insert("last_trips", &last_trips); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     context.insert("achievements", &Achievements::for_user(db, &user).await); | ||||||
|     context.insert("notifications", &Notification::for_user(db, &user).await); |     context.insert("notifications", &Notification::for_user(db, &user).await); | ||||||
|     context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await); |     context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await); | ||||||
|     context.insert("costs_scheckbuch", &SCHECKBUCH); |     context.insert("costs_scheckbuch", &SCHECKBUCH); | ||||||
|   | |||||||
| @@ -74,6 +74,32 @@ | |||||||
|                 </div> |                 </div> | ||||||
|             {% endif %} |             {% endif %} | ||||||
|             {% if "Donau Linz" in loggedin_user.roles and "Unterstützend" not in loggedin_user.roles and "Förderndes Mitglied" not in loggedin_user.roles %} |             {% if "Donau Linz" in loggedin_user.roles and "Unterstützend" not in loggedin_user.roles and "Förderndes Mitglied" not in loggedin_user.roles %} | ||||||
|  |                 <div class="bg-white dark:bg-primary-900 text-black dark:text-white rounded-md block shadow mt-5" | ||||||
|  |                      role="alert"> | ||||||
|  |                     <h2 class="h2">Persönliches</h2> | ||||||
|  |                     <div class="mx-2 divide-y divide-gray-200 dark:divide-primary-600"> | ||||||
|  |                         <div class="py-3"> | ||||||
|  |                             <h3 class="font-bold text-xl mb-3">Äquatorpreis</h3> | ||||||
|  |                             {% set price = achievements.equatorprice %} | ||||||
|  |                             {% if price.level == "DONE" %} | ||||||
|  |                                 Gratuliere, du hast alles erreicht, was es beim Äquatorpreis zu erreichen gibt. | ||||||
|  |                             {% else %} | ||||||
|  |                                 <label for="equatorprice" class="label">{{ price.level }}</label> | ||||||
|  |                                 <progress id="equatorprice" | ||||||
|  |                                           class="w-full block my-3" | ||||||
|  |                                           value="{{ price.rowed_km }}" | ||||||
|  |                                           max="{{ price.required_km }}"></progress> | ||||||
|  |                                 <details> | ||||||
|  |                                     <summary>Details</summary> | ||||||
|  |                                     Du bist insgesamt {{ price.rowed_km }} km gerudert. Um den Äquatorpreis in {{ price.level }} zu erhalten, benötigst du noch {{ price.missing_km }} um die notwendigen {{ price.required_km }} km zu erreichen. | ||||||
|  |                                 </details> | ||||||
|  |                             {% endif %} | ||||||
|  |                         </div> | ||||||
|  |                         <div class="py-1"> | ||||||
|  |                             <h3>Fahrtenabzeichen</h3> | ||||||
|  |                         </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" | ||||||
|                      role="alert"> |                      role="alert"> | ||||||
|                     <h2 class="h2">Aktives Vereinsmitglied</h2> |                     <h2 class="h2">Aktives Vereinsmitglied</h2> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user