Files
SkinsPins/app/database.py
T
Rolf c51c872c33 Add Skins format, GolfCourseAPI import, and UX improvements
- Add Skins game format with optional carryover, mirroring BBB scoring pattern
- Add GolfCourseAPI integration for course search and import (httpx)
- Unwrap {"course": {}} API response envelope on import
- Add searchable course combobox and player filter to new game form
- Redirect to summary page after finishing a game
- Fix auth to allow registered users without a pending invitation
- Fix hardcoded BBB format in create_game; store carryover flag in DB
- Add game summary/scorecard view for completed games
- Add live games social stream to dashboard
- Add course name + subtitle to recent game cards
- Reorder nav: Home → Games → Admin → Profile

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 09:18:04 +01:00

130 lines
4.7 KiB
Python

import os
import aiosqlite
DATABASE_PATH = os.getenv("DATABASE_PATH", "/data/skinspins.db")
async def get_db():
async with aiosqlite.connect(DATABASE_PATH) as db:
db.row_factory = aiosqlite.Row
yield db
async def init_db():
async with aiosqlite.connect(DATABASE_PATH) as db:
await db.executescript("""
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
name TEXT,
handicap REAL,
avatar TEXT,
is_onboarded INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS invitations (
id TEXT PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
invited_by TEXT NOT NULL,
created_at TEXT NOT NULL,
FOREIGN KEY (invited_by) REFERENCES users(id)
);
CREATE TABLE IF NOT EXISTS magic_link_tokens (
id TEXT PRIMARY KEY,
email TEXT NOT NULL,
token TEXT UNIQUE NOT NULL,
expires_at TEXT NOT NULL,
used_at TEXT
);
CREATE TABLE IF NOT EXISTS sessions (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
token TEXT UNIQUE NOT NULL,
expires_at TEXT NOT NULL,
created_at TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);
CREATE TABLE IF NOT EXISTS courses (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
holes INTEGER NOT NULL DEFAULT 18,
location TEXT,
created_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS course_holes (
id TEXT PRIMARY KEY,
course_id TEXT NOT NULL,
hole_number INTEGER NOT NULL,
par INTEGER NOT NULL DEFAULT 4,
UNIQUE (course_id, hole_number),
FOREIGN KEY (course_id) REFERENCES courses(id)
);
CREATE TABLE IF NOT EXISTS course_tees (
id TEXT PRIMARY KEY,
course_id TEXT NOT NULL,
name TEXT NOT NULL,
slope INTEGER NOT NULL,
rating REAL NOT NULL,
FOREIGN KEY (course_id) REFERENCES courses(id)
);
CREATE TABLE IF NOT EXISTS games (
id TEXT PRIMARY KEY,
format TEXT NOT NULL DEFAULT 'bingo_bango_bongo',
holes_count INTEGER NOT NULL,
status TEXT NOT NULL DEFAULT 'lobby',
created_by TEXT NOT NULL,
created_at TEXT NOT NULL,
course_id TEXT,
FOREIGN KEY (created_by) REFERENCES users(id),
FOREIGN KEY (course_id) REFERENCES courses(id)
);
CREATE TABLE IF NOT EXISTS game_players (
id TEXT PRIMARY KEY,
game_id TEXT NOT NULL,
user_id TEXT NOT NULL,
handicap_snapshot REAL,
joined_at TEXT NOT NULL,
UNIQUE (game_id, user_id),
FOREIGN KEY (game_id) REFERENCES games(id),
FOREIGN KEY (user_id) REFERENCES users(id)
);
CREATE TABLE IF NOT EXISTS hole_scores (
id TEXT PRIMARY KEY,
game_id TEXT NOT NULL,
hole_number INTEGER NOT NULL,
user_id TEXT NOT NULL,
strokes INTEGER,
bingo INTEGER NOT NULL DEFAULT 0,
bango REAL NOT NULL DEFAULT 0.0,
bongo INTEGER NOT NULL DEFAULT 0,
updated_by TEXT NOT NULL,
updated_at TEXT NOT NULL,
UNIQUE (game_id, hole_number, user_id),
FOREIGN KEY (game_id) REFERENCES games(id),
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (updated_by) REFERENCES users(id)
);
""")
await db.commit()
# Migrations for columns added after initial release
for migration in [
"ALTER TABLE games ADD COLUMN course_id TEXT REFERENCES courses(id)",
"ALTER TABLE game_players ADD COLUMN tee_id TEXT REFERENCES course_tees(id)",
"ALTER TABLE course_holes ADD COLUMN stroke_index INTEGER",
"ALTER TABLE games ADD COLUMN carryover INTEGER NOT NULL DEFAULT 1",
]:
try:
await db.execute(migration)
await db.commit()
except Exception:
pass # column already exists