Merge branch 'ergo' into 'staging'
Ergo See merge request PhilippHofer/rot!62
This commit is contained in:
commit
07c9ac8a31
@ -42,7 +42,7 @@ deploy-staging:
|
|||||||
- scp -r templates $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
|
- scp -r templates $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
|
||||||
- scp -r svelte $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
|
- scp -r svelte $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
|
||||||
- ssh $SSH_USER@$SSH_HOST 'sudo systemctl stop rotstaging'
|
- ssh $SSH_USER@$SSH_HOST 'sudo systemctl stop rotstaging'
|
||||||
- ssh $SSH_USER@$SSH_HOST 'rm /home/k004373/rowing-staging/db.sqlite && cp /home/k004373/rowing/db.sqlite /home/k004373/rowing-staging/db.sqlite && mkdir -p /home/k004373/rowing-staging/svelte/build && sqlite3 /home/k004373/rowing-staging/db.sqlite < /home/k004373/rowing-staging/staging-diff.sql'
|
- ssh $SSH_USER@$SSH_HOST 'rm /home/k004373/rowing-staging/db.sqlite && cp /home/k004373/rowing/db.sqlite /home/k004373/rowing-staging/db.sqlite && mkdir -p /home/k004373/rowing-staging/svelte/build && mkdir -p /home/k004373/rowing-staging/data-ergo/thirty && mkdir -p /home/k004373/rowing-staging/data-ergo/dozen && && sqlite3 /home/k004373/rowing-staging/db.sqlite < /home/k004373/rowing-staging/staging-diff.sql'
|
||||||
- ssh $SSH_USER@$SSH_HOST 'mv /home/k004373/rowing-staging/rot-updating /home/k004373/rowing-staging/rot'
|
- ssh $SSH_USER@$SSH_HOST 'mv /home/k004373/rowing-staging/rot-updating /home/k004373/rowing-staging/rot'
|
||||||
- ssh $SSH_USER@$SSH_HOST 'sudo systemctl start rotstaging'
|
- ssh $SSH_USER@$SSH_HOST 'sudo systemctl start rotstaging'
|
||||||
only:
|
only:
|
||||||
@ -62,7 +62,7 @@ deploy-main:
|
|||||||
- scp -r static $SSH_USER@$SSH_HOST:/home/k004373/rowing/
|
- scp -r static $SSH_USER@$SSH_HOST:/home/k004373/rowing/
|
||||||
- scp -r templates $SSH_USER@$SSH_HOST:/home/k004373/rowing/
|
- scp -r templates $SSH_USER@$SSH_HOST:/home/k004373/rowing/
|
||||||
- scp -r svelte $SSH_USER@$SSH_HOST:/home/k004373/rowing/
|
- scp -r svelte $SSH_USER@$SSH_HOST:/home/k004373/rowing/
|
||||||
- ssh $SSH_USER@$SSH_HOST 'mkdir -p /home/k004373/rowing/svelte/build'
|
- ssh $SSH_USER@$SSH_HOST 'mkdir -p /home/k004373/rowing/svelte/build && mkdir -p /home/k004373/rowing-staging/data-ergo/thirty && mkdir -p /home/k004373/rowing-staging/data-ergo/dozen'
|
||||||
- ssh $SSH_USER@$SSH_HOST 'sudo systemctl stop rot'
|
- ssh $SSH_USER@$SSH_HOST 'sudo systemctl stop rot'
|
||||||
- ssh $SSH_USER@$SSH_HOST 'mv /home/k004373/rowing/rot-updating /home/k004373/rowing/rot'
|
- ssh $SSH_USER@$SSH_HOST 'mv /home/k004373/rowing/rot-updating /home/k004373/rowing/rot'
|
||||||
- ssh $SSH_USER@$SSH_HOST 'sudo systemctl start rot'
|
- ssh $SSH_USER@$SSH_HOST 'sudo systemctl start rot'
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
[default]
|
[default]
|
||||||
secret_key = "/NtVGizglEoyoxBLzsRDWTy4oAG1qDw4J4O+CWJSv+fypD7W9sam8hUY4j90EZsbZk8wEradS5zBoWtWKi3k8w=="
|
secret_key = "/NtVGizglEoyoxBLzsRDWTy4oAG1qDw4J4O+CWJSv+fypD7W9sam8hUY4j90EZsbZk8wEradS5zBoWtWKi3k8w=="
|
||||||
rss_key = "rss-key-for-ci"
|
rss_key = "rss-key-for-ci"
|
||||||
|
limits = { file = "10 MiB"}
|
||||||
|
BIN
db.sqlite.bkp
BIN
db.sqlite.bkp
Binary file not shown.
@ -7,7 +7,12 @@ CREATE TABLE IF NOT EXISTS "user" (
|
|||||||
"is_guest" boolean NOT NULL DEFAULT TRUE,
|
"is_guest" boolean NOT NULL DEFAULT TRUE,
|
||||||
"is_tech" boolean NOT NULL DEFAULT FALSE,
|
"is_tech" boolean NOT NULL DEFAULT FALSE,
|
||||||
"deleted" boolean NOT NULL DEFAULT FALSE,
|
"deleted" boolean NOT NULL DEFAULT FALSE,
|
||||||
"last_access" DATETIME
|
"last_access" DATETIME,
|
||||||
|
"dob" text,
|
||||||
|
"weight" text,
|
||||||
|
"sex" text,
|
||||||
|
"dirty_thirty" text,
|
||||||
|
"dirty_dozen" text
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS "trip_type" (
|
CREATE TABLE IF NOT EXISTS "trip_type" (
|
||||||
@ -115,4 +120,3 @@ CREATE TABLE IF NOT EXISTS "boat_damage" (
|
|||||||
"verified_at" datetime,
|
"verified_at" datetime,
|
||||||
"lock_boat" boolean not null default false -- if true: noone can use the boat
|
"lock_boat" boolean not null default false -- if true: noone can use the boat
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -389,7 +389,7 @@ ORDER BY departure DESC
|
|||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.id
|
.id
|
||||||
.unwrap()
|
.unwrap() as i32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn home(
|
pub async fn home(
|
||||||
|
@ -14,7 +14,7 @@ impl Rower {
|
|||||||
sqlx::query_as!(
|
sqlx::query_as!(
|
||||||
User,
|
User,
|
||||||
"
|
"
|
||||||
SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech
|
SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech, dob, weight, sex
|
||||||
FROM user
|
FROM user
|
||||||
WHERE id in (SELECT rower_id FROM rower WHERE logbook_id=?)
|
WHERE id in (SELECT rower_id FROM rower WHERE logbook_id=?)
|
||||||
",
|
",
|
||||||
|
@ -14,6 +14,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
|
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
|
||||||
|
|
||||||
use super::{log::Log, tripdetails::TripDetails, Day};
|
use super::{log::Log, tripdetails::TripDetails, Day};
|
||||||
|
use crate::tera::admin::user::UserEditForm;
|
||||||
|
|
||||||
#[derive(FromRow, Debug, Serialize, Deserialize)]
|
#[derive(FromRow, Debug, Serialize, Deserialize)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
@ -24,6 +25,9 @@ pub struct User {
|
|||||||
pub is_admin: bool,
|
pub is_admin: bool,
|
||||||
pub is_guest: bool,
|
pub is_guest: bool,
|
||||||
pub is_tech: bool,
|
pub is_tech: bool,
|
||||||
|
pub dob: Option<String>,
|
||||||
|
pub weight: Option<String>,
|
||||||
|
pub sex: Option<String>,
|
||||||
pub deleted: bool,
|
pub deleted: bool,
|
||||||
pub last_access: Option<chrono::NaiveDateTime>,
|
pub last_access: Option<chrono::NaiveDateTime>,
|
||||||
}
|
}
|
||||||
@ -92,7 +96,7 @@ impl User {
|
|||||||
sqlx::query_as!(
|
sqlx::query_as!(
|
||||||
Self,
|
Self,
|
||||||
"
|
"
|
||||||
SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech
|
SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech, dob, weight, sex
|
||||||
FROM user
|
FROM user
|
||||||
WHERE id like ?
|
WHERE id like ?
|
||||||
",
|
",
|
||||||
@ -107,7 +111,7 @@ WHERE id like ?
|
|||||||
sqlx::query_as!(
|
sqlx::query_as!(
|
||||||
Self,
|
Self,
|
||||||
"
|
"
|
||||||
SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech
|
SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech, dob, weight, sex
|
||||||
FROM user
|
FROM user
|
||||||
WHERE id like ?
|
WHERE id like ?
|
||||||
",
|
",
|
||||||
@ -122,7 +126,7 @@ WHERE id like ?
|
|||||||
sqlx::query_as!(
|
sqlx::query_as!(
|
||||||
Self,
|
Self,
|
||||||
"
|
"
|
||||||
SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech
|
SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech, dob, weight, sex
|
||||||
FROM user
|
FROM user
|
||||||
WHERE name like ?
|
WHERE name like ?
|
||||||
",
|
",
|
||||||
@ -164,7 +168,7 @@ WHERE name like ?
|
|||||||
sqlx::query_as!(
|
sqlx::query_as!(
|
||||||
Self,
|
Self,
|
||||||
"
|
"
|
||||||
SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech
|
SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech, dob, weight, sex
|
||||||
FROM user
|
FROM user
|
||||||
WHERE deleted = 0
|
WHERE deleted = 0
|
||||||
ORDER BY last_access DESC
|
ORDER BY last_access DESC
|
||||||
@ -175,11 +179,26 @@ ORDER BY last_access DESC
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn ergo(db: &SqlitePool) -> Vec<Self> {
|
||||||
|
sqlx::query_as!(
|
||||||
|
Self,
|
||||||
|
"
|
||||||
|
SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech, dob, weight, sex
|
||||||
|
FROM user
|
||||||
|
WHERE deleted = 0 AND dob is not null and weight is not null and sex is not null
|
||||||
|
ORDER BY last_access DESC
|
||||||
|
"
|
||||||
|
)
|
||||||
|
.fetch_all(db)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn cox(db: &SqlitePool) -> Vec<Self> {
|
pub async fn cox(db: &SqlitePool) -> Vec<Self> {
|
||||||
sqlx::query_as!(
|
sqlx::query_as!(
|
||||||
Self,
|
Self,
|
||||||
"
|
"
|
||||||
SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech
|
SELECT id, name, pw, is_cox, is_admin, is_guest, deleted, last_access, is_tech, dob, weight, sex
|
||||||
FROM user
|
FROM user
|
||||||
WHERE deleted = 0 AND is_cox=true
|
WHERE deleted = 0 AND is_cox=true
|
||||||
ORDER BY last_access DESC
|
ORDER BY last_access DESC
|
||||||
@ -201,20 +220,16 @@ ORDER BY last_access DESC
|
|||||||
.is_ok()
|
.is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update(
|
pub async fn update(&self, db: &SqlitePool, data: UserEditForm) {
|
||||||
&self,
|
|
||||||
db: &SqlitePool,
|
|
||||||
is_cox: bool,
|
|
||||||
is_admin: bool,
|
|
||||||
is_guest: bool,
|
|
||||||
is_tech: bool,
|
|
||||||
) {
|
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
"UPDATE user SET is_cox = ?, is_admin = ?, is_guest = ?, is_tech = ? where id = ?",
|
"UPDATE user SET is_cox = ?, is_admin = ?, is_guest = ?, is_tech = ?, dob = ?, weight = ?, sex = ? where id = ?",
|
||||||
is_cox,
|
data.is_cox,
|
||||||
is_admin,
|
data.is_admin,
|
||||||
is_guest,
|
data.is_guest,
|
||||||
is_tech,
|
data.is_tech,
|
||||||
|
data.dob,
|
||||||
|
data.weight,
|
||||||
|
data.sex,
|
||||||
self.id
|
self.id
|
||||||
)
|
)
|
||||||
.execute(db)
|
.execute(db)
|
||||||
@ -515,7 +530,7 @@ impl<'r> FromRequest<'r> for NonGuestUser {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::testdb;
|
use crate::{tera::admin::user::UserEditForm, testdb};
|
||||||
|
|
||||||
use super::User;
|
use super::User;
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
@ -584,7 +599,20 @@ mod test {
|
|||||||
let pool = testdb!();
|
let pool = testdb!();
|
||||||
|
|
||||||
let user = User::find_by_id(&pool, 1).await.unwrap();
|
let user = User::find_by_id(&pool, 1).await.unwrap();
|
||||||
user.update(&pool, false, false, false, false).await;
|
user.update(
|
||||||
|
&pool,
|
||||||
|
UserEditForm {
|
||||||
|
id: 1,
|
||||||
|
is_guest: false,
|
||||||
|
is_cox: false,
|
||||||
|
is_admin: false,
|
||||||
|
is_tech: false,
|
||||||
|
dob: None,
|
||||||
|
weight: None,
|
||||||
|
sex: None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
let user = User::find_by_id(&pool, 1).await.unwrap();
|
let user = User::find_by_id(&pool, 1).await.unwrap();
|
||||||
assert_eq!(user.is_admin, false);
|
assert_eq!(user.is_admin, false);
|
||||||
|
@ -58,12 +58,15 @@ async fn delete(db: &State<SqlitePool>, _admin: AdminUser, user: i32) -> Flash<R
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromForm)]
|
#[derive(FromForm)]
|
||||||
struct UserEditForm {
|
pub struct UserEditForm {
|
||||||
id: i32,
|
pub(crate) id: i32,
|
||||||
is_guest: bool,
|
pub(crate) is_guest: bool,
|
||||||
is_cox: bool,
|
pub(crate) is_cox: bool,
|
||||||
is_admin: bool,
|
pub(crate) is_admin: bool,
|
||||||
is_tech: bool,
|
pub(crate) is_tech: bool,
|
||||||
|
pub(crate) dob: Option<String>,
|
||||||
|
pub(crate) weight: Option<String>,
|
||||||
|
pub(crate) sex: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/user", data = "<data>")]
|
#[post("/user", data = "<data>")]
|
||||||
@ -80,8 +83,7 @@ async fn update(
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
user.update(db, data.is_cox, data.is_admin, data.is_guest, data.is_tech)
|
user.update(db, data.into_inner()).await;
|
||||||
.await;
|
|
||||||
|
|
||||||
Flash::success(Redirect::to("/admin/user"), "Successfully updated user")
|
Flash::success(Redirect::to("/admin/user"), "Successfully updated user")
|
||||||
}
|
}
|
||||||
|
191
src/tera/ergo.rs
Normal file
191
src/tera/ergo.rs
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
use std::env;
|
||||||
|
|
||||||
|
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::{
|
||||||
|
log::Log,
|
||||||
|
user::{AdminUser, NonGuestUser, User},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct ErgoStat {
|
||||||
|
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,
|
||||||
|
"SELECT name, dirty_thirty as result, dob, weight, sex FROM user WHERE deleted = 0 AND dirty_thirty is not null ORDER BY result DESC"
|
||||||
|
)
|
||||||
|
.fetch_all(db.inner())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let dozen= sqlx::query_as!(
|
||||||
|
ErgoStat,
|
||||||
|
"SELECT name, dirty_dozen as result, dob, weight, sex FROM user WHERE deleted = 0 AND dirty_dozen is not null ORDER BY result DESC"
|
||||||
|
)
|
||||||
|
.fetch_all(db.inner())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Template::render(
|
||||||
|
"ergo.final",
|
||||||
|
context!(loggedin_user: &_user.user, thirty, dozen),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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!)",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
async fn index(
|
||||||
|
db: &State<SqlitePool>,
|
||||||
|
user: NonGuestUser,
|
||||||
|
flash: Option<FlashMessage<'_>>,
|
||||||
|
) -> Template {
|
||||||
|
let users = User::ergo(db).await;
|
||||||
|
|
||||||
|
let thirty = sqlx::query_as!(
|
||||||
|
ErgoStat,
|
||||||
|
"SELECT name, dirty_thirty as result, dob, weight, sex FROM user WHERE deleted = 0 AND dirty_thirty is not null ORDER BY result DESC"
|
||||||
|
)
|
||||||
|
.fetch_all(db.inner())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let dozen= sqlx::query_as!(
|
||||||
|
ErgoStat,
|
||||||
|
"SELECT name, dirty_dozen as result, dob, weight, sex FROM user WHERE deleted = 0 AND dirty_dozen is not null ORDER BY result DESC"
|
||||||
|
)
|
||||||
|
.fetch_all(db.inner())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut context = Context::new();
|
||||||
|
if let Some(msg) = flash {
|
||||||
|
context.insert("flash", &msg.into_inner());
|
||||||
|
}
|
||||||
|
context.insert("loggedin_user", &user.user);
|
||||||
|
context.insert("users", &users);
|
||||||
|
context.insert("thirty", &thirty);
|
||||||
|
context.insert("dozen", &dozen);
|
||||||
|
|
||||||
|
Template::render("ergo", context.into_json())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<'_>>,
|
||||||
|
created_by: NonGuestUser,
|
||||||
|
) -> 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();
|
||||||
|
let file_path = base_dir.join(format!("data-ergo/thirty/{}.{extension}", user.name));
|
||||||
|
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,
|
||||||
|
format!("{created_by:?} created thirty-ergo entry: {data:?}"),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
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<'_>>,
|
||||||
|
created_by: NonGuestUser,
|
||||||
|
) -> 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();
|
||||||
|
let file_path = base_dir.join(format!("data-ergo/dozen/{}.{extension}", user.name));
|
||||||
|
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,
|
||||||
|
format!("{created_by:?} created dozen-ergo entry: {data:?}"),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Flash::success(Redirect::to("/ergo"), "Erfolgreich eingetragen")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn routes() -> Vec<Route> {
|
||||||
|
routes![index, new_thirty, new_dozen, send, reset]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {}
|
@ -20,10 +20,11 @@ use crate::model::{
|
|||||||
usertrip::{UserTrip, UserTripDeleteError, UserTripError},
|
usertrip::{UserTrip, UserTripDeleteError, UserTripError},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod admin;
|
pub(crate) mod admin;
|
||||||
mod auth;
|
mod auth;
|
||||||
mod boatdamage;
|
mod boatdamage;
|
||||||
mod cox;
|
mod cox;
|
||||||
|
mod ergo;
|
||||||
mod log;
|
mod log;
|
||||||
mod misc;
|
mod misc;
|
||||||
mod stat;
|
mod stat;
|
||||||
@ -56,6 +57,7 @@ async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_
|
|||||||
if let Some(msg) = flash {
|
if let Some(msg) = flash {
|
||||||
context.insert("flash", &msg.into_inner());
|
context.insert("flash", &msg.into_inner());
|
||||||
}
|
}
|
||||||
|
println!("{user:#?}");
|
||||||
context.insert("loggedin_user", &user);
|
context.insert("loggedin_user", &user);
|
||||||
context.insert("days", &days);
|
context.insert("days", &days);
|
||||||
Template::render("index", context.into_json())
|
Template::render("index", context.into_json())
|
||||||
@ -212,6 +214,7 @@ pub fn config(rocket: Rocket<Build>) -> Rocket<Build> {
|
|||||||
.mount("/auth", auth::routes())
|
.mount("/auth", auth::routes())
|
||||||
.mount("/wikiauth", routes![wikiauth])
|
.mount("/wikiauth", routes![wikiauth])
|
||||||
.mount("/log", log::routes())
|
.mount("/log", log::routes())
|
||||||
|
.mount("/ergo", ergo::routes())
|
||||||
.mount("/stat", stat::routes())
|
.mount("/stat", stat::routes())
|
||||||
.mount("/boatdamage", boatdamage::routes())
|
.mount("/boatdamage", boatdamage::routes())
|
||||||
.mount("/cox", cox::routes())
|
.mount("/cox", cox::routes())
|
||||||
|
@ -18,3 +18,13 @@ DROP TABLE logbook;
|
|||||||
ALTER TABLE logbook_temp RENAME TO logbook;
|
ALTER TABLE logbook_temp RENAME TO logbook;
|
||||||
|
|
||||||
INSERT INTO rower(rower_id, logbook_id) SELECT shipmaster, id FROM logbook;
|
INSERT INTO rower(rower_id, logbook_id) SELECT shipmaster, id FROM logbook;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- tmp ergo challenge stuff
|
||||||
|
ALTER TABLE user ADD COLUMN dob text;
|
||||||
|
ALTER TABLE user ADD COLUMN weight text;
|
||||||
|
ALTER TABLE user ADD COLUMN sex text;
|
||||||
|
ALTER TABLE user ADD COLUMN dirty_thirty text;
|
||||||
|
ALTER TABLE user ADD COLUMN dirty_dozen text;
|
||||||
|
|
||||||
|
@ -54,6 +54,9 @@
|
|||||||
{{ macros::checkbox(label='Steuerberechtigter', name='is_cox', id=loop.index , checked=user.is_cox) }}
|
{{ macros::checkbox(label='Steuerberechtigter', name='is_cox', id=loop.index , checked=user.is_cox) }}
|
||||||
{{ macros::checkbox(label='Technical', name='is_tech', id=loop.index , checked=user.is_tech) }}
|
{{ macros::checkbox(label='Technical', name='is_tech', id=loop.index , checked=user.is_tech) }}
|
||||||
{{ macros::checkbox(label='Admin', name='is_admin', id=loop.index , checked=user.is_admin) }}
|
{{ macros::checkbox(label='Admin', name='is_admin', id=loop.index , checked=user.is_admin) }}
|
||||||
|
{{ macros::input(label='DOB', name='dob', id=loop.index, type="text", value=user.dob) }}
|
||||||
|
{{ macros::input(label='Weight (kg)', name='weight', id=loop.index, type="text", value=user.weight) }}
|
||||||
|
{{ macros::input(label='Sex', name='sex', id=loop.index, type="text", value=user.sex) }}
|
||||||
</div>
|
</div>
|
||||||
{% if user.pw %}
|
{% if user.pw %}
|
||||||
<a class="inline-block mt-1 text-primary-600 hover:text-primary-900 underline" href="/admin/user/{{ user.id }}/reset-pw">Passwort zurücksetzen</a>
|
<a class="inline-block mt-1 text-primary-600 hover:text-primary-900 underline" href="/admin/user/{{ user.id }}/reset-pw">Passwort zurücksetzen</a>
|
||||||
|
31
templates/ergo.final.html.tera
Normal file
31
templates/ergo.final.html.tera
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{% import "includes/macros" as macros %}
|
||||||
|
|
||||||
|
{% extends "base" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="max-w-screen-lg w-full">
|
||||||
|
{% if flash %}
|
||||||
|
{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<h1 class="h1">Aktuelle Woche</h1>
|
||||||
|
<details>
|
||||||
|
<summary>Dirty Thirty</summary>
|
||||||
|
<p>
|
||||||
|
<div class="border-r border-l">{% for stat in thirty %}{{ stat.name }}	{{ stat.dob }}	{{ stat.weight }}	{{ stat.sex }}		Donau Linz	{{ stat.result }}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Dirty Dozen</summary>
|
||||||
|
<p>
|
||||||
|
<div class="border-r border-l">{% for stat in dozen %}{{ stat.name }}	{{ stat.dob }}	{{ stat.weight }}	{{ stat.sex }}		Donau Linz	{{ stat.result }}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock content%}
|
80
templates/ergo.html.tera
Normal file
80
templates/ergo.html.tera
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
{% import "includes/macros" as macros %}
|
||||||
|
|
||||||
|
{% extends "base" %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="max-w-screen-lg w-full">
|
||||||
|
{% if flash %}
|
||||||
|
{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}
|
||||||
|
{% endif %}
|
||||||
|
<h1 class="h1">Neuer Eintrag</h1>
|
||||||
|
<details>
|
||||||
|
<summary>Dirty Thirty</summary>
|
||||||
|
<p>
|
||||||
|
<div class="border-r border-l">
|
||||||
|
<form action="/ergo/thirty" method="post" enctype="multipart/form-data">
|
||||||
|
<label for="user-thirty" class="text-sm text-gray-600">Ergo-Fahrer</label>
|
||||||
|
<select name="user" id="user-thirty" class="input">
|
||||||
|
{% for user in users %}
|
||||||
|
<option value="{{ user.id }}">{{ user.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
{{ macros::input(label="Zeit [(hh:)mm:ss]/Distanz [m]", name="result", required=true, type="text", class="input") }}
|
||||||
|
<input type="file" name="proof" class="input">
|
||||||
|
<input type="submit" value="Speichern" class="btn btn-primary w-full col-span-4 m-auto"/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Dirty Dozen</summary>
|
||||||
|
<p>
|
||||||
|
<div class="border-r border-l">
|
||||||
|
<form action="/ergo/dozen" method="post" enctype="multipart/form-data">
|
||||||
|
<label for="user-dozen" class="text-sm text-gray-600">Ergo-Fahrer</label>
|
||||||
|
<select name="user" id="user-dozen" class="input">
|
||||||
|
<option disabled="disabled">User auswählen</option>
|
||||||
|
{% for user in users %}
|
||||||
|
<option value="{{ user.id }}">{{ user.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
{{ macros::input(label="Zeit [(hh:)mm:ss]/Distanz [m]", name="result", required=true, type="text", class="input") }}
|
||||||
|
<input type="file" name="proof" class="input">
|
||||||
|
<input type="submit" value="Speichern" class="btn btn-primary w-full col-span-4 m-auto"/>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<h1 class="h1">Aktuelle Woche</h1>
|
||||||
|
<details>
|
||||||
|
<summary>Dirty Thirty</summary>
|
||||||
|
<p>
|
||||||
|
<div class="border-r border-l">
|
||||||
|
<ol>
|
||||||
|
{% for stat in thirty %}
|
||||||
|
<li>{{ stat.name }}: {{ stat.result }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Dirty Dozen</summary>
|
||||||
|
<p>
|
||||||
|
<div class="border-r border-l">
|
||||||
|
<ol>
|
||||||
|
{% for stat in dozen%}
|
||||||
|
<li>{{ stat.name }}: {{ stat.result }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock content%}
|
@ -1,70 +1,129 @@
|
|||||||
{% macro header(loggedin_user) %}
|
{% macro header(loggedin_user) %}
|
||||||
<header class="bg-primary-900 text-white flex justify-center p-3 fixed w-full z-10">
|
<header
|
||||||
<div class="max-w-screen-xl w-full flex justify-between items-center">
|
class="bg-primary-900 text-white flex justify-center p-3 fixed w-full z-10"
|
||||||
<div class="w-1/3 truncate">
|
>
|
||||||
<a href="/">
|
<div class="max-w-screen-xl w-full flex justify-between items-center">
|
||||||
Hü
|
<div class="w-1/3 truncate">
|
||||||
{{ loggedin_user.name }}
|
<a href="/">
|
||||||
</a>
|
Hü
|
||||||
</div>
|
{{ loggedin_user.name }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<a href="https://wiki.rudernlinz.at/ruderassistent#faq" target="_blank" class="inline-flex justify-center rounded-md bg-primary-600 mx-1 px-3 py-2 text-sm font-semibold text-white hover:bg-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 cursor-pointer">
|
<a
|
||||||
{% include "includes/question-icon" %}
|
href="https://wiki.rudernlinz.at/ruderassistent#faq"
|
||||||
<span class="sr-only">FAQs</span>
|
target="_blank"
|
||||||
</a>
|
class="inline-flex justify-center rounded-md bg-primary-600 mx-1 px-3 py-2 text-sm font-semibold text-white hover:bg-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 cursor-pointer"
|
||||||
{% if not loggedin_user.is_guest %}
|
>
|
||||||
<a href="#" class="inline-flex justify-center rounded-md bg-primary-600 mx-1 px-3 py-2 text-sm font-semibold text-white hover:bg-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 cursor-pointer"
|
{% include "includes/question-icon" %}
|
||||||
data-sidebar="true" data-trigger="sidebar" data-header="Logbuch" data-body="#mobile-menu">
|
<span class="sr-only">FAQs</span>
|
||||||
{% include "includes/book" %}
|
</a>
|
||||||
<span class="sr-only">Logbuch</span>
|
{% if not loggedin_user.is_guest %}
|
||||||
</a>
|
<a
|
||||||
<div class="hidden">
|
href="#"
|
||||||
<div id="mobile-menu">
|
class="inline-flex justify-center rounded-md bg-primary-600 mx-1 px-3 py-2 text-sm font-semibold text-white hover:bg-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 cursor-pointer"
|
||||||
<a href="/log" class="block w-100 py-2 hover:text-primary-600">
|
data-sidebar="true"
|
||||||
Ausfahrt eintragen
|
data-trigger="sidebar"
|
||||||
</a>
|
data-header="Logbuch"
|
||||||
<a href="/log/show" class="block w-100 py-2 hover:text-primary-600 border-t">
|
data-body="#mobile-menu"
|
||||||
Logbuch
|
>
|
||||||
</a>
|
{% include "includes/book" %}
|
||||||
<a href="/stat" class="block w-100 py-2 hover:text-primary-600 border-t">
|
<span class="sr-only">Logbuch</span>
|
||||||
Statistik
|
</a>
|
||||||
</a>
|
<div class="hidden">
|
||||||
<a href="/stat/boats" class="block w-100 py-2 hover:text-primary-600 border-t">
|
<div id="mobile-menu">
|
||||||
Bootsauswertung
|
<a href="/log" class="block w-100 py-2 hover:text-primary-600">
|
||||||
</a>
|
Ausfahrt eintragen
|
||||||
{% if loggedin_user.is_admin %}
|
</a>
|
||||||
<a href="/admin/boat" class="block w-100 py-2 hover:text-primary-600 border-t">
|
<a
|
||||||
Boote
|
href="/log/show"
|
||||||
</a>
|
class="block w-100 py-2 hover:text-primary-600 border-t"
|
||||||
{% endif %}
|
>
|
||||||
<a href="/boatdamage" class="block w-100 py-2 hover:text-primary-600 border-t">
|
Logbuch
|
||||||
Bootsschaden
|
</a>
|
||||||
</a>
|
{% if loggedin_user.weight and loggedin_user.sex and loggedin_user.dob %}
|
||||||
</div>
|
<a
|
||||||
|
href="/ergo"
|
||||||
|
class="block w-100 py-2 hover:text-primary-600 border-t"
|
||||||
|
>
|
||||||
|
Ergo
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
<a
|
||||||
|
href="/stat"
|
||||||
|
class="block w-100 py-2 hover:text-primary-600 border-t"
|
||||||
|
>
|
||||||
|
Statistik
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href="/stat/boats"
|
||||||
|
class="block w-100 py-2 hover:text-primary-600 border-t"
|
||||||
|
>
|
||||||
|
Bootsauswertung
|
||||||
|
</a>
|
||||||
|
{% if loggedin_user.is_admin %}
|
||||||
|
<a
|
||||||
|
href="/admin/boat"
|
||||||
|
class="block w-100 py-2 hover:text-primary-600 border-t"
|
||||||
|
>
|
||||||
|
Boote
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
<a
|
||||||
|
href="/boatdamage"
|
||||||
|
class="block w-100 py-2 hover:text-primary-600 border-t"
|
||||||
|
>
|
||||||
|
Bootsschaden
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
</div>
|
||||||
|
{% endif %} {% if loggedin_user.is_admin %}
|
||||||
{% if loggedin_user.is_admin %}
|
<a
|
||||||
<a href="/admin/user" class="inline-flex justify-center rounded-md bg-primary-600 mx-1 px-3 py-2 text-sm font-semibold text-white hover:bg-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 cursor-pointer">
|
href="/admin/user"
|
||||||
<svg class="inline h-4" width="16" height="16" fill="currentColor" class="bi bi-person-lines-fill" viewbox="0 0 16 16">
|
class="inline-flex justify-center rounded-md bg-primary-600 mx-1 px-3 py-2 text-sm font-semibold text-white hover:bg-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 cursor-pointer"
|
||||||
<path d="M6 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-5 6s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1H1zM11 3.5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5zm.5 2.5a.5.5 0 0 0 0 1h4a.5.5 0 0 0 0-1h-4zm2 3a.5.5 0 0 0 0 1h2a.5.5 0 0 0 0-1h-2zm0 3a.5.5 0 0 0 0 1h2a.5.5 0 0 0 0-1h-2z"/>
|
>
|
||||||
</svg>
|
<svg
|
||||||
<span class="sr-only">Userverwaltung</span>
|
class="inline h-4"
|
||||||
</a>
|
width="16"
|
||||||
{% endif %}
|
height="16"
|
||||||
<a href="/auth/logout" class="inline-flex justify-center rounded-md bg-primary-600 ml-1 px-3 py-2 text-sm font-semibold text-white hover:bg-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 cursor-pointer">
|
fill="currentColor"
|
||||||
<svg class="inline h-4" width="24" height="24" viewbox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-log-out">
|
class="bi bi-person-lines-fill"
|
||||||
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path>
|
viewbox="0 0 16 16"
|
||||||
<polyline points="16 17 21 12 16 7"></polyline>
|
>
|
||||||
<line x1="21" y1="12" x2="9" y2="12"></line>
|
<path
|
||||||
</svg>
|
d="M6 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-5 6s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1H1zM11 3.5a.5.5 0 0 1 .5-.5h4a.5.5 0 0 1 0 1h-4a.5.5 0 0 1-.5-.5zm.5 2.5a.5.5 0 0 0 0 1h4a.5.5 0 0 0 0-1h-4zm2 3a.5.5 0 0 0 0 1h2a.5.5 0 0 0 0-1h-2zm0 3a.5.5 0 0 0 0 1h2a.5.5 0 0 0 0-1h-2z"
|
||||||
<span class="sr-only">Ausloggen</span>
|
/>
|
||||||
</a>
|
</svg>
|
||||||
</div>
|
<span class="sr-only">Userverwaltung</span>
|
||||||
</div>
|
</a>
|
||||||
</header>
|
{% endif %}
|
||||||
<div class="h-8"></div>
|
<a
|
||||||
|
href="/auth/logout"
|
||||||
|
class="inline-flex justify-center rounded-md bg-primary-600 ml-1 px-3 py-2 text-sm font-semibold text-white hover:bg-primary-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 cursor-pointer"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="inline h-4"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewbox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
class="feather feather-log-out"
|
||||||
|
>
|
||||||
|
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path>
|
||||||
|
<polyline points="16 17 21 12 16 7"></polyline>
|
||||||
|
<line x1="21" y1="12" x2="9" y2="12"></line>
|
||||||
|
</svg>
|
||||||
|
<span class="sr-only">Ausloggen</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div class="h-8"></div>
|
||||||
{% endmacro header %}
|
{% endmacro header %}
|
||||||
|
|
||||||
{% macro input(label, name, type, required=false, class='rounded-md', value='', min='', hide_label=false, id='', autofocus=false, wrapper_class='') %}
|
{% macro input(label, name, type, required=false, class='rounded-md', value='', min='', hide_label=false, id='', autofocus=false, wrapper_class='') %}
|
||||||
|
Loading…
Reference in New Issue
Block a user