add stats
This commit is contained in:
@@ -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<String>,
|
||||
logtype: Option<i64>,
|
||||
rower: Vec<i64>,
|
||||
) -> 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(())
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
35
src/model/stat.rs
Normal file
35
src/model/stat.rs
Normal file
@@ -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<Stat> {
|
||||
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()
|
||||
}
|
||||
}
|
@@ -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<Self> {
|
||||
sqlx::query_as!(
|
||||
User,
|
||||
|
@@ -98,6 +98,7 @@ struct LogHomeForm {
|
||||
distance_in_km: i64,
|
||||
comments: Option<String>,
|
||||
logtype: Option<i64>,
|
||||
rower: Vec<i64>,
|
||||
}
|
||||
|
||||
#[post("/<logbook_id>", data = "<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
|
||||
{
|
||||
|
@@ -24,6 +24,7 @@ mod auth;
|
||||
mod cox;
|
||||
mod log;
|
||||
mod misc;
|
||||
mod stat;
|
||||
|
||||
#[get("/")]
|
||||
async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_>>) -> Template {
|
||||
@@ -116,6 +117,7 @@ pub fn config(rocket: Rocket<Build>) -> Rocket<Build> {
|
||||
.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())
|
||||
|
19
src/tera/stat.rs
Normal file
19
src/tera/stat.rs
Normal file
@@ -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<SqlitePool>, user: User) -> Template {
|
||||
let stat = Stat::get_rowed_km(db).await;
|
||||
|
||||
Template::render("stat", context!(loggedin_user: &user, stat))
|
||||
}
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
routes![index]
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {}
|
Reference in New Issue
Block a user