Add full SkinsPins application
- FastAPI + SQLite + Alpine.js + HTMX PWA for Bingo Bango Bongo golf scoring - Passwordless magic link auth with email (aiosmtplib) and admin invite system - Real-time score entry via WebSocket with drag-and-drop player ordering - Course management with per-hole par and stroke index - Scorecard with golf score markers (birdie, eagle, bogey, etc.) - Two-step new game form with tee selection per player - Dashboard with stats, live games social stream, and recent game history - Game summary view (read-only scorecard with leaderboard) - User profile pages with win stats - PWA install support with generated PNG icons - Admin panel for users, courses, games, and invitations Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
import aiosqlite
|
||||
|
||||
from app.database import get_db
|
||||
from app.dependencies import get_current_user
|
||||
from app.templates_config import templates
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/", response_class=HTMLResponse)
|
||||
async def dashboard(request: Request, user: dict = Depends(get_current_user),
|
||||
db: aiosqlite.Connection = Depends(get_db)):
|
||||
uid = user["id"]
|
||||
|
||||
# Games played (completed)
|
||||
async with db.execute(
|
||||
"""SELECT COUNT(*) FROM games
|
||||
WHERE status = 'completed'
|
||||
AND id IN (SELECT game_id FROM game_players WHERE user_id = ?)""",
|
||||
(uid,),
|
||||
) as c:
|
||||
games_played = (await c.fetchone())[0]
|
||||
|
||||
# Victories: completed games where this user has the highest total points
|
||||
# For each completed game the user played, rank players by total points
|
||||
async with db.execute(
|
||||
"""SELECT gp.game_id,
|
||||
SUM(CASE WHEN hs.user_id = ? THEN hs.bingo + hs.bango + hs.bongo ELSE 0 END) AS my_total,
|
||||
MAX(hs.bingo + hs.bango + hs.bongo) AS top_hole_score
|
||||
FROM game_players gp
|
||||
JOIN games g ON g.id = gp.game_id
|
||||
JOIN hole_scores hs ON hs.game_id = gp.game_id
|
||||
WHERE gp.user_id = ? AND g.status = 'completed'
|
||||
GROUP BY gp.game_id""",
|
||||
(uid, uid),
|
||||
) as c:
|
||||
game_rows = await c.fetchall()
|
||||
|
||||
victories = 0
|
||||
for row in game_rows:
|
||||
game_id = row["game_id"]
|
||||
my_total = row["my_total"]
|
||||
# Get the max total among all players in this game
|
||||
async with db.execute(
|
||||
"""SELECT MAX(player_total) FROM (
|
||||
SELECT user_id, SUM(bingo + bango + bongo) AS player_total
|
||||
FROM hole_scores WHERE game_id = ? GROUP BY user_id
|
||||
)""",
|
||||
(game_id,),
|
||||
) as c2:
|
||||
max_total = (await c2.fetchone())[0] or 0
|
||||
if my_total >= max_total and my_total > 0:
|
||||
victories += 1
|
||||
|
||||
# Recent completed games with results
|
||||
async with db.execute(
|
||||
"""SELECT g.id, g.holes_count, g.created_at, g.format, c.name AS course_name
|
||||
FROM games g
|
||||
LEFT JOIN courses c ON c.id = g.course_id
|
||||
JOIN game_players gp ON gp.game_id = g.id
|
||||
WHERE gp.user_id = ? AND g.status = 'completed'
|
||||
ORDER BY g.created_at DESC LIMIT 5""",
|
||||
(uid,),
|
||||
) as c:
|
||||
recent_games = [dict(r) for r in await c.fetchall()]
|
||||
|
||||
# For each recent game, get player totals
|
||||
for game in recent_games:
|
||||
async with db.execute(
|
||||
"""SELECT u.id, u.name, u.avatar,
|
||||
SUM(hs.bingo + hs.bango + hs.bongo) AS total
|
||||
FROM hole_scores hs JOIN users u ON hs.user_id = u.id
|
||||
WHERE hs.game_id = ?
|
||||
GROUP BY hs.user_id
|
||||
ORDER BY total DESC""",
|
||||
(game["id"],),
|
||||
) as c:
|
||||
game["results"] = [dict(r) for r in await c.fetchall()]
|
||||
|
||||
# All active games (social stream)
|
||||
async with db.execute(
|
||||
"""SELECT g.id, g.holes_count, g.created_at, g.format, c.name AS course_name
|
||||
FROM games g
|
||||
LEFT JOIN courses c ON c.id = g.course_id
|
||||
WHERE g.status = 'active'
|
||||
ORDER BY g.created_at DESC""",
|
||||
) as c:
|
||||
active_games = [dict(r) for r in await c.fetchall()]
|
||||
|
||||
for game in active_games:
|
||||
async with db.execute(
|
||||
"""SELECT u.id, u.name, u.avatar,
|
||||
COALESCE(SUM(hs.bingo + hs.bango + hs.bongo), 0) AS total
|
||||
FROM game_players gp
|
||||
JOIN users u ON u.id = gp.user_id
|
||||
LEFT JOIN hole_scores hs ON hs.game_id = gp.game_id AND hs.user_id = u.id
|
||||
WHERE gp.game_id = ?
|
||||
GROUP BY u.id
|
||||
ORDER BY total DESC""",
|
||||
(game["id"],),
|
||||
) as c:
|
||||
game["players"] = [dict(r) for r in await c.fetchall()]
|
||||
game["is_participant"] = any(p["id"] == uid for p in game["players"])
|
||||
|
||||
return templates.TemplateResponse("dashboard/dashboard.html", {
|
||||
"request": request, "user": user,
|
||||
"games_played": games_played, "victories": victories,
|
||||
"recent_games": recent_games,
|
||||
"active_games": active_games,
|
||||
})
|
||||
Reference in New Issue
Block a user