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

87
Cargo.lock generated
View File

@ -439,7 +439,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
dependencies = [
"generic-array",
"rand_core",
"rand_core 0.6.4",
"subtle",
"zeroize",
]
@ -632,7 +632,7 @@ dependencies = [
"hkdf",
"pem-rfc7468",
"pkcs8",
"rand_core",
"rand_core 0.6.4",
"sec1",
"subtle",
"zeroize",
@ -697,7 +697,7 @@ version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
dependencies = [
"rand_core",
"rand_core 0.6.4",
"subtle",
]
@ -756,9 +756,9 @@ dependencies = [
[[package]]
name = "futures-channel"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
dependencies = [
"futures-core",
"futures-sink",
@ -766,15 +766,15 @@ dependencies = [
[[package]]
name = "futures-core"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-executor"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
dependencies = [
"futures-core",
"futures-task",
@ -794,27 +794,27 @@ dependencies = [
[[package]]
name = "futures-io"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
[[package]]
name = "futures-sink"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
[[package]]
name = "futures-task"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-util"
version = "0.3.30"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-core",
"futures-io",
@ -875,7 +875,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
dependencies = [
"ff",
"rand_core",
"rand_core 0.6.4",
"subtle",
]
@ -1202,7 +1202,6 @@ dependencies = [
name = "ice-bingo"
version = "0.1.0"
dependencies = [
"async-trait",
"axum",
"axum-auth",
"axum-core",
@ -1210,6 +1209,7 @@ dependencies = [
"chrono",
"config",
"openidconnect",
"rand 0.9.1",
"reqwest 0.12.15",
"serde",
"serde_json",
@ -1450,7 +1450,7 @@ dependencies = [
"num-integer",
"num-iter",
"num-traits",
"rand",
"rand 0.8.5",
"smallvec",
"zeroize",
]
@ -1501,7 +1501,7 @@ dependencies = [
"chrono",
"getrandom 0.2.15",
"http 0.2.12",
"rand",
"rand 0.8.5",
"reqwest 0.11.27",
"serde",
"serde_json",
@ -1543,7 +1543,7 @@ dependencies = [
"oauth2",
"p256",
"p384",
"rand",
"rand 0.8.5",
"rsa",
"serde",
"serde-value",
@ -1834,8 +1834,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_chacha 0.3.1",
"rand_core 0.6.4",
]
[[package]]
name = "rand"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
dependencies = [
"rand_chacha 0.9.0",
"rand_core 0.9.3",
]
[[package]]
@ -1845,7 +1855,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
"rand_core 0.6.4",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core 0.9.3",
]
[[package]]
@ -1857,6 +1877,15 @@ dependencies = [
"getrandom 0.2.15",
]
[[package]]
name = "rand_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
"getrandom 0.3.2",
]
[[package]]
name = "redox_syscall"
version = "0.5.6"
@ -2001,7 +2030,7 @@ dependencies = [
"num-traits",
"pkcs1",
"pkcs8",
"rand_core",
"rand_core 0.6.4",
"signature",
"spki",
"subtle",
@ -2371,7 +2400,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
dependencies = [
"digest",
"rand_core",
"rand_core 0.6.4",
]
[[package]]
@ -2541,7 +2570,7 @@ dependencies = [
"memchr",
"once_cell",
"percent-encoding",
"rand",
"rand 0.8.5",
"rsa",
"serde",
"sha1",
@ -2581,7 +2610,7 @@ dependencies = [
"md-5",
"memchr",
"once_cell",
"rand",
"rand 0.8.5",
"serde",
"serde_json",
"sha2",

View File

@ -22,4 +22,4 @@ reqwest = { version = "0.12.15", features = ["json"] }
strum = { version = "0.27.1", features = ["derive"] }
strum_macros = "0.27.1"
chrono = { version = "0.4.41", features = ["alloc"] }
async-trait = "0.1.88"
rand = "0.9.1"

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;

View File

@ -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()

View File

@ -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],
}

View File

@ -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,
}

View File

@ -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;

View File

@ -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 (