feat: add admin seed endpoint for demo data
This commit is contained in:
227
backend/app/api/admin.py
Normal file
227
backend/app/api/admin.py
Normal 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)}
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user