show if a user has < 30 km to thousand km trip, Fixes #551 #582

Merged
philipp merged 2 commits from thousand-km-trips into main 2024-06-10 15:50:35 +02:00
4 changed files with 56 additions and 7 deletions
Showing only changes of commit 31bf38f112 - Show all commits

View File

@ -301,7 +301,7 @@ ORDER BY departure DESC
db: &SqlitePool, db: &SqlitePool,
mut log: LogToAdd, mut log: LogToAdd,
created_by_user: &User, created_by_user: &User,
) -> Result<(), LogbookCreateError> { ) -> Result<String, LogbookCreateError> {
let Some(boat) = Boat::find_by_id(db, log.boat_id).await else { let Some(boat) = Boat::find_by_id(db, log.boat_id).await else {
return Err(LogbookCreateError::BoatNotFound); return Err(LogbookCreateError::BoatNotFound);
}; };
@ -354,7 +354,7 @@ ORDER BY departure DESC
{ {
Ok(_) => { Ok(_) => {
tx.commit().await.unwrap(); tx.commit().await.unwrap();
Ok(()) Ok(String::new())
} }
Err(a) => Err(a.into()), Err(a) => Err(a.into()),
}; };
@ -426,7 +426,15 @@ ORDER BY departure DESC
tx.commit().await.unwrap(); tx.commit().await.unwrap();
Ok(()) let mut ret = String::new();
for rower in &log.rowers {
let user = User::find_by_id(db, *rower as i32).await.unwrap();
if let Some(msg) = user.close_thousands_trip(db).await {
ret.push_str(&format!("\n{msg}"));
}
}
Ok(ret)
} }
pub async fn distances(db: &SqlitePool) -> Vec<(String, i64)> { pub async fn distances(db: &SqlitePool) -> Vec<(String, i64)> {

View File

@ -95,7 +95,7 @@ ORDER BY
#[derive(FromRow, Serialize, Clone)] #[derive(FromRow, Serialize, Clone)]
pub struct Stat { pub struct Stat {
name: String, name: String,
rowed_km: i32, pub(crate) rowed_km: i32,
} }
impl Stat { impl Stat {
@ -195,6 +195,34 @@ ORDER BY rowed_km DESC, u.name;
}) })
.collect() .collect()
} }
pub async fn person(db: &SqlitePool, year: Option<i32>, user: &User) -> Stat {
let year = match year {
Some(year) => year,
None => chrono::Local::now().year(),
};
//TODO: switch to query! macro again (once upgraded to sqlite 3.42 on server)
let row = sqlx::query(&format!(
"
SELECT u.name, CAST(SUM(l.distance_in_km) AS INTEGER) AS rowed_km
FROM (
SELECT * FROM user
WHERE id={}
) u
INNER JOIN rower r ON u.id = r.rower_id
INNER JOIN logbook l ON r.logbook_id = l.id
WHERE l.distance_in_km IS NOT NULL AND l.arrival LIKE '{year}-%';
",
user.id
))
.fetch_one(db)
.await
.unwrap();
Stat {
name: row.get("name"),
rowed_km: row.get("rowed_km"),
}
}
} }
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
@ -218,7 +246,7 @@ FROM (
LEFT JOIN LEFT JOIN
rower r ON l.id = r.logbook_id rower r ON l.id = r.logbook_id
WHERE WHERE
l.shipmaster = {0} OR r.rower_id = {0} r.rower_id = {}
GROUP BY GROUP BY
departure_date departure_date
) as subquery ) as subquery

View File

@ -15,7 +15,7 @@ use serde::{Deserialize, Serialize};
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction}; use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
use super::{ use super::{
family::Family, log::Log, mail::Mail, notification::Notification, role::Role, family::Family, log::Log, mail::Mail, notification::Notification, role::Role, stat::Stat,
tripdetails::TripDetails, Day, tripdetails::TripDetails, Day,
}; };
use crate::tera::admin::user::UserEditForm; use crate::tera::admin::user::UserEditForm;
@ -849,6 +849,19 @@ ORDER BY last_access DESC
6 6
} }
} }
pub(crate) async fn close_thousands_trip(&self, db: &SqlitePool) -> Option<String> {
let rowed_km = Stat::person(db, None, self).await.rowed_km;
if rowed_km % 1000 > 970 {
return Some(format!(
"{} braucht nur mehr {} km bis die {} km voll sind 🤑",
self.name,
1000 - rowed_km % 1000,
rowed_km + 1000 - (rowed_km % 1000)
));
}
None
}
} }
#[async_trait] #[async_trait]

View File

@ -212,7 +212,7 @@ async fn create_logbook(
) )
.await .await
{ {
Ok(_) => Flash::success(Redirect::to("/log"), "Ausfahrt erfolgreich hinzugefügt"), Ok(msg) => Flash::success(Redirect::to("/log"), format!("Ausfahrt erfolgreich hinzugefügt {msg}")),
Err(LogbookCreateError::BoatAlreadyOnWater) => Flash::error(Redirect::to("/log"), "Boot schon am Wasser"), Err(LogbookCreateError::BoatAlreadyOnWater) => Flash::error(Redirect::to("/log"), "Boot schon am Wasser"),
Err(LogbookCreateError::RowerAlreadyOnWater(rower)) => Flash::error(Redirect::to("/log"), format!("Ruderer {} schon am Wasser", rower.name)), Err(LogbookCreateError::RowerAlreadyOnWater(rower)) => Flash::error(Redirect::to("/log"), format!("Ruderer {} schon am Wasser", rower.name)),
Err(LogbookCreateError::BoatLocked) => Flash::error(Redirect::to("/log"),"Boot gesperrt"), Err(LogbookCreateError::BoatLocked) => Flash::error(Redirect::to("/log"),"Boot gesperrt"),