From 082fac97895835c70cc0e794af56ba096df74392 Mon Sep 17 00:00:00 2001 From: philipp Date: Mon, 24 Jul 2023 20:56:46 +0200 Subject: [PATCH] add stats --- README.md | 4 +--- src/model/logbook.rs | 26 +++++++++++++++++++++ src/model/mod.rs | 1 + src/model/stat.rs | 35 +++++++++++++++++++++++++++++ src/model/user.rs | 23 +++++++++++++++++++ src/tera/log.rs | 2 ++ src/tera/mod.rs | 2 ++ src/tera/stat.rs | 19 ++++++++++++++++ templates/includes/macros.html.tera | 4 ++++ templates/log.html.tera | 19 +++++++++++----- templates/stat.html.tera | 16 +++++++++++++ 11 files changed, 142 insertions(+), 9 deletions(-) create mode 100644 src/model/stat.rs create mode 100644 src/tera/stat.rs create mode 100644 templates/stat.html.tera diff --git a/README.md b/README.md index 8a7c7e0..448a39b 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,7 @@ ## New large features ### Logbuch -- Next: Allow editing of rowers on "Ausfahrt beenden" -- Then - - Allow editing own logbook entries of same day +- Finally - Stats (Personenliste mit Gesamt-KM vom Jahr) ### Guest-Scheckbuch diff --git a/src/model/logbook.rs b/src/model/logbook.rs index 9d9179f..1aa60ef 100644 --- a/src/model/logbook.rs +++ b/src/model/logbook.rs @@ -30,6 +30,7 @@ pub struct LogbookWithBoatAndRowers { pub enum LogbookUpdateError { NotYourEntry, + TooManyRowers(usize, usize), } pub enum LogbookCreateError { @@ -170,6 +171,13 @@ impl Logbook { Ok(()) } + async fn remove_rowers(&self, db: &SqlitePool) { + sqlx::query!("DELETE FROM rower WHERE logbook_id=?", self.id) + .execute(db) + .await + .unwrap(); + } + pub async fn home( &self, db: &SqlitePool, @@ -178,10 +186,21 @@ impl Logbook { distance_in_km: i64, comments: Option, logtype: Option, + rower: Vec, ) -> Result<(), LogbookUpdateError> { if user.id != self.shipmaster { return Err(LogbookUpdateError::NotYourEntry); } + + let boat = Boat::find_by_id(db, self.boat_id as i32).await.unwrap(); //ok + + if rower.len() > boat.amount_seats as usize - 1 { + return Err(LogbookUpdateError::TooManyRowers( + boat.amount_seats as usize, + rower.len() + 1, + )); + } + //TODO: check current date let arrival = format!("{}", chrono::offset::Local::now().format("%Y-%m-%d %H:%M")); @@ -197,6 +216,13 @@ impl Logbook { ) .execute(db) .await.unwrap(); //TODO: fixme + + self.remove_rowers(db).await; + + for rower in &rower { + Rower::create(db, self.id, *rower).await; + } + Ok(()) } diff --git a/src/model/mod.rs b/src/model/mod.rs index de034b9..1aaa7e8 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -14,6 +14,7 @@ pub mod logbook; pub mod logtype; pub mod planned_event; pub mod rower; +pub mod stat; pub mod trip; pub mod tripdetails; pub mod triptype; diff --git a/src/model/stat.rs b/src/model/stat.rs new file mode 100644 index 0000000..c1dee56 --- /dev/null +++ b/src/model/stat.rs @@ -0,0 +1,35 @@ +use serde::Serialize; +use sqlx::{FromRow, SqlitePool}; + +#[derive(FromRow, Serialize, Clone)] +pub struct Stat { + name: String, + rowed_km: i32, +} + +impl Stat { + pub async fn get_rowed_km(db: &SqlitePool) -> Vec { + sqlx::query!( + "SELECT u.name AS name, COALESCE(SUM(distance_in_km), 0) as rowed_km + FROM user u + INNER JOIN ( + SELECT shipmaster AS user_id, distance_in_km + FROM logbook + UNION + SELECT r.rower_id AS user_id, l.distance_in_km + FROM logbook l + INNER JOIN rower r ON r.logbook_id = l.id + ) AS subquery ON u.id = subquery.user_id + GROUP BY u.id ORDER BY rowed_km DESC;" + ) + .fetch_all(db) + .await + .unwrap() + .into_iter() + .map(|row| Stat { + name: row.name, + rowed_km: row.rowed_km.unwrap_or(0), + }) + .collect() + } +} diff --git a/src/model/user.rs b/src/model/user.rs index 5fc4dfe..0100ab6 100644 --- a/src/model/user.rs +++ b/src/model/user.rs @@ -40,6 +40,29 @@ pub enum LoginError { } impl User { + pub async fn rowed_km(&self, db: &SqlitePool) -> i32 { + sqlx::query!( + "SELECT COALESCE(SUM(distance_in_km),0) as rowed_km + FROM ( + SELECT distance_in_km + FROM logbook + WHERE shipmaster = ?1 + UNION + SELECT l.distance_in_km + FROM logbook l + INNER JOIN rower r ON r.logbook_id = l.id + WHERE r.rower_id = ?1 + + );", + self.id, + ) + .fetch_one(db) + .await + .unwrap() + .rowed_km + .unwrap() + } + pub async fn find_by_id(db: &SqlitePool, id: i32) -> Option { sqlx::query_as!( User, diff --git a/src/tera/log.rs b/src/tera/log.rs index bc1f323..f7d6669 100644 --- a/src/tera/log.rs +++ b/src/tera/log.rs @@ -98,6 +98,7 @@ struct LogHomeForm { distance_in_km: i64, comments: Option, logtype: Option, + rower: Vec, } #[post("/", data = "")] @@ -123,6 +124,7 @@ async fn home( data.distance_in_km, data.comments.clone(), //TODO: fixme data.logtype, + data.rower.clone(), //TODO: fixme ) .await { diff --git a/src/tera/mod.rs b/src/tera/mod.rs index 61367f2..fea7fe8 100644 --- a/src/tera/mod.rs +++ b/src/tera/mod.rs @@ -24,6 +24,7 @@ mod auth; mod cox; mod log; mod misc; +mod stat; #[get("/")] async fn index(db: &State, user: User, flash: Option>) -> Template { @@ -116,6 +117,7 @@ pub fn config(rocket: Rocket) -> Rocket { .mount("/", routes![index, join, remove]) .mount("/auth", auth::routes()) .mount("/log", log::routes()) + .mount("/stat", stat::routes()) .mount("/cox", cox::routes()) .mount("/admin", admin::routes()) .mount("/", misc::routes()) diff --git a/src/tera/stat.rs b/src/tera/stat.rs new file mode 100644 index 0000000..dc611ef --- /dev/null +++ b/src/tera/stat.rs @@ -0,0 +1,19 @@ +use rocket::{get, routes, Route, State}; +use rocket_dyn_templates::{context, Template}; +use sqlx::SqlitePool; + +use crate::model::{stat::Stat, user::User}; + +#[get("/")] +async fn index(db: &State, user: User) -> Template { + let stat = Stat::get_rowed_km(db).await; + + Template::render("stat", context!(loggedin_user: &user, stat)) +} + +pub fn routes() -> Vec { + routes![index] +} + +#[cfg(test)] +mod test {} diff --git a/templates/includes/macros.html.tera b/templates/includes/macros.html.tera index f2a9a67..bc24889 100644 --- a/templates/includes/macros.html.tera +++ b/templates/includes/macros.html.tera @@ -13,6 +13,10 @@ FAQs {% if loggedin_user.is_admin %} + + STATS + Logbuch + LOGBUCH Logbuch diff --git a/templates/log.html.tera b/templates/log.html.tera index 8fb8031..a101ca5 100644 --- a/templates/log.html.tera +++ b/templates/log.html.tera @@ -43,16 +43,12 @@ @@ -89,6 +85,17 @@ {{ macros::input(label="Distanz", name="distance_in_km", id="distance_in_km_home", type="number", min=0, value=log.distance_in_km) }} {{ macros::input(label="Kommentar", name="comments", type="text", value=log.comments) }} {{ macros::select(data=logtypes, select_name='logtype', default="Normal", selected_id=log.logtype) }} + {% endif %} diff --git a/templates/stat.html.tera b/templates/stat.html.tera new file mode 100644 index 0000000..19d00c9 --- /dev/null +++ b/templates/stat.html.tera @@ -0,0 +1,16 @@ +{% import "includes/macros" as macros %} + +{% extends "base" %} + +{% block content %} + + +
+

Statstik

+
    + {% for s in stat %} +
  1. {{s.name}}: {{s.rowed_km}}km
  2. + {% endfor %} +
+
+{% endblock content%}