show stations if a team is supposed to be on their way to them
This commit is contained in:
parent
b3bf50fed1
commit
4a0f6c0285
@ -235,6 +235,69 @@ DROP TABLE temp_pos;",
|
||||
.expect("db constraints"),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn prev_station(&self, db: &SqlitePool, station: &Station) -> Option<Station> {
|
||||
if station.crewless() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let ret = sqlx::query_as::<_, Station>(
|
||||
"
|
||||
WITH RECURSIVE find_previous AS (
|
||||
-- Get position of station
|
||||
SELECT pos, 0 AS steps
|
||||
FROM route_station
|
||||
WHERE route_id = ? AND station_id = ?
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Keep looking for previous positions until we find one with amount_people > 0
|
||||
SELECT
|
||||
CASE
|
||||
-- If we're at the first position, wrap around to the last
|
||||
WHEN prev.pos = (SELECT MIN(pos) FROM route_station WHERE route_id = ?) THEN
|
||||
(SELECT MAX(pos) FROM route_station WHERE route_id = ?)
|
||||
-- Otherwise, get the previous position
|
||||
ELSE
|
||||
(SELECT MAX(pos) FROM route_station WHERE route_id = ? AND pos < prev.pos)
|
||||
END AS pos,
|
||||
prev.steps + 1 AS steps
|
||||
FROM find_previous prev
|
||||
-- Stop when we've checked all positions in the route
|
||||
WHERE prev.steps < (SELECT COUNT(*) FROM route_station WHERE route_id = ?)
|
||||
)
|
||||
|
||||
SELECT s.id, s.name, s.notes, s.amount_people, s.last_login, s.ready, s.pw, s.lat, s.lng
|
||||
FROM find_previous fp
|
||||
JOIN route_station rs ON rs.route_id = ? AND rs.pos = fp.pos
|
||||
JOIN station s ON s.id = rs.station_id
|
||||
WHERE (s.amount_people > 0 OR s.amount_people is NULL)
|
||||
AND fp.steps > 0 -- Skip the starting position
|
||||
ORDER BY fp.steps
|
||||
LIMIT 1;
|
||||
);",
|
||||
)
|
||||
.bind(self.id)
|
||||
.bind(station.id)
|
||||
.bind(self.id)
|
||||
.bind(self.id)
|
||||
.bind(self.id)
|
||||
.bind(self.id)
|
||||
.bind(self.id)
|
||||
.bind(self.id)
|
||||
.bind(station.id)
|
||||
.fetch_optional(db)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Don't return same station as prev station
|
||||
if let Some(prev) = &ret {
|
||||
if prev.id == station.id {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn routes() -> Router<AppState> {
|
||||
|
@ -6,6 +6,7 @@ use crate::{
|
||||
};
|
||||
use axum::Router;
|
||||
use chrono::{DateTime, Local, NaiveDateTime, Utc};
|
||||
use futures::{stream, StreamExt};
|
||||
use maud::{html, Markup, Render};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{FromRow, SqlitePool};
|
||||
@ -427,6 +428,63 @@ ORDER BY LOWER(t.name);",
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub(crate) async fn left_teams(&self, db: &SqlitePool) -> Vec<Team> {
|
||||
sqlx::query_as::<_, Team>(
|
||||
"SELECT t.id, t.name, t.notes, t.amount_people, t.first_station_id, t.route_id
|
||||
FROM team t
|
||||
JOIN rating r ON t.id = r.team_id
|
||||
WHERE r.station_id = ?
|
||||
AND r.left_at IS NOT NULL;",
|
||||
)
|
||||
.bind(self.id)
|
||||
.fetch_all(db)
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub async fn teams_on_the_way(&self, db: &SqlitePool) -> Vec<TeamOnTheWay> {
|
||||
let mut ret = Vec::new();
|
||||
|
||||
let teams = self.teams(db).await;
|
||||
|
||||
let missing_teams: Vec<Team> = stream::iter(teams)
|
||||
.filter_map(|entry| async move {
|
||||
if !entry.been_at_station(db, &self).await {
|
||||
Some(entry)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
for team in missing_teams {
|
||||
let route = team.route(db).await;
|
||||
let Some(prev_station) = route.prev_station(db, self).await else {
|
||||
continue;
|
||||
};
|
||||
let left_teams_of_prev_station = prev_station.left_teams(db).await;
|
||||
if left_teams_of_prev_station.contains(&team) {
|
||||
// team not yet at `self`, but already left `prev_station`
|
||||
let rating = Rating::find_by_team_and_station(db, &team, &prev_station)
|
||||
.await
|
||||
.unwrap();
|
||||
ret.push(TeamOnTheWay {
|
||||
left: rating.local_time_left(),
|
||||
team: team.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TeamOnTheWay {
|
||||
pub(crate) team: Team,
|
||||
pub(crate) left: String,
|
||||
//avg_time_in_secs: i64,
|
||||
}
|
||||
|
||||
pub(super) fn routes() -> Router<AppState> {
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::{
|
||||
admin::{route::Route, station::Station},
|
||||
models::rating::Rating,
|
||||
AppState,
|
||||
};
|
||||
use axum::Router;
|
||||
@ -9,7 +10,7 @@ use sqlx::{FromRow, SqlitePool};
|
||||
|
||||
mod web;
|
||||
|
||||
#[derive(FromRow, Debug, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(FromRow, Debug, Serialize, Deserialize, PartialEq, Clone)]
|
||||
pub(crate) struct Team {
|
||||
pub(crate) id: i64,
|
||||
pub(crate) name: String,
|
||||
@ -227,6 +228,12 @@ impl Team {
|
||||
.unwrap()
|
||||
.points
|
||||
}
|
||||
|
||||
pub async fn been_at_station(&self, db: &SqlitePool, station: &Station) -> bool {
|
||||
Rating::find_by_team_and_station(db, &self, station)
|
||||
.await
|
||||
.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn routes() -> Router<AppState> {
|
||||
|
@ -11,7 +11,7 @@ pub(crate) struct Rating {
|
||||
pub(crate) notes: Option<String>,
|
||||
arrived_at: NaiveDateTime,
|
||||
started_at: Option<NaiveDateTime>,
|
||||
left_at: Option<NaiveDateTime>,
|
||||
pub(crate) left_at: Option<NaiveDateTime>,
|
||||
}
|
||||
|
||||
impl Rating {
|
||||
|
@ -29,6 +29,7 @@ async fn view(
|
||||
};
|
||||
|
||||
let teams = TeamsAtStationLocation::for_station(&db, &station).await;
|
||||
let teams_on_the_way = station.teams_on_the_way(&db).await;
|
||||
|
||||
let content = html! {
|
||||
h1 { (format!("Station {}", station.name)) }
|
||||
@ -109,6 +110,19 @@ async fn view(
|
||||
" Teams zu deiner Station kommen."
|
||||
progress value=(teams.total_teams-teams.not_yet_here.len() as i64) max=(teams.total_teams) {}
|
||||
}
|
||||
@for team in teams_on_the_way {
|
||||
article {
|
||||
"Team "
|
||||
(team.team.name)
|
||||
" ist seit "
|
||||
(team.left)
|
||||
" auf dem Weg zu deiner Station."
|
||||
form action=(format!("/s/{id}/{code}/new-waiting")) method="post" {
|
||||
input type="hidden" name="team_id" value=(team.team.id);
|
||||
input type="submit" value="Team ist da";
|
||||
}
|
||||
}
|
||||
}
|
||||
@if !teams.not_yet_here.is_empty() {
|
||||
form action=(format!("/s/{id}/{code}/new-waiting")) method="post" {
|
||||
fieldset role="group" {
|
||||
|
Loading…
x
Reference in New Issue
Block a user