2024-04-06 15:27:35 +02:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
2023-10-01 14:35:04 +02:00
|
|
|
use crate::model::user::User;
|
2023-11-18 12:21:37 +01:00
|
|
|
use chrono::Datelike;
|
2023-07-24 20:56:46 +02:00
|
|
|
use serde::Serialize;
|
2023-07-24 21:17:51 +02:00
|
|
|
use sqlx::{FromRow, Row, SqlitePool};
|
2023-07-24 20:56:46 +02:00
|
|
|
|
2024-04-06 15:27:35 +02:00
|
|
|
#[derive(Serialize, Clone)]
|
|
|
|
pub struct BoatStat {
|
|
|
|
pot_years: Vec<i32>,
|
|
|
|
boats: Vec<SingleBoatStat>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Clone)]
|
|
|
|
pub struct SingleBoatStat {
|
2023-07-24 20:56:46 +02:00
|
|
|
name: String,
|
2024-04-06 15:27:35 +02:00
|
|
|
location: String,
|
|
|
|
years: HashMap<String, i32>,
|
2023-07-24 20:56:46 +02:00
|
|
|
}
|
|
|
|
|
2024-04-06 15:27:35 +02:00
|
|
|
impl BoatStat {
|
|
|
|
pub async fn get(db: &SqlitePool) -> BoatStat {
|
|
|
|
let mut years = Vec::new();
|
|
|
|
let mut boat_stats_map: HashMap<String, SingleBoatStat> = HashMap::new();
|
|
|
|
|
|
|
|
let rows = sqlx::query(
|
2023-10-31 16:07:15 +01:00
|
|
|
"
|
2024-04-06 15:27:35 +02:00
|
|
|
SELECT
|
|
|
|
boat.name,
|
|
|
|
location.name AS location,
|
|
|
|
CAST(strftime('%Y', arrival) AS INTEGER) AS year,
|
|
|
|
CAST(SUM(distance_in_km) AS INTEGER) AS rowed_km
|
|
|
|
FROM
|
|
|
|
logbook
|
|
|
|
INNER JOIN
|
|
|
|
boat ON boat.id = logbook.boat_id
|
|
|
|
INNER JOIN
|
|
|
|
location ON boat.location_id = location.id
|
|
|
|
WHERE
|
|
|
|
boat.name != 'Externes Boot'
|
|
|
|
GROUP BY
|
|
|
|
boat_id, year
|
|
|
|
ORDER BY
|
|
|
|
boat.name, year DESC;
|
|
|
|
",
|
2023-10-31 16:07:15 +01:00
|
|
|
)
|
|
|
|
.fetch_all(db)
|
|
|
|
.await
|
2024-04-06 15:27:35 +02:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
for row in rows {
|
|
|
|
let name: String = row.get("name");
|
|
|
|
let location: String = row.get("location");
|
|
|
|
let year: i32 = row.get("year");
|
|
|
|
if !years.contains(&year) {
|
|
|
|
years.push(year);
|
|
|
|
}
|
|
|
|
|
|
|
|
let year: String = format!("{year}");
|
|
|
|
|
|
|
|
let rowed_km: i32 = row.get("rowed_km");
|
|
|
|
|
|
|
|
let boat_stat = boat_stats_map
|
|
|
|
.entry(name.clone())
|
|
|
|
.or_insert(SingleBoatStat {
|
|
|
|
name,
|
|
|
|
location,
|
|
|
|
years: HashMap::new(),
|
|
|
|
});
|
|
|
|
boat_stat.years.insert(year, rowed_km);
|
|
|
|
}
|
|
|
|
|
|
|
|
BoatStat {
|
|
|
|
pot_years: years,
|
|
|
|
boats: boat_stats_map.into_values().collect(),
|
|
|
|
}
|
2023-10-31 16:07:15 +01:00
|
|
|
}
|
2024-04-06 15:27:35 +02:00
|
|
|
}
|
2023-10-31 16:07:15 +01:00
|
|
|
|
2024-04-06 15:27:35 +02:00
|
|
|
#[derive(FromRow, Serialize, Clone)]
|
|
|
|
pub struct Stat {
|
|
|
|
name: String,
|
|
|
|
rowed_km: i32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Stat {
|
2023-12-23 15:26:49 +01:00
|
|
|
pub async fn guest(db: &SqlitePool, year: Option<i32>) -> Stat {
|
|
|
|
let year = match year {
|
|
|
|
Some(year) => year,
|
2024-03-05 00:17:02 +01:00
|
|
|
None => chrono::Local::now().year(),
|
2023-12-23 15:26:49 +01:00
|
|
|
};
|
|
|
|
//TODO: switch to query! macro again (once upgraded to sqlite 3.42 on server)
|
|
|
|
let rowed_km = sqlx::query(&format!(
|
|
|
|
"
|
|
|
|
SELECT SUM((b.amount_seats - COALESCE(m.member_count, 0)) * l.distance_in_km) as total_guest_km
|
|
|
|
FROM logbook l
|
|
|
|
JOIN boat b ON l.boat_id = b.id
|
|
|
|
LEFT JOIN (
|
|
|
|
SELECT logbook_id, COUNT(*) as member_count
|
|
|
|
FROM rower
|
|
|
|
GROUP BY logbook_id
|
|
|
|
) m ON l.id = m.logbook_id
|
|
|
|
WHERE l.distance_in_km IS NOT NULL AND l.arrival LIKE '{year}-%' AND b.name != 'Externes Boot';
|
|
|
|
"
|
|
|
|
))
|
|
|
|
.fetch_one(db)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
2024-01-05 20:08:13 +01:00
|
|
|
.get::<i64, usize>(0) as i32;
|
|
|
|
|
|
|
|
let rowed_km_guests = sqlx::query(&format!(
|
|
|
|
"
|
2024-02-12 20:09:30 +01:00
|
|
|
SELECT CAST(SUM(l.distance_in_km) AS INTEGER) AS rowed_km
|
2024-01-05 20:08:13 +01:00
|
|
|
FROM user u
|
|
|
|
INNER JOIN rower r ON u.id = r.rower_id
|
|
|
|
INNER JOIN logbook l ON r.logbook_id = l.id
|
2024-02-12 20:09:30 +01:00
|
|
|
WHERE u.id NOT IN (
|
|
|
|
SELECT ur.user_id
|
|
|
|
FROM user_role ur
|
|
|
|
INNER JOIN role ro ON ur.role_id = ro.id
|
|
|
|
WHERE ro.name = 'Donau Linz'
|
|
|
|
)
|
|
|
|
AND l.distance_in_km IS NOT NULL
|
2024-03-20 08:58:51 +01:00
|
|
|
AND l.arrival LIKE '{year}-%'
|
|
|
|
AND u.name != 'Externe Steuerperson';
|
2024-01-05 20:08:13 +01:00
|
|
|
"
|
|
|
|
))
|
|
|
|
.fetch_one(db)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.get::<i64, usize>(0) as i32;
|
2023-12-23 15:26:49 +01:00
|
|
|
|
|
|
|
Stat {
|
|
|
|
name: "Gäste".into(),
|
2024-01-05 20:08:13 +01:00
|
|
|
rowed_km: rowed_km + rowed_km_guests,
|
2023-12-23 15:26:49 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-20 13:34:21 +01:00
|
|
|
pub async fn sum_people(db: &SqlitePool, year: Option<i32>) -> i32 {
|
|
|
|
let stats = Self::people(db, year).await;
|
|
|
|
let mut sum = 0;
|
|
|
|
for stat in stats {
|
|
|
|
sum += stat.rowed_km;
|
|
|
|
}
|
|
|
|
|
|
|
|
sum
|
|
|
|
}
|
|
|
|
|
2023-11-18 12:21:37 +01:00
|
|
|
pub async fn people(db: &SqlitePool, year: Option<i32>) -> Vec<Stat> {
|
|
|
|
let year = match year {
|
|
|
|
Some(year) => year,
|
2024-03-05 00:17:02 +01:00
|
|
|
None => chrono::Local::now().year(),
|
2023-11-18 12:21:37 +01:00
|
|
|
};
|
2023-07-24 21:17:51 +02:00
|
|
|
//TODO: switch to query! macro again (once upgraded to sqlite 3.42 on server)
|
2023-11-18 12:21:37 +01:00
|
|
|
sqlx::query(&format!(
|
2023-08-05 15:58:17 +02:00
|
|
|
"
|
2023-10-30 15:31:12 +01:00
|
|
|
SELECT u.name, CAST(SUM(l.distance_in_km) AS INTEGER) AS rowed_km
|
2024-01-05 20:37:40 +01:00
|
|
|
FROM (
|
|
|
|
SELECT * FROM user
|
2024-02-12 20:09:30 +01:00
|
|
|
WHERE id IN (
|
2024-01-05 20:37:40 +01:00
|
|
|
SELECT user_id FROM user_role
|
|
|
|
JOIN role ON user_role.role_id = role.id
|
2024-02-12 20:09:30 +01:00
|
|
|
WHERE role.name = 'Donau Linz'
|
2024-01-05 20:37:40 +01:00
|
|
|
)
|
|
|
|
) u
|
2023-10-30 15:31:12 +01:00
|
|
|
INNER JOIN rower r ON u.id = r.rower_id
|
|
|
|
INNER JOIN logbook l ON r.logbook_id = l.id
|
2024-01-05 22:23:36 +01:00
|
|
|
WHERE l.distance_in_km IS NOT NULL AND l.arrival LIKE '{year}-%' AND u.name != 'Externe Steuerperson'
|
2023-08-05 15:58:17 +02:00
|
|
|
GROUP BY u.name
|
2024-03-04 17:12:16 +01:00
|
|
|
ORDER BY rowed_km DESC, u.name;
|
2023-11-27 12:49:36 +01:00
|
|
|
"
|
2023-11-18 12:21:37 +01:00
|
|
|
))
|
2023-07-24 20:56:46 +02:00
|
|
|
.fetch_all(db)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.into_iter()
|
|
|
|
.map(|row| Stat {
|
2023-07-24 21:16:43 +02:00
|
|
|
name: row.get("name"),
|
|
|
|
rowed_km: row.get("rowed_km"),
|
2023-07-24 20:56:46 +02:00
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
}
|
2023-10-01 14:35:04 +02:00
|
|
|
|
|
|
|
#[derive(Debug, Serialize)]
|
|
|
|
pub struct PersonalStat {
|
|
|
|
date: String,
|
|
|
|
km: i32,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn get_personal(db: &SqlitePool, user: &User) -> Vec<PersonalStat> {
|
2023-10-01 18:14:05 +02:00
|
|
|
sqlx::query(&format!(
|
|
|
|
"
|
|
|
|
SELECT
|
|
|
|
departure_date as date,
|
|
|
|
SUM(total_distance) OVER (ORDER BY departure_date) as km
|
|
|
|
FROM (
|
|
|
|
SELECT
|
|
|
|
date(l.departure) as departure_date,
|
|
|
|
COALESCE(SUM(l.distance_in_km),0) as total_distance
|
|
|
|
FROM
|
|
|
|
logbook l
|
|
|
|
LEFT JOIN
|
|
|
|
rower r ON l.id = r.logbook_id
|
|
|
|
WHERE
|
|
|
|
l.shipmaster = {0} OR r.rower_id = {0}
|
|
|
|
GROUP BY
|
|
|
|
departure_date
|
|
|
|
) as subquery
|
|
|
|
ORDER BY
|
|
|
|
departure_date;
|
|
|
|
",
|
|
|
|
user.id
|
|
|
|
))
|
|
|
|
.fetch_all(db)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
.into_iter()
|
|
|
|
.map(|row| PersonalStat {
|
|
|
|
date: row.get("date"),
|
|
|
|
km: row.get("km"),
|
|
|
|
})
|
|
|
|
.collect()
|
2023-10-01 14:35:04 +02:00
|
|
|
}
|