Compare commits

...

5 Commits

Author SHA1 Message Date
97dbd4fcae Merge pull request 'use-sqliteconnection' (#86) from use-sqliteconnection into main
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / deploy (push) Has been cancelled
Reviewed-on: star/stationslauf#86
2025-05-15 19:43:49 +02:00
a46cf6ed97 pedantic clippy
Some checks failed
CI/CD Pipeline / deploy (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2025-05-15 19:40:52 +02:00
2a0098b0cb pedantic clippy 2025-05-15 19:40:16 +02:00
69aed3be27 cargo clippy
All checks were successful
CI/CD Pipeline / test (push) Successful in 11m30s
CI/CD Pipeline / deploy (push) Has been skipped
2025-05-15 19:27:39 +02:00
79d22a0ad1 use sqlite connection instead of db pool, removes the need for 2 function (pool + tx)
Some checks failed
CI/CD Pipeline / deploy (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2025-05-15 19:26:05 +02:00
13 changed files with 449 additions and 350 deletions

View File

@@ -16,7 +16,7 @@ use rand::{
rng,
};
use route::Route;
use sqlx::SqlitePool;
use sqlx::{SqliteConnection, SqlitePool};
use std::sync::Arc;
use team::Team;
use tower_sessions::Session;
@@ -35,7 +35,9 @@ fn generate_random_alphanumeric(length: usize) -> String {
}
async fn highscore(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
let routes = Route::all(&db).await;
let db = &mut *db.acquire().await.unwrap();
let routes = Route::all(db).await;
let content = html! {
h1 {
@@ -51,7 +53,7 @@ async fn highscore(State(db): State<Arc<SqlitePool>>, session: Session) -> Marku
thead {
tr {
td { (t!("team")) }
@for station in route.stations(&db).await {
@for station in route.stations(db).await {
td {
(station)
}
@@ -65,7 +67,7 @@ async fn highscore(State(db): State<Arc<SqlitePool>>, session: Session) -> Marku
@let mut rank = 0;
@let mut amount_teams_iterated = 0;
@let mut prev_points = i64::MAX;
@for team in route.teams_ordered_by_points(&db).await {
@for team in route.teams_ordered_by_points(db).await {
@let mut total_points = 0;
({ amount_teams_iterated += 1;"" })
tr {
@@ -74,9 +76,9 @@ async fn highscore(State(db): State<Arc<SqlitePool>>, session: Session) -> Marku
(team.name)
}
}
@for station in route.stations(&db).await {
@for station in route.stations(db).await {
td {
@if let Some(rating) = Rating::find_by_team_and_station(&db, &team, &station).await {
@if let Some(rating) = Rating::find_by_team_and_station(db, &team, &station).await {
@if let (Some(notes), Some(points)) = (rating.notes, rating.points) {
({total_points += points;""})
em data-placement="bottom" data-tooltip=(notes) { (points) }
@@ -125,7 +127,7 @@ pub enum RunStatus {
}
impl RunStatus {
pub async fn curr(db: &SqlitePool) -> Self {
pub async fn curr(db: &mut SqliteConnection) -> Self {
let stations = Station::all(db).await;
if stations.is_empty() {
return RunStatus::NoStationsYet;
@@ -143,10 +145,12 @@ async fn index(
session: Session,
auth_session: AuthSession,
) -> Markup {
let db = &mut *db.acquire().await.unwrap();
let user = auth_session
.user
.expect("Can only be called by loggedin people");
let status = RunStatus::curr(&db).await;
let status = RunStatus::curr(db).await;
let content = html! {
nav {
ul {
@@ -201,7 +205,7 @@ async fn index(
}
},
RunStatus::Active => {
@let stations = Station::all(&db).await;
@let stations = Station::all(db).await;
a href="/admin/end-run" onclick=(format!("return confirm('{}');", t!("confirm_end_run"))) {
button style="background-color: red;" {
(t!("end_run"))
@@ -230,7 +234,7 @@ async fn index(
}
td {
ol {
@for team in Team::all_with_first_station(&db, &station).await {
@for team in Team::all_with_first_station(db, &station).await {
li { (team) }
}
}
@@ -241,7 +245,7 @@ async fn index(
}
},
RunStatus::HasEnded => {
@let stations = Station::all(&db).await;
@let stations = Station::all(db).await;
a href="/admin/restart-run" onclick=(format!("return confirm('{}');", t!("confirm_restart_run"))) {
button style="background-color: red;" {
(t!("restart_run"))
@@ -263,7 +267,7 @@ async fn index(
td { (station) }
td {
ol {
@for team in Team::all_with_last_station(&db, &station).await {
@for team in Team::all_with_last_station(db, &station).await {
li { (team) }
}
}
@@ -279,12 +283,16 @@ async fn index(
}
async fn end_run(State(db): State<Arc<SqlitePool>>, session: Session) -> impl IntoResponse {
Team::end_run(&db).await;
let db = &mut *db.acquire().await.unwrap();
Team::end_run(db).await;
suc!(session, t!("run_ended"));
Redirect::to("/admin")
}
async fn restart_run(State(db): State<Arc<SqlitePool>>, session: Session) -> impl IntoResponse {
Team::restart_run(&db).await;
let db = &mut *db.acquire().await.unwrap();
Team::restart_run(db).await;
suc!(session, t!("run_restarted"));
Redirect::to("/admin")
}

View File

@@ -3,10 +3,8 @@ use crate::{
AppState,
};
use axum::Router;
use futures::future::join_all;
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, Row, Sqlite, SqlitePool, Transaction};
use std::ops::DerefMut;
use sqlx::{FromRow, Row, SqliteConnection};
mod web;
@@ -17,29 +15,29 @@ pub(crate) struct Route {
}
impl Route {
pub(crate) async fn all(db: &SqlitePool) -> Vec<Self> {
pub(crate) async fn all(db: &mut SqliteConnection) -> Vec<Self> {
sqlx::query_as::<_, Self>("SELECT id, name FROM route;")
.fetch_all(db)
.await
.unwrap()
}
pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> {
pub async fn find_by_id(db: &mut SqliteConnection, id: i64) -> Option<Self> {
sqlx::query_as!(Self, "SELECT id, name FROM route WHERE id = ?", id)
.fetch_one(db)
.await
.ok()
}
pub(crate) async fn create(db: &SqlitePool, name: &str) -> Result<Self, String> {
pub(crate) async fn create(db: &mut SqliteConnection, name: &str) -> Result<Self, String> {
let route = sqlx::query!("INSERT INTO route(name) VALUES (?) RETURNING id", name)
.fetch_one(db)
.fetch_one(&mut *db)
.await
.map_err(|e| e.to_string())?;
Ok(Self::find_by_id(db, route.id).await.expect("just created"))
}
async fn update_name(&self, db: &SqlitePool, name: &str) {
async fn update_name(&self, db: &mut SqliteConnection, name: &str) {
sqlx::query!("UPDATE route SET name = ? WHERE id = ?", name, self.id)
.execute(db)
.await
@@ -48,7 +46,7 @@ impl Route {
pub(crate) async fn add_station(
&self,
db: &SqlitePool,
db: &mut SqliteConnection,
station: &Station,
) -> Result<(), String> {
sqlx::query!(
@@ -71,7 +69,7 @@ impl Route {
Ok(())
}
async fn delete_station(&self, db: &SqlitePool, station: &Station) -> bool {
async fn delete_station(&self, db: &mut SqliteConnection, station: &Station) -> bool {
let result = sqlx::query!(
"DELETE FROM route_station WHERE route_id = ? AND station_id = ?",
self.id,
@@ -84,13 +82,13 @@ impl Route {
result.rows_affected() > 0
}
async fn move_station_higher(&self, db: &SqlitePool, station: &Station) -> bool {
async fn move_station_higher(&self, db: &mut SqliteConnection, station: &Station) -> bool {
let result = sqlx::query!(
"UPDATE route_station SET pos = pos-3 WHERE route_id = ? AND station_id = ?",
self.id,
station.id
)
.execute(db)
.execute(&mut *db)
.await
.unwrap();
@@ -126,7 +124,7 @@ DROP TABLE temp_pos;",
true
}
async fn delete(&self, db: &SqlitePool) -> Result<(), String> {
async fn delete(&self, db: &mut SqliteConnection) -> Result<(), String> {
sqlx::query!("DELETE FROM route WHERE id = ?", self.id)
.execute(db)
.await
@@ -134,7 +132,7 @@ DROP TABLE temp_pos;",
Ok(())
}
pub(crate) async fn stations(&self, db: &SqlitePool) -> Vec<Station> {
pub(crate) async fn stations(&self, db: &mut SqliteConnection) -> Vec<Station> {
// TODO: switch to macro
sqlx::query_as::<_, Station>(
"
@@ -151,7 +149,7 @@ DROP TABLE temp_pos;",
.unwrap()
}
pub(crate) async fn crewless_stations(&self, db: &SqlitePool) -> Vec<Station> {
pub(crate) async fn crewless_stations(&self, db: &mut SqliteConnection) -> Vec<Station> {
self.stations(db)
.await
.into_iter()
@@ -159,7 +157,7 @@ DROP TABLE temp_pos;",
.collect()
}
pub(crate) async fn crewful_stations(&self, db: &SqlitePool) -> Vec<Station> {
pub(crate) async fn crewful_stations(&self, db: &mut SqliteConnection) -> Vec<Station> {
self.stations(db)
.await
.into_iter()
@@ -167,7 +165,7 @@ DROP TABLE temp_pos;",
.collect()
}
async fn stations_not_in_route(&self, db: &SqlitePool) -> Vec<Station> {
async fn stations_not_in_route(&self, db: &mut SqliteConnection) -> Vec<Station> {
// TODO: switch to macro
sqlx::query_as::<_, Station>(
"
@@ -187,15 +185,18 @@ DROP TABLE temp_pos;",
.unwrap()
}
pub(crate) async fn teams(&self, db: &SqlitePool) -> Vec<Team> {
pub(crate) async fn teams(&self, db: &mut SqliteConnection) -> Vec<Team> {
Team::all_with_route(db, self).await
}
pub(crate) async fn teams_ordered_by_points(&self, db: &SqlitePool) -> Vec<Team> {
pub(crate) async fn teams_ordered_by_points(&self, db: &mut SqliteConnection) -> Vec<Team> {
let teams = Team::all_with_route(db, self).await;
// First, collect all the points
let points_futures: Vec<_> = teams.iter().map(|team| team.get_curr_points(db)).collect();
let points = join_all(points_futures).await;
let mut points = Vec::new();
for team in &teams {
points.push(team.get_curr_points(&mut *db).await);
}
// Create pairs of (team, points)
let mut team_with_points: Vec<_> = teams.into_iter().zip(points).collect();
@@ -207,9 +208,9 @@ DROP TABLE temp_pos;",
team_with_points.into_iter().map(|(team, _)| team).collect()
}
pub(crate) async fn get_next_first_station_tx(
pub(crate) async fn get_next_first_station(
&self,
db: &mut Transaction<'_, Sqlite>,
db: &mut SqliteConnection,
) -> Option<Station> {
let Ok(row) = sqlx::query(&format!(
"
@@ -226,7 +227,7 @@ DROP TABLE temp_pos;",
LIMIT 1",
self.id
))
.fetch_one(db.deref_mut())
.fetch_one(&mut *db)
.await
else {
return None; // No station for route exists
@@ -234,22 +235,17 @@ DROP TABLE temp_pos;",
let next_first_station_id = row.get::<i64, _>("next_first_station_id");
Some(
Station::find_by_id_tx(db, next_first_station_id)
Station::find_by_id(db, next_first_station_id)
.await
.expect("db constraints"),
)
}
pub(crate) async fn get_next_first_station(&self, db: &SqlitePool) -> Option<Station> {
let mut transaction = db.begin().await.unwrap();
let res = self.get_next_first_station_tx(&mut transaction).await;
transaction.commit().await.unwrap();
res
}
pub async fn next_station(&self, db: &SqlitePool, target_station: &Station) -> Option<Station> {
pub async fn next_station(
&self,
db: &mut SqliteConnection,
target_station: &Station,
) -> Option<Station> {
let stations = Station::all(db).await;
for station in stations {
if let Some(prev_station) = self.prev_station(db, &station).await {
@@ -261,7 +257,11 @@ DROP TABLE temp_pos;",
None
}
pub async fn prev_station(&self, db: &SqlitePool, station: &Station) -> Option<Station> {
pub async fn prev_station(
&self,
db: &mut SqliteConnection,
station: &Station,
) -> Option<Station> {
if station.crewless() {
return None;
}

View File

@@ -13,7 +13,9 @@ use std::sync::Arc;
use tower_sessions::Session;
async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
let routes = Route::all(&db).await;
let db = &mut *db.acquire().await.unwrap();
let routes = Route::all(db).await;
let content = html! {
h1 {
@@ -28,7 +30,7 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
ol {
@for route in &routes{
li {
@if route.stations(&db).await.is_empty() {
@if route.stations(db).await.is_empty() {
em data-tooltip=(t!("route_has_no_station_assigned")) {
"⚠️"
}
@@ -71,7 +73,9 @@ async fn create(
session: Session,
Form(form): Form<CreateForm>,
) -> impl IntoResponse {
match Route::create(&db, &form.name).await {
let db = &mut *db.acquire().await.unwrap();
match Route::create(db, &form.name).await {
Ok(_) => suc!(session, t!("route_create_succ", name = form.name)),
Err(e) => er!(
session,
@@ -87,12 +91,14 @@ async fn delete(
session: Session,
axum::extract::Path(id): axum::extract::Path<i64>,
) -> impl IntoResponse {
let Some(route) = Route::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(route) = Route::find_by_id(db, id).await else {
er!(session, t!("nonexisting_route", id = id));
return Redirect::to("/admin/route");
};
match route.delete(&db).await {
match route.delete(db).await {
Ok(()) => suc!(session, t!("route_delete_succ", name = route.name)),
Err(e) => er!(
session,
@@ -108,15 +114,17 @@ async fn view(
session: Session,
axum::extract::Path(id): axum::extract::Path<i64>,
) -> Result<Markup, impl IntoResponse> {
let Some(route) = Route::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(route) = Route::find_by_id(db, id).await else {
er!(session, t!("nonexisting_route", id = id));
return Err(Redirect::to("/admin/route"));
};
let cur_stations = route.stations(&db).await;
let stations_not_in_route = route.stations_not_in_route(&db).await;
let cur_stations = route.stations(db).await;
let stations_not_in_route = route.stations_not_in_route(db).await;
let teams = route.teams(&db).await;
let teams = route.teams(db).await;
let content = html! {
h1 {
@@ -220,12 +228,14 @@ async fn update_name(
axum::extract::Path(id): axum::extract::Path<i64>,
Form(form): Form<UpdateNameForm>,
) -> impl IntoResponse {
let Some(route) = Route::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(route) = Route::find_by_id(db, id).await else {
er!(session, t!("nonexisting_route", id = id));
return Redirect::to("/admin/route");
};
route.update_name(&db, &form.name).await;
route.update_name(db, &form.name).await;
suc!(
session,
@@ -245,16 +255,18 @@ async fn add_station(
axum::extract::Path(id): axum::extract::Path<i64>,
Form(form): Form<AddStationForm>,
) -> impl IntoResponse {
let Some(route) = Route::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(route) = Route::find_by_id(db, id).await else {
er!(session, t!("nonexisting_route", id = id));
return Redirect::to("/admin/route");
};
let Some(station) = Station::find_by_id(&db, form.station).await else {
let Some(station) = Station::find_by_id(&mut *db, form.station).await else {
er!(session, t!("nonexisting_station", id = form.station));
return Redirect::to(&format!("/admin/route/{id}"));
};
match route.add_station(&db, &station).await {
match route.add_station(db, &station).await {
Ok(()) => suc!(
session,
t!(
@@ -282,16 +294,18 @@ async fn delete_station(
session: Session,
axum::extract::Path((route_id, station_id)): axum::extract::Path<(i64, i64)>,
) -> impl IntoResponse {
let Some(route) = Route::find_by_id(&db, route_id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(route) = Route::find_by_id(db, route_id).await else {
er!(session, t!("nonexisting_route", id = route_id));
return Redirect::to("/admin/route");
};
let Some(station) = Station::find_by_id(&db, station_id).await else {
let Some(station) = Station::find_by_id(&mut *db, station_id).await else {
er!(session, t!("nonexisting_station", id = station_id));
return Redirect::to(&format!("/admin/route/{route_id}"));
};
if route.delete_station(&db, &station).await {
if route.delete_station(db, &station).await {
suc!(
session,
t!(
@@ -319,16 +333,18 @@ async fn move_station_higher(
session: Session,
axum::extract::Path((route_id, station_id)): axum::extract::Path<(i64, i64)>,
) -> impl IntoResponse {
let Some(route) = Route::find_by_id(&db, route_id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(route) = Route::find_by_id(db, route_id).await else {
er!(session, t!("nonexisting_route", id = route_id));
return Redirect::to("/admin/route");
};
let Some(station) = Station::find_by_id(&db, station_id).await else {
let Some(station) = Station::find_by_id(&mut *db, station_id).await else {
er!(session, t!("nonexisting_station", id = station_id));
return Redirect::to(&format!("/admin/route/{route_id}"));
};
if route.move_station_higher(&db, &station).await {
if route.move_station_higher(db, &station).await {
suc!(
session,
t!(

View File

@@ -6,11 +6,9 @@ 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, Sqlite, SqlitePool, Transaction};
use std::ops::DerefMut;
use sqlx::{FromRow, SqliteConnection};
pub(crate) mod model;
pub(crate) mod print;
@@ -52,7 +50,7 @@ impl Render for Station {
}
impl Station {
pub(crate) async fn all(db: &SqlitePool) -> Vec<Self> {
pub(crate) async fn all(db: &mut SqliteConnection) -> Vec<Self> {
sqlx::query_as::<_, Self>(
"SELECT id, name, notes, amount_people, last_login, ready, pw, lat, lng FROM station;",
)
@@ -61,18 +59,7 @@ impl Station {
.unwrap()
}
pub async fn find_by_id_tx(db: &mut Transaction<'_, Sqlite>, id: i64) -> Option<Self> {
sqlx::query_as!(
Self,
"SELECT id, name, notes, amount_people, last_login, ready, pw, lat, lng FROM station WHERE id = ?",
id
)
.fetch_one(db.deref_mut())
.await
.ok()
}
pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> {
pub async fn find_by_id(db: &mut SqliteConnection, id: i64) -> Option<Self> {
sqlx::query_as!(
Self,
"SELECT id, name, notes, amount_people, last_login, ready, pw, lat, lng FROM station WHERE id = ?",
@@ -90,13 +77,13 @@ impl Station {
false
}
pub async fn login(db: &SqlitePool, id: i64, code: &str) -> Option<Self> {
pub async fn login(db: &mut SqliteConnection, id: i64, code: &str) -> Option<Self> {
let station = sqlx::query_as!(
Self,
"SELECT id, name, notes, amount_people, last_login, ready, pw, lat, lng FROM station WHERE id = ? AND pw = ?",
id, code
)
.fetch_one(db)
.fetch_one(&mut *db)
.await
.ok()?;
@@ -111,7 +98,7 @@ impl Station {
Some(station)
}
pub async fn switch_ready(&self, db: &SqlitePool) {
pub async fn switch_ready(&self, db: &mut SqliteConnection) {
let new_ready_status = !self.ready;
sqlx::query!(
"UPDATE station SET ready = ? WHERE id = ?",
@@ -123,18 +110,18 @@ impl Station {
.unwrap();
}
pub(crate) async fn create(db: &SqlitePool, name: &str) -> Result<Self, String> {
pub(crate) async fn create(db: &mut SqliteConnection, name: &str) -> Result<Self, String> {
let code = generate_random_alphanumeric(8);
let station_id = sqlx::query!(
"INSERT INTO station(name, pw) VALUES (?, ?) RETURNING id",
name,
code
)
.fetch_one(db)
.fetch_one(&mut *db)
.await
.map_err(|e| e.to_string())?;
let station = Station::find_by_id(db, station_id.id)
let station = Station::find_by_id(&mut *db, station_id.id)
.await
.expect("just created");
@@ -150,7 +137,7 @@ impl Station {
pub(crate) async fn new_team_waiting(
&self,
db: &SqlitePool,
db: &mut SqliteConnection,
team: &Team,
) -> Result<(), String> {
let teams = TeamsAtStationLocation::for_station(db, self).await;
@@ -170,7 +157,7 @@ impl Station {
pub(crate) async fn team_update(
&self,
db: &SqlitePool,
db: &mut SqliteConnection,
team: &Team,
points: Option<i64>,
notes: Option<String>,
@@ -180,7 +167,7 @@ impl Station {
Some(n) => Some(n),
None => None,
};
let teams = TeamsAtStationLocation::for_station(db, self).await;
let teams = TeamsAtStationLocation::for_station(&mut *db, self).await;
let waiting_teams: Vec<&Team> = teams.waiting.iter().map(|(team, _)| team).collect();
let doing_teams: Vec<&Team> = teams.doing.iter().map(|(team, _)| team).collect();
@@ -207,7 +194,7 @@ impl Station {
pub(crate) async fn remove_team_waiting(
&self,
db: &SqlitePool,
db: &mut SqliteConnection,
team: &Team,
) -> Result<(), String> {
let teams = TeamsAtStationLocation::for_station(db, self).await;
@@ -223,7 +210,11 @@ impl Station {
Ok(())
}
pub(crate) async fn team_starting(&self, db: &SqlitePool, team: &Team) -> Result<(), String> {
pub(crate) async fn team_starting(
&self,
db: &mut SqliteConnection,
team: &Team,
) -> Result<(), String> {
let teams = TeamsAtStationLocation::for_station(db, self).await;
let waiting_teams: Vec<&Team> = teams.waiting.iter().map(|(team, _)| team).collect();
@@ -246,7 +237,7 @@ impl Station {
pub(crate) async fn remove_team_doing(
&self,
db: &SqlitePool,
db: &mut SqliteConnection,
team: &Team,
) -> Result<(), String> {
let teams = TeamsAtStationLocation::for_station(db, self).await;
@@ -273,7 +264,11 @@ impl Station {
Ok(())
}
pub(crate) async fn team_finished(&self, db: &SqlitePool, team: &Team) -> Result<(), String> {
pub(crate) async fn team_finished(
&self,
db: &mut SqliteConnection,
team: &Team,
) -> Result<(), String> {
let teams = TeamsAtStationLocation::for_station(db, self).await;
let doing_teams: Vec<&Team> = teams.doing.iter().map(|(team, _)| team).collect();
@@ -296,7 +291,7 @@ impl Station {
pub(crate) async fn remove_team_left(
&self,
db: &SqlitePool,
db: &mut SqliteConnection,
team: &Team,
) -> Result<(), String> {
let teams = TeamsAtStationLocation::for_station(db, self).await;
@@ -325,7 +320,7 @@ impl Station {
Ok(())
}
async fn delete(&self, db: &SqlitePool) -> Result<(), String> {
async fn delete(&self, db: &mut SqliteConnection) -> Result<(), String> {
sqlx::query!("DELETE FROM station WHERE id = ?", self.id)
.execute(db)
.await
@@ -333,7 +328,7 @@ impl Station {
Ok(())
}
async fn routes(&self, db: &SqlitePool) -> Vec<Route> {
async fn routes(&self, db: &mut SqliteConnection) -> Vec<Route> {
sqlx::query_as::<_, Route>(
"
SELECT r.id, r.name
@@ -349,7 +344,7 @@ impl Station {
.unwrap()
}
pub async fn is_in_route(&self, db: &SqlitePool, route: &Route) -> bool {
pub async fn is_in_route(&self, db: &mut SqliteConnection, route: &Route) -> bool {
for r in self.routes(db).await {
if r.id == route.id {
return true;
@@ -367,7 +362,7 @@ impl Station {
Some(datetime_utc.with_timezone(&Local))
}
pub(crate) async fn teams(&self, db: &SqlitePool) -> Vec<Team> {
pub(crate) async fn teams(&self, db: &mut SqliteConnection) -> Vec<Team> {
sqlx::query_as::<_, Team>(
"SELECT DISTINCT t.id, t.name, t.notes, t.amount_people, t.first_station_id, t.last_station_id, t.route_id
FROM team t
@@ -381,7 +376,7 @@ ORDER BY LOWER(t.name);",
.unwrap()
}
pub(crate) async fn left_teams(&self, db: &SqlitePool) -> Vec<Team> {
pub(crate) async fn left_teams(&self, db: &mut SqliteConnection) -> Vec<Team> {
sqlx::query_as::<_, Team>(
"SELECT t.id, t.name, t.notes, t.amount_people, t.first_station_id, t.last_station_id, t.route_id
FROM team t
@@ -395,24 +390,20 @@ AND r.left_at IS NOT NULL;",
.unwrap()
}
pub async fn teams_on_the_way(&self, db: &SqlitePool) -> Vec<TeamOnTheWay> {
pub async fn teams_on_the_way(&self, db: &mut SqliteConnection) -> Vec<TeamOnTheWay> {
let mut ret = Vec::new();
let teams = self.teams(db).await;
let teams = self.teams(&mut *db).await;
let missing_teams: Vec<Team> = stream::iter(teams)
.filter_map(|entry| async move {
if entry.been_at_station(db, self).await {
None
} else {
Some(entry)
}
})
.collect()
.await;
let mut missing_teams = Vec::new();
for team in teams {
if !team.been_at_station(&mut *db, self).await {
missing_teams.push(team);
}
}
for team in missing_teams {
let route = team.route(db).await;
let route = team.route(&mut *db).await;
let Some(prev_station) = route.prev_station(db, self).await else {
continue;
};
@@ -433,7 +424,7 @@ AND r.left_at IS NOT NULL;",
}
}
pub async fn some_team_has_last_station_id(db: &SqlitePool) -> bool {
pub async fn some_team_has_last_station_id(db: &mut SqliteConnection) -> bool {
sqlx::query_scalar!("SELECT 1 FROM team WHERE last_station_id IS NOT NULL")
.fetch_optional(db)
.await

View File

@@ -1,7 +1,7 @@
use crate::Station;
use serde::Serialize;
use sqlx::SqlitePool;
use std::ops::DerefMut;
use sqlx::Acquire;
use sqlx::SqliteConnection;
use thiserror::Error;
#[derive(Error, Debug, PartialEq, Serialize)]
@@ -11,14 +11,14 @@ pub(crate) enum UpdateAmountPeopleError {
}
impl Station {
pub(crate) async fn update_name(&self, db: &SqlitePool, name: &str) {
pub(crate) async fn update_name(&self, db: &mut SqliteConnection, name: &str) {
sqlx::query!("UPDATE station SET name = ? WHERE id = ?", name, self.id)
.execute(db)
.await
.unwrap();
}
pub(crate) async fn update_notes(&self, db: &SqlitePool, notes: &str) {
pub(crate) async fn update_notes(&self, db: &mut SqliteConnection, notes: &str) {
sqlx::query!("UPDATE station SET notes = ? WHERE id = ?", notes, self.id)
.execute(db)
.await
@@ -27,7 +27,7 @@ impl Station {
pub(crate) async fn update_amount_people(
&self,
db: &SqlitePool,
db: &mut SqliteConnection,
amount_people: i64,
) -> Result<(), UpdateAmountPeopleError> {
let mut transaction = db.begin().await.unwrap();
@@ -37,18 +37,18 @@ impl Station {
amount_people,
self.id
)
.execute(transaction.deref_mut())
.execute(&mut *transaction)
.await
.unwrap();
if amount_people == 0 {
let teams = self.teams(db).await;
let teams = self.teams(&mut transaction).await;
for team in teams {
let route = team.route(db).await;
let Some(station) = route.get_next_first_station_tx(&mut transaction).await else {
let route = team.route(&mut transaction).await;
let Some(station) = route.get_next_first_station(&mut transaction).await else {
return Err(UpdateAmountPeopleError::LastStationCantBeCrewlessIfTeamExists);
};
team.update_first_station_tx(&mut transaction, &station)
team.update_first_station(transaction.as_mut(), &station)
.await;
}
}
@@ -58,7 +58,7 @@ impl Station {
Ok(())
}
pub(crate) async fn update_amount_people_reset(&self, db: &SqlitePool) {
pub(crate) async fn update_amount_people_reset(&self, db: &mut SqliteConnection) {
sqlx::query!(
"UPDATE station SET amount_people = NULL WHERE id = ?",
self.id
@@ -68,7 +68,7 @@ impl Station {
.unwrap();
}
pub(crate) async fn update_location(&self, db: &SqlitePool, lat: f64, lng: f64) {
pub(crate) async fn update_location(&self, db: &mut SqliteConnection, lat: f64, lng: f64) {
sqlx::query!(
"UPDATE station SET lat = ?, lng = ? WHERE id = ?",
lat,
@@ -80,7 +80,7 @@ impl Station {
.unwrap();
}
pub(crate) async fn update_location_clear(&self, db: &SqlitePool) {
pub(crate) async fn update_location_clear(&self, db: &mut SqliteConnection) {
sqlx::query!(
"UPDATE station SET lat = NULL, lng=NULL WHERE id = ?",
self.id
@@ -103,30 +103,30 @@ mod test {
#[sqlx::test]
async fn succ_update_not_last_crewful_station() {
let pool = testdb!();
let db = &mut *pool.acquire().await.unwrap();
let station = Station::create(&pool, "Teststation").await.unwrap();
let crew_station = Station::create(&pool, "Bemannte Teststation")
.await
.unwrap();
let route = Route::create(&pool, "Testroute").await.unwrap();
route.add_station(&pool, &station).await.unwrap();
route.add_station(&pool, &crew_station).await.unwrap();
let _ = Team::create(&pool, "Testteam", &route).await.unwrap();
let station = Station::create(db, "Teststation").await.unwrap();
let crew_station = Station::create(db, "Bemannte Teststation").await.unwrap();
let route = Route::create(db, "Testroute").await.unwrap();
route.add_station(db, &station).await.unwrap();
route.add_station(db, &crew_station).await.unwrap();
let _ = Team::create(db, "Testteam", &route).await.unwrap();
assert_eq!(station.update_amount_people(&pool, 0).await, Ok(()));
assert_eq!(station.update_amount_people(db, 0).await, Ok(()));
}
#[sqlx::test]
async fn fail_update_last_crewful_station() {
let pool = testdb!();
let db = &mut *pool.acquire().await.unwrap();
let station = Station::create(&pool, "Teststation").await.unwrap();
let route = Route::create(&pool, "Testroute").await.unwrap();
route.add_station(&pool, &station).await.unwrap();
let _ = Team::create(&pool, "Testteam", &route).await.unwrap();
let station = Station::create(db, "Teststation").await.unwrap();
let route = Route::create(db, "Testroute").await.unwrap();
route.add_station(db, &station).await.unwrap();
let _ = Team::create(db, "Testteam", &route).await.unwrap();
assert_eq!(
station.update_amount_people(&pool, 0).await,
station.update_amount_people(db, 0).await,
Err(UpdateAmountPeopleError::LastStationCantBeCrewlessIfTeamExists)
);
}

View File

@@ -73,9 +73,9 @@ pub(crate) async fn station_pdf(stations: Vec<Station>) -> Vec<u8> {
write!(
content,
r#")
r")
#create_card_grid(cards)"#
#create_card_grid(cards)"
)
.unwrap();
@@ -92,7 +92,9 @@ pub(crate) async fn station_pdf(stations: Vec<Station>) -> Vec<u8> {
}
async fn index(State(db): State<Arc<SqlitePool>>) -> impl IntoResponse {
let stations = Station::all(&db).await;
let db = &mut *db.acquire().await.unwrap();
let stations = Station::all(db).await;
let pdf = station_pdf(stations).await;
(

View File

@@ -54,15 +54,14 @@ impl TypstWrapperWorld {
source: Source::detached(source),
time: time::OffsetDateTime::now_utc(),
cache_directory: std::env::var_os("CACHE_DIRECTORY")
.map(|os_path| os_path.into())
.unwrap_or(std::env::temp_dir()),
.map_or(std::env::temp_dir(), std::convert::Into::into),
http: ureq::Agent::new_with_defaults(),
files: Arc::new(Mutex::new(HashMap::new())),
}
}
}
/// A File that will be stored in the HashMap.
/// A File that will be stored in the `HashMap`.
#[derive(Clone, Debug)]
struct FileEntry {
bytes: Bytes,

View File

@@ -15,6 +15,7 @@ use axum::{
use maud::{html, Markup};
use serde::Deserialize;
use sqlx::SqlitePool;
use std::fmt::Write;
use std::{collections::HashMap, sync::Arc};
use tower_sessions::Session;
@@ -28,7 +29,9 @@ async fn create(
session: Session,
Form(form): Form<CreateForm>,
) -> impl IntoResponse {
match Station::create(&db, &form.name).await {
let db = &mut *db.acquire().await.unwrap();
match Station::create(db, &form.name).await {
Ok(_) => suc!(session, t!("station_create_succ", name = form.name)),
Err(e) => er!(
session,
@@ -48,12 +51,14 @@ async fn delete(
session: Session,
axum::extract::Path(id): axum::extract::Path<i64>,
) -> impl IntoResponse {
let Some(station) = Station::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(station) = Station::find_by_id(db, id).await else {
er!(session, t!("nonexisting_station", id = id));
return Redirect::to("/admin/station");
};
match station.delete(&db).await {
match station.delete(db).await {
Ok(()) => suc!(session, t!("station_delete_succ", name = station.name)),
Err(e) => er!(
session,
@@ -73,12 +78,14 @@ async fn view(
session: Session,
axum::extract::Path(id): axum::extract::Path<i64>,
) -> Result<Markup, impl IntoResponse> {
let Some(station) = Station::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(station) = Station::find_by_id(db, id).await else {
er!(session, t!("nonexisting_station", id = id));
return Err(Redirect::to("/admin/station"));
};
let ratings = Rating::for_station(&db, &station).await;
let ratings = Rating::for_station(db, &station).await;
// maybe switch to maud-display impl of station
let content = html! {
@@ -226,7 +233,7 @@ async fn view(
tr {
td {
a href=(format!("/admin/team/{}", rating.team_id)) {
(rating.team(&db).await.name)
(rating.team(db).await.name)
}
}
td {
@@ -348,12 +355,14 @@ async fn update_name(
axum::extract::Path(id): axum::extract::Path<i64>,
Form(form): Form<UpdateNameForm>,
) -> impl IntoResponse {
let Some(station) = Station::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(station) = Station::find_by_id(db, id).await else {
er!(session, t!("nonexisting_station", id = id));
return Redirect::to("/admin/station");
};
station.update_name(&db, &form.name).await;
station.update_name(db, &form.name).await;
suc!(
session,
@@ -373,12 +382,14 @@ async fn update_notes(
axum::extract::Path(id): axum::extract::Path<i64>,
Form(form): Form<UpdateNotesForm>,
) -> impl IntoResponse {
let Some(station) = Station::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(station) = Station::find_by_id(db, id).await else {
er!(session, t!("nonexisting_station", id = id));
return Redirect::to("/admin/station");
};
station.update_notes(&db, &form.notes).await;
station.update_notes(db, &form.notes).await;
suc!(session, t!("station_new_notes", station = station.name));
@@ -395,18 +406,20 @@ async fn update_amount_people(
axum::extract::Path(id): axum::extract::Path<i64>,
Form(form): Form<UpdateAmountPeopleForm>,
) -> impl IntoResponse {
let Some(station) = Station::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(station) = Station::find_by_id(db, id).await else {
er!(session, t!("nonexisting_station", id = id));
return Redirect::to("/admin/station");
};
match station.update_amount_people(&db, form.amount_people).await {
match station.update_amount_people(db, form.amount_people).await {
Ok(()) => suc!(
session,
t!("station_new_crew_amount", station = station.name)
),
Err(UpdateAmountPeopleError::LastStationCantBeCrewlessIfTeamExists) => {
er!(session, t!("last_station_has_to_be_crewful"))
er!(session, t!("last_station_has_to_be_crewful"));
}
}
@@ -418,12 +431,14 @@ async fn update_amount_people_reset(
session: Session,
axum::extract::Path(id): axum::extract::Path<i64>,
) -> impl IntoResponse {
let Some(station) = Station::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(station) = Station::find_by_id(&mut *db, id).await else {
er!(session, t!("nonexisting_station", id = id));
return Redirect::to("/admin/station");
};
station.update_amount_people_reset(&db).await;
station.update_amount_people_reset(db).await;
suc!(
session,
@@ -438,12 +453,14 @@ async fn quick(
session: Session,
axum::extract::Path(id): axum::extract::Path<i64>,
) -> Result<Markup, impl IntoResponse> {
let Some(station) = Station::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(station) = Station::find_by_id(db, id).await else {
er!(session, t!("nonexisting_station", id = id));
return Err(Redirect::to("/admin/station"));
};
let teams = station.teams(&db).await;
let teams = station.teams(db).await;
// maybe switch to maud-display impl of team
let content = html! {
@@ -470,7 +487,7 @@ async fn quick(
}
}
td {
@if let Some(rating) = Rating::find_by_team_and_station(&db, team, &station).await {
@if let Some(rating) = Rating::find_by_team_and_station(db, team, &station).await {
a href=(format!("/s/{}/{}", station.id, station.pw)){
@if let Some(points) = rating.points {
em data-tooltip=(t!("already_entered")) {
@@ -506,7 +523,9 @@ async fn quick_post(
axum::extract::Path(id): axum::extract::Path<i64>,
Form(form): Form<QuickUpdate>,
) -> impl IntoResponse {
let Some(station) = Station::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(station) = Station::find_by_id(db, id).await else {
er!(session, t!("nonexisting_station", id = id));
return Redirect::to("/admin/station");
};
@@ -516,25 +535,27 @@ async fn quick_post(
for (team_id, points) in &form.fields {
let Ok(team_id) = team_id.parse::<i64>() else {
ret.push_str(&format!(
let _ = write!(
ret,
"Skipped team_id={team_id} because this id can't be parsed as i64"
));
);
continue;
};
let Ok(points) = points.parse::<i64>() else {
ret.push_str(&format!(
"Skipped team_id={team_id} because points {} can't be parsed as i64",
points
));
let _ = write!(
ret,
"Skipped team_id={team_id} because {points} points can't be parsed as i64",
);
continue;
};
let Some(team) = Team::find_by_id(&db, team_id).await else {
ret.push_str(&format!(
let Some(team) = Team::find_by_id(db, team_id).await else {
let _ = write!(
ret,
"Skipped team_id={team_id} because this team does not exist"
));
);
continue;
};
if Rating::find_by_team_and_station(&db, &team, &station)
if Rating::find_by_team_and_station(db, &team, &station)
.await
.is_some()
{
@@ -548,7 +569,7 @@ async fn quick_post(
continue;
}
Rating::create_quick(&db, &team, &station, points).await;
Rating::create_quick(db, &team, &station, points).await;
amount_succ += 1;
}
@@ -577,12 +598,14 @@ async fn update_location(
axum::extract::Path(id): axum::extract::Path<i64>,
Form(form): Form<UpdateLocationForm>,
) -> impl IntoResponse {
let Some(station) = Station::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(station) = Station::find_by_id(db, id).await else {
er!(session, t!("nonexisting_station", id = id));
return Redirect::to("/admin/station");
};
station.update_location(&db, form.lat, form.lng).await;
station.update_location(db, form.lat, form.lng).await;
suc!(session, t!("location_changed", station = station.name));
@@ -594,12 +617,14 @@ async fn update_location_clear(
session: Session,
axum::extract::Path(id): axum::extract::Path<i64>,
) -> impl IntoResponse {
let Some(station) = Station::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(station) = Station::find_by_id(db, id).await else {
er!(session, t!("nonexisting_station", id = id));
return Redirect::to("/admin/station");
};
station.update_location_clear(&db).await;
station.update_location_clear(db).await;
suc!(session, t!("location_deleted", station = station.name));
@@ -607,7 +632,9 @@ async fn update_location_clear(
}
async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
let stations = Station::all(&db).await;
let db = &mut *db.acquire().await.unwrap();
let stations = Station::all(db).await;
let content = html! {
h1 {
@@ -641,7 +668,7 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
}
tbody {
@for station in &stations {
@let status = TeamsAtStationLocation::for_station(&db, station).await;
@let status = TeamsAtStationLocation::for_station(db, station).await;
tr {
td {
@if station.ready {
@@ -649,7 +676,7 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
small { "🟢 " }
}
}
@if station.routes(&db).await.is_empty() {
@if station.routes(db).await.is_empty() {
a href="/admin/route" {
em data-tooltip=(t!("station_warning_not_assigned_route")) {
"⚠️ "
@@ -667,8 +694,8 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
}
td {
em data-tooltip=(t!("station_team_progress", arrived=status.total_teams-status.not_yet_here.len() as i64, total=status.total_teams, waiting= status.waiting.len(), active=status.doing.len() )) {
@if status.not_yet_here.len() == 0 {
@if status.waiting.len() == 0 && status.doing.len() == 0 {
@if status.not_yet_here.is_empty() {
@if status.waiting.is_empty() && status.doing.is_empty() {
""
}@else{
"🔜"

View File

@@ -7,8 +7,7 @@ use axum::Router;
use chrono::{DateTime, Local, NaiveDateTime, Utc};
use maud::{html, Markup, Render};
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
use std::ops::DerefMut;
use sqlx::{FromRow, SqliteConnection};
mod web;
@@ -41,7 +40,7 @@ pub(crate) struct LastContactTeam {
}
impl LastContactTeam {
pub(crate) async fn all_sort_missing(db: &SqlitePool) -> Vec<Self> {
pub(crate) async fn all_sort_missing(db: &mut SqliteConnection) -> Vec<Self> {
let rows = sqlx::query_as::<_, (i64, i64, Option<NaiveDateTime>)>(
"SELECT
t.id AS team_id,
@@ -65,13 +64,15 @@ LEFT JOIN station s ON last_contact.station_id = s.id
ORDER BY
last_contact.last_contact_time DESC",
)
.fetch_all(db)
.fetch_all(&mut *db)
.await
.unwrap();
let mut ret = Vec::new();
for (team_id, station_id, last_contact_time) in rows {
ret.push(LastContactTeam {
team: Team::find_by_id(db, team_id).await.expect("db constraints"),
team: Team::find_by_id(&mut *db, team_id)
.await
.expect("db constraints"),
station: Station::find_by_id(db, station_id).await,
last_contact_time,
});
@@ -95,7 +96,7 @@ pub(crate) enum CreateError {
}
impl Team {
pub(crate) async fn all(db: &SqlitePool) -> Vec<Self> {
pub(crate) async fn all(db: &mut SqliteConnection) -> Vec<Self> {
sqlx::query_as::<_, Self>(
"SELECT id, name, notes, amount_people, first_station_id, last_station_id, route_id FROM team ORDER BY name;",
)
@@ -104,7 +105,7 @@ impl Team {
.unwrap()
}
pub(crate) async fn all_with_route(db: &SqlitePool, route: &Route) -> Vec<Self> {
pub(crate) async fn all_with_route(db: &mut SqliteConnection, route: &Route) -> Vec<Self> {
sqlx::query_as!(
Team,
"SELECT id, name, notes, amount_people, first_station_id, last_station_id, route_id FROM team WHERE route_id = ?;",
@@ -115,7 +116,10 @@ impl Team {
.unwrap()
}
pub(crate) async fn all_with_first_station(db: &SqlitePool, station: &Station) -> Vec<Self> {
pub(crate) async fn all_with_first_station(
db: &mut SqliteConnection,
station: &Station,
) -> Vec<Self> {
sqlx::query_as!(
Team,
"select id, name, notes, amount_people, first_station_id, last_station_id, route_id from team where first_station_id = ?;",
@@ -126,7 +130,10 @@ impl Team {
.unwrap()
}
pub(crate) async fn all_with_last_station(db: &SqlitePool, station: &Station) -> Vec<Self> {
pub(crate) async fn all_with_last_station(
db: &mut SqliteConnection,
station: &Station,
) -> Vec<Self> {
sqlx::query_as!(
Team,
"select id, name, notes, amount_people, first_station_id, last_station_id, route_id from team where last_station_id = ?;",
@@ -137,7 +144,7 @@ impl Team {
.unwrap()
}
pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> {
pub async fn find_by_id(db: &mut SqliteConnection, id: i64) -> Option<Self> {
sqlx::query_as!(
Self,
"SELECT id, name, notes, amount_people, first_station_id, last_station_id, route_id FROM team WHERE id = ?",
@@ -149,7 +156,7 @@ impl Team {
}
pub(crate) async fn create(
db: &SqlitePool,
db: &mut SqliteConnection,
name: &str,
route: &Route,
) -> Result<i64, CreateError> {
@@ -171,14 +178,14 @@ impl Team {
Ok(result.id.unwrap())
}
async fn update_name(&self, db: &SqlitePool, name: &str) {
async fn update_name(&self, db: &mut SqliteConnection, name: &str) {
sqlx::query!("UPDATE team SET name = ? WHERE id = ?", name, self.id)
.execute(db)
.await
.unwrap();
}
async fn update_end_station(&self, db: &SqlitePool, station: &Station) {
async fn update_end_station(&self, db: &mut SqliteConnection, station: &Station) {
sqlx::query!(
"UPDATE team SET last_station_id = ? WHERE id = ?",
station.id,
@@ -189,14 +196,14 @@ impl Team {
.unwrap();
}
async fn update_notes(&self, db: &SqlitePool, notes: &str) {
async fn update_notes(&self, db: &mut SqliteConnection, notes: &str) {
sqlx::query!("UPDATE team SET notes = ? WHERE id = ?", notes, self.id)
.execute(db)
.await
.unwrap();
}
async fn update_amount_people(&self, db: &SqlitePool, amount_people: i64) {
async fn update_amount_people(&self, db: &mut SqliteConnection, amount_people: i64) {
sqlx::query!(
"UPDATE team SET amount_people = ? WHERE id = ?",
amount_people,
@@ -207,7 +214,7 @@ impl Team {
.unwrap();
}
async fn update_route(&self, db: &SqlitePool, route: &Route) -> Result<String, ()> {
async fn update_route(&self, db: &mut SqliteConnection, route: &Route) -> Result<String, ()> {
let Some(station) = route.get_next_first_station(db).await else {
return Err(());
};
@@ -225,7 +232,7 @@ impl Team {
Ok(station.name)
}
pub(crate) async fn update_first_station(&self, db: &SqlitePool, station: &Station) {
pub(crate) async fn update_first_station(&self, db: &mut SqliteConnection, station: &Station) {
sqlx::query!(
"UPDATE team SET first_station_id = ? WHERE id = ?",
station.id,
@@ -236,22 +243,7 @@ impl Team {
.unwrap();
}
pub(crate) async fn update_first_station_tx(
&self,
db: &mut Transaction<'_, Sqlite>,
station: &Station,
) {
sqlx::query!(
"UPDATE team SET first_station_id = ? WHERE id = ?",
station.id,
self.id
)
.execute(db.deref_mut())
.await
.unwrap();
}
async fn update_last_station(&self, db: &SqlitePool, station: &Station) {
async fn update_last_station(&self, db: &mut SqliteConnection, station: &Station) {
sqlx::query!(
"UPDATE team SET last_station_id = ? WHERE id = ?",
station.id,
@@ -262,14 +254,14 @@ impl Team {
.unwrap();
}
async fn update_amount_people_reset(&self, db: &SqlitePool) {
async fn update_amount_people_reset(&self, db: &mut SqliteConnection) {
sqlx::query!("UPDATE team SET amount_people = NULL WHERE id = ?", self.id)
.execute(db)
.await
.unwrap();
}
async fn delete(&self, db: &SqlitePool) -> Result<(), String> {
async fn delete(&self, db: &mut SqliteConnection) -> Result<(), String> {
sqlx::query!("DELETE FROM team WHERE id = ?", self.id)
.execute(db)
.await
@@ -277,13 +269,13 @@ impl Team {
Ok(())
}
pub async fn first_station(&self, db: &SqlitePool) -> Station {
pub async fn first_station(&self, db: &mut SqliteConnection) -> Station {
Station::find_by_id(db, self.first_station_id)
.await
.expect("db constraints")
}
pub async fn last_station(&self, db: &SqlitePool) -> Option<Station> {
pub async fn last_station(&self, db: &mut SqliteConnection) -> Option<Station> {
if let Some(last_station_id) = self.last_station_id {
Station::find_by_id(db, last_station_id).await
} else {
@@ -291,13 +283,13 @@ impl Team {
}
}
pub async fn route(&self, db: &SqlitePool) -> Route {
pub async fn route(&self, db: &mut SqliteConnection) -> Route {
Route::find_by_id(db, self.route_id)
.await
.expect("db constraints")
}
pub async fn get_curr_points(&self, db: &SqlitePool) -> i64 {
pub async fn get_curr_points(&self, db: &mut SqliteConnection) -> i64 {
sqlx::query!(
"SELECT IFNULL(sum(points), 0) as points FROM rating WHERE team_id = ?",
self.id
@@ -308,13 +300,13 @@ impl Team {
.points
}
pub async fn been_at_station(&self, db: &SqlitePool, station: &Station) -> bool {
pub async fn been_at_station(&self, db: &mut SqliteConnection, station: &Station) -> bool {
Rating::find_by_team_and_station(db, self, station)
.await
.is_some()
}
pub(crate) async fn end_station(&self, db: &SqlitePool) -> Station {
pub(crate) async fn end_station(&self, db: &mut SqliteConnection) -> Station {
match LastContactTeam::all_sort_missing(db)
.await
.into_iter()
@@ -355,7 +347,7 @@ impl Team {
}
}
pub async fn end_run(db: &SqlitePool) {
pub async fn end_run(db: &mut SqliteConnection) {
// set `last_station_id` to the next station where `left_at` is not null
let teams = Team::all(db).await;
for team in teams {
@@ -364,7 +356,7 @@ impl Team {
}
}
pub async fn restart_run(db: &SqlitePool) {
pub async fn restart_run(db: &mut SqliteConnection) {
sqlx::query!("UPDATE team SET last_station_id = null")
.execute(db)
.await

View File

@@ -14,7 +14,8 @@ use axum::{
};
use maud::{html, Markup, PreEscaped};
use serde::Deserialize;
use sqlx::SqlitePool;
use sqlx::{SqliteConnection, SqlitePool};
use std::fmt::Write;
use std::{collections::HashMap, sync::Arc};
use tower_sessions::Session;
@@ -29,13 +30,15 @@ async fn create(
session: Session,
Form(form): Form<CreateForm>,
) -> impl IntoResponse {
let Some(route) = Route::find_by_id(&db, form.route_id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(route) = Route::find_by_id(db, form.route_id).await else {
er!(session, t!("nonexisting_route", id = form.route_id));
return Redirect::to("/admin/team");
};
let id = match Team::create(&db, &form.name, &route).await {
let id = match Team::create(db, &form.name, &route).await {
Ok(id) => {
suc!(session, t!("team_created", team = form.name));
id
@@ -70,12 +73,14 @@ async fn delete(
session: Session,
axum::extract::Path(id): axum::extract::Path<i64>,
) -> impl IntoResponse {
let Some(team) = Team::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(team) = Team::find_by_id(db, id).await else {
er!(session, t!("nonexisting_team", id = id));
return Redirect::to("/admin/team");
};
match team.delete(&db).await {
match team.delete(db).await {
Ok(()) => suc!(session, t!("team_deleted", team = team.name)),
Err(e) => er!(
session,
@@ -86,7 +91,12 @@ async fn delete(
Redirect::to("/admin/team")
}
async fn quick(db: Arc<SqlitePool>, team: &Team, stations: Vec<Station>, redirect: &str) -> Markup {
async fn quick(
db: &mut SqliteConnection,
team: &Team,
stations: Vec<Station>,
redirect: &str,
) -> Markup {
html! {
h1 {
a href=(format!("/admin/team/{}", team.id)) { "↩️" }
@@ -114,7 +124,7 @@ async fn quick(db: Arc<SqlitePool>, team: &Team, stations: Vec<Station>, redirec
}
}
td {
@if let Some(rating) = Rating::find_by_team_and_station(&db, team, station).await {
@if let Some(rating) = Rating::find_by_team_and_station(db, team, station).await {
a href=(format!("/s/{}/{}", station.id, station.pw)){
@if let Some(points) = rating.points {
em data-tooltip=(t!("already_entered")) {
@@ -142,12 +152,14 @@ async fn quick_crewless(
session: Session,
axum::extract::Path(id): axum::extract::Path<i64>,
) -> Result<Markup, impl IntoResponse> {
let Some(team) = Team::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(team) = Team::find_by_id(db, id).await else {
er!(session, t!("nonexisting_team", id = id));
return Err(Redirect::to("/admin/team"));
};
let stations: Vec<Station> = team.route(&db).await.crewless_stations(&db).await;
let stations: Vec<Station> = team.route(db).await.crewless_stations(db).await;
let content = quick(db, &team, stations, "/crewless").await;
@@ -159,12 +171,14 @@ async fn quick_all(
session: Session,
axum::extract::Path(id): axum::extract::Path<i64>,
) -> Result<Markup, impl IntoResponse> {
let Some(team) = Team::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(team) = Team::find_by_id(db, id).await else {
er!(session, t!("nonexisting_team", id = id));
return Err(Redirect::to("/admin/team"));
};
let stations = team.route(&db).await.stations(&db).await;
let stations = team.route(db).await.stations(db).await;
let content = quick(db, &team, stations, "").await;
@@ -183,7 +197,9 @@ async fn quick_post(
axum::extract::Path(id): axum::extract::Path<i64>,
Form(form): Form<QuickUpdate>,
) -> impl IntoResponse {
let Some(team) = Team::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(team) = Team::find_by_id(db, id).await else {
er!(session, t!("nonexisting_team", id = id));
return Redirect::to("/admin/team");
};
@@ -193,24 +209,27 @@ async fn quick_post(
for (station_id, points) in &form.fields {
let Ok(station_id) = station_id.parse::<i64>() else {
ret.push_str(&format!(
let _ = write!(
ret,
"Skipped stationid={station_id} because this id can't be parsed as i64"
));
);
continue;
};
let Ok(points) = points.parse::<i64>() else {
ret.push_str(&format!(
"Skipped stationid={station_id} because points {points} can't be parsed as i64",
));
let _ = write!(
ret,
"Skipped stationid={station_id} because {points} points can't be parsed as i64",
);
continue;
};
let Some(station) = Station::find_by_id(&db, station_id).await else {
ret.push_str(&format!(
let Some(station) = Station::find_by_id(&mut *db, station_id).await else {
let _ = write!(
ret,
"Skipped stationid={station_id} because this station does not exist"
));
);
continue;
};
if Rating::find_by_team_and_station(&db, &team, &station)
if Rating::find_by_team_and_station(db, &team, &station)
.await
.is_some()
{
@@ -224,7 +243,7 @@ async fn quick_post(
continue;
}
Rating::create_quick(&db, &team, &station, points).await;
Rating::create_quick(db, &team, &station, points).await;
amount_succ += 1;
}
@@ -247,15 +266,17 @@ async fn view(
session: Session,
axum::extract::Path(id): axum::extract::Path<i64>,
) -> Result<Markup, impl IntoResponse> {
let Some(team) = Team::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(team) = Team::find_by_id(db, id).await else {
er!(session, t!("nonexisting_team", id = id));
return Err(Redirect::to("/admin/team"));
};
let first_station = team.first_station(&db).await;
let last_station = team.last_station(&db).await;
let routes = Route::all(&db).await;
let first_station = team.first_station(db).await;
let last_station = team.last_station(db).await;
let routes = Route::all(db).await;
let stations = team.route(&db).await.crewful_stations(&db).await;
let stations = team.route(db).await.crewful_stations(db).await;
// maybe switch to maud-display impl of team
let content = html! {
@@ -335,8 +356,8 @@ async fn view(
tr {
th scope="row" { (t!("route")) };
td {
a href=(format!("/admin/route/{}", &team.route(&db).await.id)) {
(&team.route(&db).await.name)
a href=(format!("/admin/route/{}", &team.route(db).await.id)) {
(&team.route(db).await.name)
}
@if routes.len() > 1 {
details {
@@ -344,7 +365,7 @@ async fn view(
form action=(format!("/admin/team/{}/update-route", team.id)) method="post" {
select name="route_id" aria-label=(t!("select_route")) required {
@for route in &routes {
@if route.id != team.route(&db).await.id {
@if route.id != team.route(db).await.id {
option value=(route.id) {
(route.name)
}
@@ -377,7 +398,7 @@ async fn view(
@if station.id != first_station.id {
option value=(station.id) {
(station.name)
@let amount_start_teams = Team::all_with_first_station(&db, station).await.len();
@let amount_start_teams = Team::all_with_first_station(db, station).await.len();
@if amount_start_teams > 0 {
@if amount_start_teams == 1 {
" ("
@@ -456,12 +477,14 @@ async fn update_name(
axum::extract::Path(id): axum::extract::Path<i64>,
Form(form): Form<UpdateNameForm>,
) -> impl IntoResponse {
let Some(team) = Team::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(team) = Team::find_by_id(db, id).await else {
er!(session, t!("nonexisting_team", id = id));
return Redirect::to("/admin/team");
};
team.update_name(&db, &form.name).await;
team.update_name(db, &form.name).await;
suc!(
session,
@@ -481,12 +504,14 @@ async fn update_notes(
axum::extract::Path(id): axum::extract::Path<i64>,
Form(form): Form<UpdateNotesForm>,
) -> impl IntoResponse {
let Some(team) = Team::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(team) = Team::find_by_id(db, id).await else {
er!(session, t!("nonexisting_team", id = id));
return Redirect::to("/admin/team");
};
team.update_notes(&db, &form.notes).await;
team.update_notes(db, &form.notes).await;
suc!(session, t!("notes_edited", team = team.name));
@@ -503,12 +528,14 @@ async fn update_amount_people(
axum::extract::Path(id): axum::extract::Path<i64>,
Form(form): Form<UpdateAmountPeopleForm>,
) -> impl IntoResponse {
let Some(team) = Team::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(team) = Team::find_by_id(db, id).await else {
er!(session, t!("nonexisting_team", id = id));
return Redirect::to("/admin/team");
};
team.update_amount_people(&db, form.amount_people).await;
team.update_amount_people(db, form.amount_people).await;
suc!(session, t!("amount_teammembers_edited", team = team.name));
@@ -525,18 +552,20 @@ async fn update_route(
axum::extract::Path(id): axum::extract::Path<i64>,
Form(form): Form<UpdateRouteForm>,
) -> impl IntoResponse {
let db = &mut *db.acquire().await.unwrap();
// TODO: move sanity checks into mod.rs
let Some(team) = Team::find_by_id(&db, id).await else {
let Some(team) = Team::find_by_id(db, id).await else {
er!(session, t!("nonexisting_team", id = id));
return Redirect::to("/admin/team");
};
let Some(route) = Route::find_by_id(&db, form.route_id).await else {
let Some(route) = Route::find_by_id(db, form.route_id).await else {
er!(session, t!("nonexisting_route", id = form.route_id));
return Redirect::to(&format!("/admin/team/{id}"));
};
match team.update_route(&db, &route).await {
match team.update_route(db, &route).await {
Ok(new_first_station_name) => suc!(
session,
t!(
@@ -569,12 +598,14 @@ async fn update_first_station(
axum::extract::Path(id): axum::extract::Path<i64>,
Form(form): Form<UpdateFirstStationForm>,
) -> impl IntoResponse {
let Some(team) = Team::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(team) = Team::find_by_id(db, id).await else {
er!(session, t!("nonexisting_team", id = id));
return Redirect::to("/admin/team");
};
let Some(station) = Station::find_by_id(&db, form.first_station_id).await else {
let Some(station) = Station::find_by_id(&mut *db, form.first_station_id).await else {
er!(
session,
t!("nonexisting_station", id = form.first_station_id)
@@ -583,21 +614,22 @@ async fn update_first_station(
return Redirect::to(&format!("/admin/team/{id}"));
};
if !station.is_in_route(&db, &team.route(&db).await).await {
let route = team.route(db).await;
if !station.is_in_route(db, &route).await {
er!(
session,
t!(
"first_station_not_edited_not_on_route",
station = station.name,
team = team.name,
route = team.route(&db).await.name
route = team.route(db).await.name
)
);
return Redirect::to(&format!("/admin/team/{id}"));
}
team.update_first_station(&db, &station).await;
team.update_first_station(db, &station).await;
suc!(
session,
@@ -621,12 +653,14 @@ async fn update_last_station(
axum::extract::Path(id): axum::extract::Path<i64>,
Form(form): Form<UpdateLastStationForm>,
) -> impl IntoResponse {
let Some(team) = Team::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(team) = Team::find_by_id(db, id).await else {
er!(session, t!("nonexisting_team", id = id));
return Redirect::to("/admin/team");
};
let Some(station) = Station::find_by_id(&db, form.last_station_id).await else {
let Some(station) = Station::find_by_id(&mut *db, form.last_station_id).await else {
er!(
session,
t!("nonexisting_station", id = form.last_station_id)
@@ -634,21 +668,22 @@ async fn update_last_station(
return Redirect::to(&format!("/admin/team/{id}"));
};
if !station.is_in_route(&db, &team.route(&db).await).await {
let route = team.route(db).await;
if !station.is_in_route(db, &route).await {
er!(
session,
t!(
"last_station_not_edited_not_on_route",
station = station.name,
team = team.name,
route = team.route(&db).await.name
route = team.route(db).await.name
)
);
return Redirect::to(&format!("/admin/team/{id}"));
}
team.update_last_station(&db, &station).await;
team.update_last_station(db, &station).await;
suc!(
session,
@@ -667,12 +702,14 @@ async fn update_amount_people_reset(
session: Session,
axum::extract::Path(id): axum::extract::Path<i64>,
) -> impl IntoResponse {
let Some(team) = Team::find_by_id(&db, id).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(team) = Team::find_by_id(db, id).await else {
er!(session, t!("nonexisting_team", id = id));
return Redirect::to("/admin/team");
};
team.update_amount_people_reset(&db).await;
team.update_amount_people_reset(db).await;
suc!(session, t!("amount_teammembers_edited", team = team.name));
@@ -680,7 +717,9 @@ async fn update_amount_people_reset(
}
async fn lost(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
let losts = LastContactTeam::all_sort_missing(&db).await;
let db = &mut *db.acquire().await.unwrap();
let losts = LastContactTeam::all_sort_missing(db).await;
let content = html! {
h1 {
@@ -731,8 +770,10 @@ async fn lost(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
}
async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
let teams = Team::all(&db).await;
let routes = Route::all(&db).await;
let db = &mut *db.acquire().await.unwrap();
let teams = Team::all(db).await;
let routes = Route::all(db).await;
let content = html! {
h1 {
@@ -792,7 +833,7 @@ async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
@for route in &routes {
h2 { (route.name) }
ol {
@for team in &route.teams(&db).await{
@for team in &route.teams(db).await{
li {
a href=(format!("/admin/team/{}", team.id)){
(team.name)

View File

@@ -302,7 +302,7 @@ pub async fn start(listener: TcpListener, db: SqlitePool) {
tokio::spawn(async move {
// Kick-off typst compilation, to reduce wait time for 1st load
let stations = Station::all(&db).await;
let stations = Station::all(&mut db.acquire().await.unwrap()).await;
station_pdf(stations).await;
});

View File

@@ -4,7 +4,7 @@ use crate::{
};
use chrono::{DateTime, Local, NaiveDateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, SqlitePool};
use sqlx::{FromRow, SqliteConnection};
use std::collections::HashMap;
#[derive(FromRow, Debug, Serialize, Deserialize)]
@@ -20,7 +20,7 @@ pub(crate) struct Rating {
impl Rating {
pub(crate) async fn create(
db: &SqlitePool,
db: &mut SqliteConnection,
station: &Station,
team: &Team,
) -> Result<(), String> {
@@ -35,7 +35,12 @@ impl Rating {
Ok(())
}
pub(crate) async fn create_quick(db: &SqlitePool, team: &Team, station: &Station, points: i64) {
pub(crate) async fn create_quick(
db: &mut SqliteConnection,
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,
@@ -47,19 +52,19 @@ impl Rating {
.unwrap();
}
pub(crate) async fn team(&self, db: &SqlitePool) -> Team {
pub(crate) async fn team(&self, db: &mut SqliteConnection) -> Team {
Team::find_by_id(db, self.team_id)
.await
.expect("db constraints")
}
pub(crate) async fn station(&self, db: &SqlitePool) -> Station {
pub(crate) async fn station(&self, db: &mut SqliteConnection) -> Station {
Station::find_by_id(db, self.station_id)
.await
.expect("db constraints")
}
pub(crate) async fn for_station(db: &SqlitePool, station: &Station) -> Vec<Self> {
pub(crate) async fn for_station(db: &mut SqliteConnection, 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)
@@ -68,7 +73,7 @@ impl Rating {
}
pub(crate) async fn update(
db: &SqlitePool,
db: &mut SqliteConnection,
station: &Station,
team: &Team,
points: Option<i64>,
@@ -88,7 +93,7 @@ impl Rating {
}
pub(crate) async fn delete(
db: &SqlitePool,
db: &mut SqliteConnection,
station: &Station,
team: &Team,
) -> Result<(), String> {
@@ -103,7 +108,7 @@ impl Rating {
Ok(())
}
pub async fn find_by_team_and_station(
db: &SqlitePool,
db: &mut SqliteConnection,
team: &Team,
station: &Station,
) -> Option<Self> {
@@ -152,7 +157,10 @@ pub(crate) struct TeamsAtStationLocation {
}
impl TeamsAtStationLocation {
pub(crate) async fn for_station(db: &SqlitePool, station: &Station) -> TeamsAtStationLocation {
pub(crate) async fn for_station(
db: &mut SqliteConnection,
station: &Station,
) -> TeamsAtStationLocation {
let teams = station.teams(db).await;
let total_teams = teams.len() as i64;
@@ -165,27 +173,24 @@ impl TeamsAtStationLocation {
let mut done = true;
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 {
done = false;
left_not_yet_rated.push((team, rating));
}
} else if rating.started_at.is_some() {
done = false;
doing.push((team, rating));
if let Some(rating) = Rating::find_by_team_and_station(db, &team, station).await {
if rating.left_at.is_some() {
if rating.points.is_some() {
left_and_rated.push((team, rating));
} else {
done = false;
waiting.push((team, rating));
left_not_yet_rated.push((team, rating));
}
}
None => {
} else if rating.started_at.is_some() {
done = false;
not_yet_here.push(team)
doing.push((team, rating));
} else {
done = false;
waiting.push((team, rating));
}
} else {
done = false;
not_yet_here.push(team);
}
}
@@ -203,9 +208,9 @@ impl TeamsAtStationLocation {
not_yet_here_by_route,
waiting,
doing,
done,
left_not_yet_rated,
left_and_rated,
done,
}
}
}

View File

@@ -21,7 +21,9 @@ async fn view(
session: Session,
axum::extract::Path((id, code)): axum::extract::Path<(i64, String)>,
) -> Markup {
let Some(station) = Station::login(&db, id, &code).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(station) = Station::login(db, id, &code).await else {
let content = html! {
article class="error" {
(t!("invalid_rating_code"))
@@ -30,9 +32,9 @@ async fn view(
return partials::page(content, session, false).await;
};
let teams = TeamsAtStationLocation::for_station(&db, &station).await;
let teams_on_the_way = station.teams_on_the_way(&db).await;
let status = RunStatus::curr(&db).await;
let teams = TeamsAtStationLocation::for_station(db, &station).await;
let teams_on_the_way = station.teams_on_the_way(db).await;
let status = RunStatus::curr(db).await;
let content = html! {
h1 {
@@ -47,7 +49,7 @@ async fn view(
"👋"
(t!("station_info"))
" "
@let first_teams = Team::all_with_first_station(&db, &station).await;
@let first_teams = Team::all_with_first_station(db, &station).await;
@if first_teams.is_empty() {
(t!("station_has_no_teams_to_take_to_start"))
} @else{
@@ -145,7 +147,7 @@ async fn view(
}
@if status == RunStatus::HasEnded {
(t!("station_done"))
@let teams_to_take_home = Team::all_with_last_station(&db, &station).await;
@let teams_to_take_home = Team::all_with_last_station(db, &station).await;
@if !teams_to_take_home.is_empty() {
@if teams_to_take_home.len() == 1 {
(t!("take_home_the_following_team"))
@@ -379,12 +381,14 @@ async fn ready(
session: Session,
axum::extract::Path((id, code)): axum::extract::Path<(i64, String)>,
) -> impl IntoResponse {
let Some(station) = Station::login(&db, id, &code).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(station) = Station::login(db, id, &code).await else {
er!(session, t!("invalid_rating_code"));
return Redirect::to("/s/{id}/{code}");
};
station.switch_ready(&db).await;
station.switch_ready(db).await;
suc!(session, t!("succ_change"));
Redirect::to(&format!("/s/{id}/{code}"))
@@ -400,16 +404,18 @@ async fn new_waiting(
axum::extract::Path((id, code)): axum::extract::Path<(i64, String)>,
Form(form): Form<NewWaitingForm>,
) -> impl IntoResponse {
let Some(station) = Station::login(&db, id, &code).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(station) = Station::login(db, id, &code).await else {
er!(session, t!("invalid_rating_code"));
return Redirect::to("/s/{id}/{code}");
};
let Some(team) = Team::find_by_id(&db, form.team_id).await else {
let Some(team) = Team::find_by_id(db, form.team_id).await else {
er!(session, t!("nonexisting_team", id = form.team_id));
return Redirect::to("/s/{id}/{code}");
};
match station.new_team_waiting(&db, &team).await {
match station.new_team_waiting(db, &team).await {
Ok(()) => suc!(session, t!("team_added_to_waiting", team = team.name)),
Err(e) => err!(session, "{e}"),
}
@@ -422,17 +428,19 @@ async fn remove_waiting(
session: Session,
axum::extract::Path((id, code, team_id)): axum::extract::Path<(i64, String, i64)>,
) -> impl IntoResponse {
let Some(station) = Station::login(&db, id, &code).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(station) = Station::login(db, id, &code).await else {
er!(session, t!("invalid_rating_code"));
return Redirect::to("/s/{id}/{code}");
};
let Some(team) = Team::find_by_id(&db, team_id).await else {
let Some(team) = Team::find_by_id(db, team_id).await else {
er!(session, t!("nonexisting_team", id = team_id));
return Redirect::to("/s/{id}/{code}");
};
match station.remove_team_waiting(&db, &team).await {
match station.remove_team_waiting(db, &team).await {
Ok(()) => suc!(session, t!("team_removed_from_waiting", team = team.name)),
Err(e) => err!(session, "{e}"),
}
@@ -445,17 +453,18 @@ async fn team_starting(
session: Session,
axum::extract::Path((id, code, team_id)): axum::extract::Path<(i64, String, i64)>,
) -> impl IntoResponse {
let Some(station) = Station::login(&db, id, &code).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(station) = Station::login(&mut *db, id, &code).await else {
er!(session, t!("invalid_rating_code"));
return Redirect::to("/s/{id}/{code}");
};
let Some(team) = Team::find_by_id(&db, team_id).await else {
let Some(team) = Team::find_by_id(&mut *db, team_id).await else {
er!(session, t!("nonexisting_team", id = team_id));
return Redirect::to("/s/{id}/{code}");
};
match station.team_starting(&db, &team).await {
match station.team_starting(&mut *db, &team).await {
Ok(()) => suc!(session, t!("team_added_to_active", team = team.name)),
Err(e) => err!(session, "{e}"),
}
@@ -468,17 +477,19 @@ async fn remove_doing(
session: Session,
axum::extract::Path((id, code, team_id)): axum::extract::Path<(i64, String, i64)>,
) -> impl IntoResponse {
let Some(station) = Station::login(&db, id, &code).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(station) = Station::login(db, id, &code).await else {
er!(session, t!("invalid_rating_code"));
return Redirect::to("/s/{id}/{code}");
};
let Some(team) = Team::find_by_id(&db, team_id).await else {
let Some(team) = Team::find_by_id(db, team_id).await else {
er!(session, t!("nonexisting_team", id = id));
return Redirect::to("/s/{id}/{code}");
};
match station.remove_team_doing(&db, &team).await {
match station.remove_team_doing(db, &team).await {
Ok(()) => suc!(session, t!("team_removed_from_active", team = team.name)),
Err(e) => err!(session, "{e}"),
}
@@ -491,17 +502,19 @@ async fn team_finished(
session: Session,
axum::extract::Path((id, code, team_id)): axum::extract::Path<(i64, String, i64)>,
) -> impl IntoResponse {
let Some(station) = Station::login(&db, id, &code).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(station) = Station::login(db, id, &code).await else {
er!(session, t!("invalid_rating_code"));
return Redirect::to("/s/{id}/{code}");
};
let Some(team) = Team::find_by_id(&db, team_id).await else {
let Some(team) = Team::find_by_id(db, team_id).await else {
er!(session, t!("nonexisting_team", id = id));
return Redirect::to("/s/{id}/{code}");
};
match station.team_finished(&db, &team).await {
match station.team_finished(db, &team).await {
Ok(()) => suc!(session, t!("team_added_to_finished", team = team.name)),
Err(e) => err!(session, "{e}"),
}
@@ -514,17 +527,19 @@ async fn remove_left(
session: Session,
axum::extract::Path((id, code, team_id)): axum::extract::Path<(i64, String, i64)>,
) -> impl IntoResponse {
let Some(station) = Station::login(&db, id, &code).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(station) = Station::login(db, id, &code).await else {
er!(session, t!("invalid_rating_code"));
return Redirect::to("/s/{id}/{code}");
};
let Some(team) = Team::find_by_id(&db, team_id).await else {
let Some(team) = Team::find_by_id(db, team_id).await else {
er!(session, t!("nonexisting_team", id = id));
return Redirect::to("/s/{id}/{code}");
};
match station.remove_team_left(&db, &team).await {
match station.remove_team_left(db, &team).await {
Ok(()) => suc!(session, t!("team_removed_from_finished", team = team.name)),
Err(e) => err!(session, "{e}"),
}
@@ -543,17 +558,18 @@ async fn team_update(
axum::extract::Path((id, code, team_id)): axum::extract::Path<(i64, String, i64)>,
Form(form): Form<TeamUpdateForm>,
) -> impl IntoResponse {
let Some(station) = Station::login(&db, id, &code).await else {
let db = &mut *db.acquire().await.unwrap();
let Some(station) = Station::login(db, id, &code).await else {
er!(session, t!("invalid_rating_code"));
return Redirect::to("/s/{id}/{code}");
};
let Some(team) = Team::find_by_id(&db, team_id).await else {
let Some(team) = Team::find_by_id(db, team_id).await else {
er!(session, t!("nonexisting_team", id = id));
return Redirect::to("/s/{id}/{code}");
};
match station
.team_update(&db, &team, form.points, form.notes)
.team_update(db, &team, form.points, form.notes)
.await
{
Ok(()) => suc!(session, t!("rating_updated", team = team.name)),
@@ -587,8 +603,9 @@ mod test {
#[sqlx::test]
async fn test_wrong_station() {
let pool = testdb!();
let db = &mut *pool.acquire().await.unwrap();
Station::create(&pool, "Teststation").await.unwrap();
Station::create(db, "Teststation").await.unwrap();
let server = TestServer::new(router(pool)).unwrap();
@@ -600,9 +617,10 @@ mod test {
#[sqlx::test]
async fn test_correct_station() {
let pool = testdb!();
let db = &mut *pool.acquire().await.unwrap();
Station::create(&pool, "42-Station").await.unwrap();
let stations = Station::all(&pool).await;
Station::create(db, "42-Station").await.unwrap();
let stations = Station::all(db).await;
let station = stations.last().unwrap();
let server = TestServer::new(router(pool)).unwrap();