182 lines
5.7 KiB
Rust
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,
|
|
}
|
|
}
|
|
}
|