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,66 @@
|
||||
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("/users/{user_id}", response_class=HTMLResponse)
|
||||
async def user_profile(user_id: str, request: Request,
|
||||
user: dict = Depends(get_current_user),
|
||||
db: aiosqlite.Connection = Depends(get_db)):
|
||||
async with db.execute(
|
||||
"SELECT id, name, handicap, avatar FROM users WHERE id = ? AND is_onboarded = 1",
|
||||
(user_id,),
|
||||
) as c:
|
||||
profile = await c.fetchone()
|
||||
if not profile:
|
||||
from fastapi.responses import RedirectResponse
|
||||
return RedirectResponse("/", status_code=302)
|
||||
|
||||
uid = user_id
|
||||
|
||||
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]
|
||||
|
||||
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
|
||||
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:
|
||||
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
|
||||
)""",
|
||||
(row["game_id"],),
|
||||
) as c2:
|
||||
max_total = (await c2.fetchone())[0] or 0
|
||||
if row["my_total"] >= max_total and row["my_total"] > 0:
|
||||
victories += 1
|
||||
|
||||
win_pct = round((victories / games_played) * 100) if games_played > 0 else None
|
||||
|
||||
return templates.TemplateResponse(
|
||||
"users/profile.html",
|
||||
{"request": request, "user": user, "profile": dict(profile),
|
||||
"games_played": games_played, "victories": victories, "win_pct": win_pct},
|
||||
)
|
||||
Reference in New Issue
Block a user