strat-gameplay-webapp/backend/app/models/db_models.py
Cal Corum fc7f53adf3 CLAUDE: Complete Phase 1 backend infrastructure setup
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>
2025-10-21 19:46:16 -05:00

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)