feat(api): build bingo card generation
This commit is contained in:
94
src/api/bingo_card.rs
Normal file
94
src/api/bingo_card.rs
Normal 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
|
||||
}
|
@@ -1,2 +1,3 @@
|
||||
pub(crate) mod handlers;
|
||||
mod db_vendo_navigator;
|
||||
pub(crate) mod bingo_card;
|
||||
mod db_vendo_navigator;
|
||||
|
@@ -22,6 +22,8 @@ async fn main() {
|
||||
let state: AppState = AppState::new(config.clone()).await.unwrap();
|
||||
migrate(state.clone()).await.expect("Database migration failed");
|
||||
|
||||
crate::api::bingo_card::generate(state.db.clone()).await.unwrap();
|
||||
|
||||
let router = build_router(state);
|
||||
let listener = listen(config).await;
|
||||
axum::serve::serve(listener, router).await.unwrap()
|
||||
|
@@ -1,6 +1,6 @@
|
||||
use crate::model::train::Train;
|
||||
use crate::model::database::Train;
|
||||
|
||||
struct BingoCard {
|
||||
id: uuid::Uuid,
|
||||
fields: [Train; 24],
|
||||
pub struct BingoCard {
|
||||
pub id: uuid::Uuid,
|
||||
pub fields: [Train; 24],
|
||||
}
|
@@ -11,4 +11,22 @@ pub struct Train {
|
||||
pub uuid: Uuid,
|
||||
pub tz_id: i32,
|
||||
pub name: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(sqlx::FromRow, Debug, Clone)]
|
||||
pub struct BingoCardField {
|
||||
pub uuid: Uuid,
|
||||
#[sqlx(rename = "x_pos")]
|
||||
pub x: i32,
|
||||
#[sqlx(rename = "y_pos")]
|
||||
pub y: i32,
|
||||
#[sqlx(rename = "card_uuid")]
|
||||
pub card_id: Uuid,
|
||||
#[sqlx(flatten, rename = "triebzug")]
|
||||
pub train: Train,
|
||||
}
|
||||
|
||||
#[derive(sqlx::FromRow, Debug, Clone)]
|
||||
pub struct BingoCard {
|
||||
pub id: Uuid,
|
||||
}
|
@@ -2,7 +2,7 @@ pub mod uic;
|
||||
pub mod db_vendo_navigator_api;
|
||||
pub(crate) mod travelynx;
|
||||
pub(crate) mod traewelling;
|
||||
mod bingo_card;
|
||||
mod train;
|
||||
pub(crate) mod bingo_card;
|
||||
pub(crate) mod train;
|
||||
pub(crate) mod app;
|
||||
pub(crate) mod database;
|
@@ -7,8 +7,8 @@ CREATE TABLE IF NOT EXISTS triebzug (
|
||||
|
||||
CREATE TABLE IF NOT EXISTS bingo_cards (
|
||||
uuid UUID PRIMARY KEY,
|
||||
start_time INT NOT NULL,
|
||||
end_time INT NOT NULL
|
||||
start_time TIMESTAMP NOT NULL,
|
||||
end_time TIMESTAMP NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS bingo_card (
|
||||
|
Reference in New Issue
Block a user