feat: add admin seed endpoint for demo data

This commit is contained in:
Forge
2026-03-19 01:08:21 +08:00
parent 011df19306
commit a1faa9cf3a
2 changed files with 229 additions and 1 deletions

227
backend/app/api/admin.py Normal file
View File

@@ -0,0 +1,227 @@
"""Admin/seed endpoints for demo"""
from fastapi import APIRouter, Depends, BackgroundTasks
from sqlalchemy.orm import Session
from app.database import get_db
from app.models.player import Player, MembershipTier
from app.models.court import Court
from app.models.booking import Booking, BookingStatus
from app.models.match import Match, MatchPlayer, MatchStage, MatchStatus, MatchType
from app.models.tournament import Tournament, TournamentEntry, TournamentMatch, TournamentStatus, BracketType, TournamentMatchStatus
from datetime import datetime, timedelta
import random
router = APIRouter(prefix="/admin", tags=["admin"])
@router.post("/seed")
def run_seed(db: Session = Depends(get_db)):
"""Run demo seed data"""
try:
import sys
sys.path.insert(0, '/app')
from seed import main
main()
return {"status": "ok", "message": "Seed complete"}
except Exception as e:
return {"status": "error", "message": str(e)}
@router.post("/update-elos")
def update_elos(db: Session = Depends(get_db)):
"""Update demo player ELO ratings to proper values"""
elo_map = {
"marco@demo.com": (1850, MembershipTier.ELITE, 45, 8, "#EF4444"),
"sofia@demo.com": (1720, MembershipTier.ELITE, 38, 12, "#EC4899"),
"carlos@demo.com": (1630, MembershipTier.PLATINUM, 32, 15, "#8B5CF6"),
"ana@demo.com": (1580, MembershipTier.PLATINUM, 28, 18, "#06B6D4"),
"juan@demo.com": (1450, MembershipTier.GOLD, 22, 20, "#F59E0B"),
"maria@demo.com": (1380, MembershipTier.GOLD, 18, 22, "#10B981"),
"pedro@demo.com": (1250, MembershipTier.SILVER, 14, 19, "#3B82F6"),
"rosa@demo.com": (1190, MembershipTier.SILVER, 11, 21, "#F97316"),
"diego@demo.com": (1100, MembershipTier.SILVER, 9, 18, "#84CC16"),
"lucia@demo.com": (980, MembershipTier.BRONZE, 6, 20, "#14B8A6"),
"miguel@demo.com": (920, MembershipTier.BRONZE, 4, 15, "#6366F1"),
"isabella@demo.com": (850, MembershipTier.BRONZE, 2, 12, "#D946EF"),
}
updated = []
for email, (elo, tier, wins, losses, color) in elo_map.items():
p = db.query(Player).filter(Player.email == email).first()
if p:
p.elo_rating = elo
p.membership_tier = tier
p.wins = wins
p.losses = losses
p.total_matches = wins + losses
p.avatar_color = color
updated.append(p.name)
db.commit()
# Create courts if not exist
if db.query(Court).count() == 0:
courts_data = [
("Court 1", 1, 200.0, "Sport Court", "LED Lighting, Pro Net System"),
("Court 2", 2, 200.0, "Sport Court", "LED Lighting, Spectator Seating"),
("Court 3", 3, 250.0, "Cushioned", "Tournament Grade, Scoreboards"),
("Court 4", 4, 250.0, "Cushioned", "Tournament Grade, VIP Lounge Access"),
]
for name, num, rate, surface, features in courts_data:
c = Court(name=name, court_number=num, hourly_rate=rate, surface_type=surface, features=features)
db.add(c)
db.commit()
# Create active matches
courts = db.query(Court).order_by(Court.court_number).all()
players = db.query(Player).order_by(Player.elo_rating.desc()).all()
if db.query(Match).count() == 0 and len(players) >= 4 and len(courts) >= 2:
# Active match 1
m1 = Match(
court_id=courts[0].id,
stage=MatchStage.OPEN,
match_type=MatchType.DOUBLES,
status=MatchStatus.IN_PROGRESS,
title="Open Doubles - Court 1",
max_players=4,
team1_score=7,
team2_score=5,
started_at=datetime.utcnow() - timedelta(minutes=25),
)
db.add(m1)
db.flush()
for pid, team in [(players[0].id, 1), (players[1].id, 1), (players[2].id, 2), (players[3].id, 2)]:
db.add(MatchPlayer(match_id=m1.id, player_id=pid, team=team, elo_before=db.query(Player).filter(Player.id == pid).first().elo_rating))
# Active match 2 (skill-based)
m2 = Match(
court_id=courts[1].id,
stage=MatchStage.SKILL_BASED,
match_type=MatchType.DOUBLES,
status=MatchStatus.IN_PROGRESS,
title="Skill Match - Gold League",
max_players=4,
min_elo=1200,
max_elo=1600,
team1_score=3,
team2_score=6,
started_at=datetime.utcnow() - timedelta(minutes=15),
)
db.add(m2)
db.flush()
for pid, team in [(players[4].id, 1), (players[5].id, 1), (players[6].id, 2), (players[7].id, 2)]:
db.add(MatchPlayer(match_id=m2.id, player_id=pid, team=team, elo_before=db.query(Player).filter(Player.id == pid).first().elo_rating))
# Lobby matches
m3 = Match(stage=MatchStage.OPEN, match_type=MatchType.DOUBLES, status=MatchStatus.LOBBY,
title="Evening Open Game", max_players=4)
db.add(m3)
db.flush()
db.add(MatchPlayer(match_id=m3.id, player_id=players[8].id, team=1, elo_before=players[8].elo_rating))
m4 = Match(stage=MatchStage.SKILL_BASED, match_type=MatchType.DOUBLES, status=MatchStatus.LOBBY,
title="Gold Tier Challenge", max_players=4, min_elo=1200, max_elo=1600)
db.add(m4)
db.flush()
db.add(MatchPlayer(match_id=m4.id, player_id=players[4].id, team=1, elo_before=players[4].elo_rating))
db.commit()
# Create tournament
if db.query(Tournament).count() == 0 and len(players) >= 8 and len(courts) >= 3:
t = Tournament(
name="ServeSync Grand Prix - March 2024",
status=TournamentStatus.IN_PROGRESS,
max_participants=8,
current_round=2,
started_at=datetime.utcnow() - timedelta(hours=2),
)
db.add(t)
db.flush()
tp = players[:8]
entries = []
for i, player in enumerate(tp):
entry = TournamentEntry(tournament_id=t.id, player_id=player.id, seed=i+1, wins=0, losses=0)
db.add(entry)
entries.append(entry)
db.flush()
r1 = [(tp[0].id, tp[7].id, 11, 5), (tp[1].id, tp[6].id, 11, 7),
(tp[2].id, tp[5].id, 8, 11), (tp[3].id, tp[4].id, 11, 9)]
winners = []
losers = []
for i, (p1, p2, s1, s2) in enumerate(r1):
tm = TournamentMatch(tournament_id=t.id, round_number=1, match_number=i+1,
bracket_type=BracketType.WINNERS, status=TournamentMatchStatus.COMPLETED,
player1_id=p1, player2_id=p2, team1_score=s1, team2_score=s2,
winner_team=1 if s1>s2 else 2,
completed_at=datetime.utcnow() - timedelta(hours=1, minutes=30))
db.add(tm)
w = p1 if s1>s2 else p2
l = p2 if s1>s2 else p1
winners.append(w)
losers.append(l)
for e in entries:
if e.player_id == w: e.wins += 1
elif e.player_id == l:
e.losses += 1
e.is_in_losers = True
# R2 winners - 1 completed, 1 active
wm1 = TournamentMatch(tournament_id=t.id, round_number=2, match_number=1,
bracket_type=BracketType.WINNERS, status=TournamentMatchStatus.COMPLETED,
player1_id=winners[0], player2_id=winners[1], team1_score=11, team2_score=8,
winner_team=1, completed_at=datetime.utcnow() - timedelta(minutes=45))
db.add(wm1)
for e in entries:
if e.player_id == winners[0]: e.wins += 1
elif e.player_id == winners[1]:
e.losses += 1
e.is_in_losers = True
wm2 = TournamentMatch(tournament_id=t.id, round_number=2, match_number=2,
bracket_type=BracketType.WINNERS, status=TournamentMatchStatus.IN_PROGRESS,
player1_id=winners[2], player2_id=winners[3], team1_score=6, team2_score=7)
db.add(wm2)
# Losers bracket
lb1 = TournamentMatch(tournament_id=t.id, round_number=1, match_number=1,
bracket_type=BracketType.LOSERS, status=TournamentMatchStatus.COMPLETED,
player1_id=losers[0], player2_id=losers[1], team1_score=9, team2_score=11,
winner_team=2, completed_at=datetime.utcnow() - timedelta(minutes=90))
db.add(lb1)
lb2 = TournamentMatch(tournament_id=t.id, round_number=1, match_number=2,
bracket_type=BracketType.LOSERS, status=TournamentMatchStatus.COMPLETED,
player1_id=losers[2], player2_id=losers[3], team1_score=11, team2_score=6,
winner_team=1, completed_at=datetime.utcnow() - timedelta(minutes=60))
db.add(lb2)
for e in entries:
if e.player_id == losers[0]:
e.losses += 1
e.is_eliminated = True
e.final_rank = 8
elif e.player_id == losers[3]:
e.losses += 1
e.is_eliminated = True
e.final_rank = 7
# Tournament match on court 3
p_name_2 = db.query(Player).filter(Player.id == winners[2]).first().name
p_name_3 = db.query(Player).filter(Player.id == winners[3]).first().name
m_tour = Match(
court_id=courts[2].id, stage=MatchStage.TOURNAMENT, match_type=MatchType.SINGLES,
status=MatchStatus.IN_PROGRESS,
title=f"Tournament R2 - {p_name_2} vs {p_name_3}",
max_players=2, team1_score=6, team2_score=7,
started_at=datetime.utcnow() - timedelta(minutes=20),
)
db.add(m_tour)
db.flush()
for pid, team in [(winners[2], 1), (winners[3], 2)]:
db.add(MatchPlayer(match_id=m_tour.id, player_id=pid, team=team,
elo_before=db.query(Player).filter(Player.id == pid).first().elo_rating))
db.commit()
return {"status": "ok", "updated": updated, "players": len(updated)}

View File

@@ -15,7 +15,7 @@ booking.Base.metadata.create_all(bind=engine)
match.Base.metadata.create_all(bind=engine) match.Base.metadata.create_all(bind=engine)
tournament.Base.metadata.create_all(bind=engine) tournament.Base.metadata.create_all(bind=engine)
from app.api import players, courts, matches, tournaments, screen from app.api import players, courts, matches, tournaments, screen, admin
app = FastAPI( app = FastAPI(
title="ServeSync API", title="ServeSync API",
@@ -37,6 +37,7 @@ app.include_router(courts.router, prefix="/api")
app.include_router(matches.router, prefix="/api") app.include_router(matches.router, prefix="/api")
app.include_router(tournaments.router, prefix="/api") app.include_router(tournaments.router, prefix="/api")
app.include_router(screen.router, prefix="/api") app.include_router(screen.router, prefix="/api")
app.include_router(admin.router, prefix="/api")
# WebSocket connection manager # WebSocket connection manager