feat(api): build bingo card generation

This commit is contained in:
2025-05-11 21:03:20 +02:00
parent 385ef7d833
commit 9e231babd6
9 changed files with 183 additions and 39 deletions

94
src/api/bingo_card.rs Normal file
View File

@ -0,0 +1,94 @@
use std::sync::Arc;
use rand::seq::SliceRandom;
use rand::rng;
use sqlx::PgPool;
use sqlx::postgres::PgQueryResult;
use uuid::Uuid;
use crate::model;
use crate::model::bingo_card::BingoCard;
use crate::model::database::BingoCardField;
use crate::model::database::Train;
pub(crate) async fn get(id: Uuid, db: Arc<PgPool>) -> sqlx::Result<BingoCard> {
let card = sqlx::query_as!(
crate::model::database::BingoCard,
"SELECT uuid as id FROM bingo_cards WHERE uuid = $1",
id,
).fetch_one(db.as_ref()).await?;
let mut raw_fields = get_fields_with_train(card.id, db.clone())
.await?;
raw_fields.sort_by(|a, b| (a.y * 5 + a.x).partial_cmp(&(b.y * 5 + b.x)).unwrap());
raw_fields.truncate(24);
raw_fields.shrink_to_fit();
let fields: [Train; 24] = raw_fields
.into_iter()
.map(|field| field.train)
.collect::<Vec<Train>>()
.try_into()
.unwrap_or_else(|v: Vec<Train>| panic!("Expected a Vec of length 24 but it was {}", v.len()));
Ok(BingoCard {
id: card.id,
fields
})
}
async fn get_fields_with_train(id: Uuid, db: Arc<PgPool>) -> sqlx::Result<Vec<BingoCardField>> {
sqlx::query_as(
"SELECT c.uuid, c.x_pos, c.y_pos, c.card_uuid, t.uuid, t.tz_id, t.name FROM bingo_card c LEFT JOIN triebzug t ON t.uuid = c.triebzug WHERE card_uuid = $1"
)
.bind(id)
.fetch_all(db.as_ref())
.await
}
pub(crate) async fn generate(db: Arc<PgPool>) -> sqlx::Result<BingoCard> {
let card_id = Uuid::new_v4();
let mut all_trains = fetch_all(db.clone()).await?;
all_trains.shuffle(&mut rng());
all_trains.truncate(24);
all_trains.shrink_to_fit();
let trains = all_trains.to_owned();
let fields = trains
.into_iter()
.enumerate()
.map(|(idx, train)| BingoCardField {
uuid: Uuid::new_v4(),
x: (idx as i32).rem_euclid(5),
y: (idx as i32).div_euclid(5),
train,
card_id
})
.collect::<Vec<BingoCardField>>();
let card = model::database::BingoCard {
id: card_id,
};
save_card(card, db.clone()).await?;
for field in fields {
save_card_field(field, db.clone()).await?;
}
get(card_id, db.clone()).await
}
async fn save_card(card: model::database::BingoCard, db: Arc<PgPool>) -> sqlx::Result<PgQueryResult> {
sqlx::query!(
"INSERT INTO bingo_cards VALUES ($1, NOW(), NOW() + interval '7 day')",
card.id
).execute(db.as_ref()).await
}
async fn save_card_field(field: BingoCardField, db: Arc<PgPool>) -> sqlx::Result<PgQueryResult> {
sqlx::query!(
"INSERT INTO bingo_card VALUES ($1, $2, $3, $4, $5)",
field.uuid,
field.x,
field.y,
field.train.uuid,
field.card_id
).execute(db.as_ref()).await
}
async fn fetch_all(db: Arc<PgPool>) -> sqlx::Result<Vec<Train>> {
sqlx::query_as("SELECT * FROM triebzug")
.fetch_all(db.as_ref())
.await
}

View File

@ -1,2 +1,3 @@
pub(crate) mod handlers;
mod db_vendo_navigator;
pub(crate) mod bingo_card;
mod db_vendo_navigator;