2023-11-02 12:15:10 +01:00
use std ::env ;
2024-10-19 22:02:44 +02:00
use chrono ::{ Datelike , Utc } ;
2023-11-02 12:15:10 +01:00
use rocket ::{
form ::Form ,
fs ::TempFile ,
get ,
http ::ContentType ,
post ,
request ::FlashMessage ,
response ::{ Flash , Redirect } ,
routes , FromForm , Route , State ,
} ;
use rocket_dyn_templates ::{ context , Template } ;
use serde ::Serialize ;
use sqlx ::SqlitePool ;
use tera ::Context ;
use crate ::model ::{
2024-10-19 22:02:44 +02:00
log ::Log , notification ::Notification , role ::Role , user ::{ AdminUser , User , UserWithDetails }
2023-11-02 12:15:10 +01:00
} ;
#[ derive(Serialize) ]
struct ErgoStat {
2023-11-07 21:27:40 +01:00
id : i64 ,
2023-11-02 12:15:10 +01:00
name : String ,
dob : Option < String > ,
weight : Option < String > ,
sex : Option < String > ,
result : Option < String > ,
}
#[ get( " /final " ) ]
async fn send ( db : & State < SqlitePool > , _user : AdminUser ) -> Template {
let thirty = sqlx ::query_as! (
ErgoStat ,
2023-11-07 21:27:40 +01:00
" SELECT id, name, dirty_thirty as result, dob, weight, sex FROM user WHERE deleted = 0 AND dirty_thirty is not null ORDER BY result DESC "
2023-11-02 12:15:10 +01:00
)
. fetch_all ( db . inner ( ) )
. await
. unwrap ( ) ;
let dozen = sqlx ::query_as! (
ErgoStat ,
2023-11-07 21:27:40 +01:00
" SELECT id, name, dirty_dozen as result, dob, weight, sex FROM user WHERE deleted = 0 AND dirty_dozen is not null ORDER BY result DESC "
2023-11-02 12:15:10 +01:00
)
. fetch_all ( db . inner ( ) )
. await
. unwrap ( ) ;
Template ::render (
2024-10-19 22:02:44 +02:00
" ergo/final " ,
2024-05-06 12:17:03 +02:00
context! ( loggedin_user : & UserWithDetails ::from_user ( _user . user , db ) . await , thirty , dozen ) ,
2023-11-02 12:15:10 +01:00
)
}
#[ get( " /reset " ) ]
async fn reset ( db : & State < SqlitePool > , _user : AdminUser ) -> Flash < Redirect > {
sqlx ::query! ( " UPDATE user SET dirty_thirty = NULL, dirty_dozen = NULL; " )
. execute ( db . inner ( ) )
. await
. unwrap ( ) ;
Flash ::success (
Redirect ::to ( " /ergo " ) ,
" Erfolgreich zurückgesetzt (Bilder müssen manuell gelöscht werden!) " ,
)
}
2023-11-07 21:27:40 +01:00
#[ get( " /<challenge>/user/<user_id>/new?<new> " ) ]
async fn update (
db : & State < SqlitePool > ,
_admin : AdminUser ,
challenge : & str ,
user_id : i64 ,
new : & str ,
) -> Flash < Redirect > {
if challenge = = " thirty " {
sqlx ::query! ( " UPDATE user SET dirty_thirty = ? WHERE id=? " , new , user_id )
. execute ( db . inner ( ) )
. await
. unwrap ( ) ;
Flash ::success ( Redirect ::to ( " /ergo " ) , " Succ " )
} else if challenge = = " dozen " {
sqlx ::query! ( " UPDATE user SET dirty_dozen = ? WHERE id=? " , new , user_id )
. execute ( db . inner ( ) )
. await
. unwrap ( ) ;
Flash ::success ( Redirect ::to ( " /ergo " ) , " Succ " )
} else {
Flash ::error (
Redirect ::to ( " /ergo " ) ,
" Challenge not found (should be thirty or dozen) " ,
)
}
}
2023-11-02 12:15:10 +01:00
#[ get( " / " ) ]
2023-11-03 14:20:58 +01:00
async fn index ( db : & State < SqlitePool > , user : User , flash : Option < FlashMessage < '_ > > ) -> Template {
2024-10-19 22:02:44 +02:00
let mut context = Context ::new ( ) ;
if let Some ( msg ) = flash {
context . insert ( " flash " , & msg . into_inner ( ) ) ;
}
context . insert ( " loggedin_user " , & UserWithDetails ::from_user ( user . clone ( ) , db ) . await ) ;
if ! user . has_role ( db , " ergo " ) . await {
return Template ::render (
" ergo/missing-data " ,
context . into_json ( )
) ;
}
2023-11-02 12:15:10 +01:00
let users = User ::ergo ( db ) . await ;
let thirty = sqlx ::query_as! (
ErgoStat ,
2023-11-07 21:27:40 +01:00
" SELECT id, name, dirty_thirty as result, dob, weight, sex FROM user WHERE deleted = 0 AND dirty_thirty is not null ORDER BY result DESC "
2023-11-02 12:15:10 +01:00
)
. fetch_all ( db . inner ( ) )
. await
. unwrap ( ) ;
let dozen = sqlx ::query_as! (
ErgoStat ,
2023-11-07 21:27:40 +01:00
" SELECT id, name, dirty_dozen as result, dob, weight, sex FROM user WHERE deleted = 0 AND dirty_dozen is not null ORDER BY result DESC "
2023-11-02 12:15:10 +01:00
)
. fetch_all ( db . inner ( ) )
. await
. unwrap ( ) ;
context . insert ( " users " , & users ) ;
context . insert ( " thirty " , & thirty ) ;
context . insert ( " dozen " , & dozen ) ;
2024-10-19 22:02:44 +02:00
Template ::render ( " ergo/index " , context . into_json ( ) )
}
#[ derive(FromForm, Debug) ]
pub struct UserAdd {
birthyear : i32 ,
weight : i64 ,
sex : String ,
}
#[ post( " /set-data " , data = " <data> " ) ]
async fn new_user (
db : & State < SqlitePool > ,
data : Form < UserAdd > ,
user : User ,
) -> Flash < Redirect > {
if user . has_role ( db , " ergo " ) . await {
return
Flash ::error ( Redirect ::to ( " /ergo " ) , " Du hast deine Daten schon eingegeben. Wenn du sie updaten willst, melde dich bitte bei it@rudernlinz.at " ) ;
}
// check data
if data . birthyear < 1900 | | data . birthyear > chrono ::Utc ::now ( ) . year ( ) - 5 {
return
Flash ::error ( Redirect ::to ( " /ergo " ) , " Bitte überprüfe dein Geburtsjahr... " ) ;
}
if data . weight < 20 | | data . weight > 200 {
return
Flash ::error ( Redirect ::to ( " /ergo " ) , " Bitte überprüfe dein Gewicht... " ) ;
}
if & data . sex ! = " f " & & & data . sex ! = " m " {
return
Flash ::error ( Redirect ::to ( " /ergo " ) , " Bitte überprüfe dein Geschlecht... " ) ;
}
// set data
user . update_ergo ( db , data . birthyear , data . weight , & data . sex ) . await ;
// inform all other `ergo` users
let ergo = Role ::find_by_name ( db , " ergo " ) . await . unwrap ( ) ;
Notification ::create_for_role ( db , & ergo , & format! ( " {} nimmt heuer an der Ergochallenge teil 💪 " , user . name ) , " Ergo-Challenge " , None , None ) . await ;
// add to `ergo` group
user . add_role ( db , & ergo ) . await . unwrap ( ) ;
Flash ::success ( Redirect ::to ( " /ergo " ) , " Du hast deine Daten erfolgreich eingegeben. Viel Spaß beim Schwitzen :-) " )
2023-11-02 12:15:10 +01:00
}
#[ derive(FromForm, Debug) ]
pub struct ErgoToAdd < ' a > {
user : i64 ,
result : String ,
proof : TempFile < ' a > ,
}
#[ post( " /thirty " , data = " <data> " , format = " multipart/form-data " ) ]
async fn new_thirty (
db : & State < SqlitePool > ,
mut data : Form < ErgoToAdd < '_ > > ,
2023-11-03 14:20:58 +01:00
created_by : User ,
2023-11-02 12:15:10 +01:00
) -> Flash < Redirect > {
let user = User ::find_by_id ( db , data . user as i32 ) . await . unwrap ( ) ;
let extension = if data . proof . content_type ( ) = = Some ( & ContentType ::JPEG ) {
" jpg "
} else {
return Flash ::error ( Redirect ::to ( " /ergo " ) , " Es werden nur JPG Bilder akzeptiert " ) ;
} ;
let base_dir = env ::current_dir ( ) . unwrap ( ) ;
2023-11-12 21:28:06 +01:00
let file_path = base_dir . join ( format! (
" data-ergo/thirty/{}_{}.{extension} " ,
user . name ,
Utc ::now ( )
) ) ;
2023-11-02 12:15:10 +01:00
if let Err ( e ) = data . proof . move_copy_to ( file_path ) . await {
eprintln! ( " Failed to persist file: {:?} " , e ) ;
}
sqlx ::query! (
" UPDATE user SET dirty_thirty = ? where id = ? " ,
data . result ,
data . user
)
. execute ( db . inner ( ) )
. await
. unwrap ( ) ; //Okay, because we can only create a User of a valid id
Log ::create (
db ,
2023-11-03 14:20:58 +01:00
format! ( " {} created thirty-ergo entry: {data:?} " , created_by . name ) ,
2023-11-02 12:15:10 +01:00
)
. await ;
2024-10-19 22:02:44 +02:00
let ergo = Role ::find_by_name ( db , " ergo " ) . await . unwrap ( ) ;
Notification ::create_for_role ( db , & ergo , & format! ( " {} ist gerade die Dirty Thirty Challenge gefahren 🥵 " , user . name ) , " Ergo-Challenge " , Some ( " /ergo " ) , None ) . await ;
2023-11-02 12:15:10 +01:00
Flash ::success ( Redirect ::to ( " /ergo " ) , " Erfolgreich eingetragen " )
}
#[ post( " /dozen " , data = " <data> " , format = " multipart/form-data " ) ]
async fn new_dozen (
db : & State < SqlitePool > ,
mut data : Form < ErgoToAdd < '_ > > ,
2023-11-03 14:20:58 +01:00
created_by : User ,
2023-11-02 12:15:10 +01:00
) -> Flash < Redirect > {
let user = User ::find_by_id ( db , data . user as i32 ) . await . unwrap ( ) ;
let extension = if data . proof . content_type ( ) = = Some ( & ContentType ::JPEG ) {
" jpg "
} else {
return Flash ::error ( Redirect ::to ( " /ergo " ) , " Es werden nur JPG Bilder akzeptiert " ) ;
} ;
let base_dir = env ::current_dir ( ) . unwrap ( ) ;
2023-11-12 21:28:06 +01:00
let file_path = base_dir . join ( format! (
" data-ergo/dozen/{}_{}.{extension} " ,
user . name ,
Utc ::now ( )
) ) ;
2023-11-02 12:15:10 +01:00
if let Err ( e ) = data . proof . move_copy_to ( file_path ) . await {
eprintln! ( " Failed to persist file: {:?} " , e ) ;
}
sqlx ::query! (
" UPDATE user SET dirty_dozen = ? where id = ? " ,
data . result ,
data . user
)
. execute ( db . inner ( ) )
. await
. unwrap ( ) ; //Okay, because we can only create a User of a valid id
Log ::create (
db ,
2023-11-03 14:20:58 +01:00
format! ( " {} created dozen-ergo entry: {data:?} " , created_by . name ) ,
2023-11-02 12:15:10 +01:00
)
. await ;
2024-10-19 22:02:44 +02:00
let ergo = Role ::find_by_name ( db , " ergo " ) . await . unwrap ( ) ;
Notification ::create_for_role ( db , & ergo , & format! ( " {} ist gerade die Dirty Dozen Challenge gefahren 🥵 " , user . name ) , " Ergo-Challenge " , Some ( " /ergo " ) , None ) . await ;
2023-11-02 12:15:10 +01:00
Flash ::success ( Redirect ::to ( " /ergo " ) , " Erfolgreich eingetragen " )
}
pub fn routes ( ) -> Vec < Route > {
2024-10-19 22:02:44 +02:00
routes! [ index , new_thirty , new_dozen , send , reset , update , new_user ]
2023-11-02 12:15:10 +01:00
}
#[ cfg(test) ]
mod test { }