Implemented full FastAPI backend with WebSocket support, database models, and comprehensive documentation for the Paper Dynasty game engine. Backend Implementation: - FastAPI application with Socket.io WebSocket server - SQLAlchemy async database models (Game, Play, Lineup, GameSession) - PostgreSQL connection to dev server (10.10.0.42:5432) - Connection manager for WebSocket lifecycle - JWT authentication utilities - Health check and stub API endpoints - Rotating file logger with Pendulum datetime handling - Redis via Docker Compose for caching Technical Details: - Python 3.13 with updated package versions - Pendulum 3.0 for all datetime operations - Greenlet for SQLAlchemy async support - Fixed SQLAlchemy reserved column names (metadata -> *_metadata) - Pydantic Settings with JSON array format for lists - Docker Compose V2 commands Documentation: - Updated backend/CLAUDE.md with environment-specific details - Created .claude/ENVIRONMENT.md for gotchas and quirks - Created QUICKSTART.md for developer onboarding - Documented all critical learnings and troubleshooting steps Database: - Tables created: games, plays, lineups, game_sessions - All indexes and foreign keys configured - Successfully tested connection and health checks Verified: - Server starts at http://localhost:8000 - Health endpoints responding - Database connection working - WebSocket infrastructure functional - Hot-reload working 🎯 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
82 lines
3.1 KiB
Python
82 lines
3.1 KiB
Python
from sqlalchemy import Column, Integer, String, Boolean, DateTime, JSON, Text, ForeignKey
|
|
from sqlalchemy.dialects.postgresql import UUID
|
|
import uuid
|
|
import pendulum
|
|
|
|
from app.database.session import Base
|
|
|
|
|
|
class Game(Base):
|
|
"""Game model"""
|
|
__tablename__ = "games"
|
|
|
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
league_id = Column(String(50), nullable=False, index=True)
|
|
home_team_id = Column(Integer, nullable=False)
|
|
away_team_id = Column(Integer, nullable=False)
|
|
status = Column(String(20), nullable=False, default="pending", index=True)
|
|
game_mode = Column(String(20), nullable=False)
|
|
visibility = Column(String(20), nullable=False)
|
|
current_inning = Column(Integer)
|
|
current_half = Column(String(10))
|
|
home_score = Column(Integer, default=0)
|
|
away_score = Column(Integer, default=0)
|
|
created_at = Column(DateTime, default=lambda: pendulum.now('UTC'), index=True)
|
|
started_at = Column(DateTime)
|
|
completed_at = Column(DateTime)
|
|
winner_team_id = Column(Integer)
|
|
game_metadata = Column(JSON, default=dict)
|
|
|
|
|
|
class Play(Base):
|
|
"""Play model"""
|
|
__tablename__ = "plays"
|
|
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
game_id = Column(UUID(as_uuid=True), ForeignKey("games.id"), nullable=False, index=True)
|
|
play_number = Column(Integer, nullable=False)
|
|
inning = Column(Integer, nullable=False)
|
|
half = Column(String(10), nullable=False)
|
|
outs_before = Column(Integer, nullable=False)
|
|
outs_recorded = Column(Integer, nullable=False)
|
|
batter_id = Column(Integer, nullable=False)
|
|
pitcher_id = Column(Integer, nullable=False)
|
|
runners_before = Column(JSON)
|
|
runners_after = Column(JSON)
|
|
balls = Column(Integer)
|
|
strikes = Column(Integer)
|
|
defensive_positioning = Column(String(50))
|
|
offensive_approach = Column(String(50))
|
|
dice_roll = Column(Integer)
|
|
hit_type = Column(String(50))
|
|
result_description = Column(Text)
|
|
runs_scored = Column(Integer, default=0)
|
|
created_at = Column(DateTime, default=lambda: pendulum.now('UTC'), index=True)
|
|
play_metadata = Column(JSON, default=dict)
|
|
|
|
|
|
class Lineup(Base):
|
|
"""Lineup model"""
|
|
__tablename__ = "lineups"
|
|
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
game_id = Column(UUID(as_uuid=True), ForeignKey("games.id"), nullable=False, index=True)
|
|
team_id = Column(Integer, nullable=False, index=True)
|
|
card_id = Column(Integer, nullable=False)
|
|
position = Column(String(10), nullable=False)
|
|
batting_order = Column(Integer)
|
|
is_starter = Column(Boolean, default=True)
|
|
is_active = Column(Boolean, default=True, index=True)
|
|
entered_inning = Column(Integer, default=1)
|
|
lineup_metadata = Column(JSON, default=dict)
|
|
|
|
|
|
class GameSession(Base):
|
|
"""Game session tracking"""
|
|
__tablename__ = "game_sessions"
|
|
|
|
game_id = Column(UUID(as_uuid=True), ForeignKey("games.id"), primary_key=True)
|
|
connected_users = Column(JSON, default=dict)
|
|
last_action_at = Column(DateTime, default=lambda: pendulum.now('UTC'), index=True)
|
|
state_snapshot = Column(JSON, default=dict)
|