stationslauf/src/models/rating.rs

182 lines
5.7 KiB
Rust

use crate::{admin::team::Team, Station};
use chrono::{DateTime, Local, NaiveDateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, SqlitePool};
#[derive(FromRow, Debug, Serialize, Deserialize)]
pub(crate) struct Rating {
pub(crate) team_id: i64,
pub(crate) station_id: i64,
pub(crate) points: Option<i64>,
pub(crate) notes: Option<String>,
arrived_at: NaiveDateTime,
started_at: Option<NaiveDateTime>,
left_at: Option<NaiveDateTime>,
}
impl Rating {
pub(crate) async fn create(
db: &SqlitePool,
station: &Station,
team: &Team,
) -> Result<(), String> {
sqlx::query!(
"INSERT INTO rating(team_id, station_id) VALUES (?, ?)",
team.id,
station.id
)
.execute(db)
.await
.map_err(|e| e.to_string())?;
Ok(())
}
pub(crate) async fn create_quick(db: &SqlitePool, team: &Team, station: &Station, points: i64) {
sqlx::query!(
"INSERT INTO rating(team_id, station_id, points, arrived_at, started_at, left_at) VALUES (?, ?, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)",
team.id,
station.id,
points
)
.execute(db)
.await
.unwrap();
}
pub(crate) async fn team(&self, db: &SqlitePool) -> Team {
Team::find_by_id(db, self.team_id)
.await
.expect("db constraints")
}
pub(crate) async fn for_station(db: &SqlitePool, station: &Station) -> Vec<Self> {
sqlx::query_as::<_, Self>("SELECT team_id, station_id, points, notes, arrived_at, started_at, left_at FROM rating WHERE station_id = ?;")
.bind(station.id)
.fetch_all(db)
.await
.unwrap()
}
pub(crate) async fn update(
db: &SqlitePool,
station: &Station,
team: &Team,
points: Option<i64>,
notes: Option<String>,
) -> Result<(), String> {
sqlx::query!(
"UPDATE rating SET points = ?, notes = ? WHERE station_id = ? AND team_id = ?",
points,
notes,
station.id,
team.id
)
.execute(db)
.await
.map_err(|e| e.to_string())?;
Ok(())
}
pub(crate) async fn delete(
db: &SqlitePool,
station: &Station,
team: &Team,
) -> Result<(), String> {
sqlx::query!(
"DELETE FROM rating WHERE team_id = ? AND station_id = ?",
team.id,
station.id
)
.execute(db)
.await
.map_err(|e| e.to_string())?;
Ok(())
}
pub async fn find_by_team_and_station(
db: &SqlitePool,
team: &Team,
station: &Station,
) -> Option<Self> {
sqlx::query_as!(Self, "SELECT team_id, station_id, points, notes, arrived_at, started_at, left_at FROM rating WHERE team_id = ? AND station_id = ?", team.id, station.id)
.fetch_one(db)
.await
.ok()
}
pub(crate) fn local_time_arrived_at(&self) -> String {
let datetime_utc = DateTime::<Utc>::from_naive_utc_and_offset(self.arrived_at, Utc);
let datetime_local = datetime_utc.with_timezone(&Local);
datetime_local.format("%H:%M").to_string()
}
pub(crate) fn local_time_doing(&self) -> String {
let Some(started_at) = self.started_at else {
return String::from("noch nicht gestartet");
};
let datetime_utc = DateTime::<Utc>::from_naive_utc_and_offset(started_at, Utc);
let datetime_local = datetime_utc.with_timezone(&Local);
datetime_local.format("%H:%M").to_string()
}
pub(crate) fn local_time_left(&self) -> String {
let Some(left_at) = self.left_at else {
return String::from("noch nicht fertig");
};
let datetime_utc = DateTime::<Utc>::from_naive_utc_and_offset(left_at, Utc);
let datetime_local = datetime_utc.with_timezone(&Local);
datetime_local.format("%H:%M").to_string()
}
}
pub(crate) struct TeamsAtStationLocation {
pub(crate) total_teams: i64,
pub(crate) not_yet_here: Vec<Team>,
pub(crate) waiting: Vec<(Team, Rating)>,
pub(crate) doing: Vec<(Team, Rating)>,
pub(crate) left_not_yet_rated: Vec<(Team, Rating)>,
pub(crate) left_and_rated: Vec<(Team, Rating)>,
}
impl TeamsAtStationLocation {
pub(crate) async fn for_station(db: &SqlitePool, station: &Station) -> TeamsAtStationLocation {
let teams = station.teams(db).await;
let total_teams = teams.len() as i64;
let mut not_yet_here = Vec::new();
let mut waiting = Vec::new();
let mut doing = Vec::new();
let mut left_not_yet_rated = Vec::new();
let mut left_and_rated = Vec::new();
for team in teams {
match Rating::find_by_team_and_station(db, &team, station).await {
Some(rating) => {
if rating.left_at.is_some() {
if rating.points.is_some() {
left_and_rated.push((team, rating));
} else {
left_not_yet_rated.push((team, rating));
}
} else if rating.started_at.is_some() {
doing.push((team, rating));
} else {
waiting.push((team, rating));
}
}
None => not_yet_here.push(team),
}
}
TeamsAtStationLocation {
total_teams,
not_yet_here,
waiting,
doing,
left_not_yet_rated,
left_and_rated,
}
}
}