12 KiB
Week 6: Player Models & League Integration - Overview
Created: 2025-10-25 Status: Planning Complete, Ready for Implementation Prerequisites: Week 5 Complete (Game engine working) Focus: League-specific player models and API integration
Quick Summary
Implement two-layer player model system:
- API Models - Exact match to external league APIs (PD & SBA)
- Game Models - Optimized for gameplay with only essential fields
This allows us to:
- Deserialize API responses with full type safety
- Work with clean, minimal data in game engine
- Support both leagues with different data structures
Architecture
┌─────────────────────────────────────────────────────────────┐
│ External League APIs │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ PD API │ │ SBA API │ │
│ │ - Player │ │ - Player │ │
│ │ - Batting Ratings │ │ │ │
│ │ - Pitching Ratings │ │ │ │
│ └──────────────────────┘ └──────────────────────┘ │
└────────────────┬──────────────────────┬─────────────────────┘
│ │
↓ ↓
┌─────────────────────────────────────────────────────────────┐
│ API Response Models (api_models.py) │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ PdPlayerApi │ │ SbaPlayerApi │ │
│ │ PdBattingRatingsApi │ │ (with nested Team, │ │
│ │ PdPitchingRatingsApi│ │ Manager, Division) │ │
│ └──────────────────────┘ └──────────────────────┘ │
└────────────────┬──────────────────────┬─────────────────────┘
│ │
↓ ↓
┌─────────────────────────────────────────────────────────────┐
│ Mapper Layer (PlayerMapper class) │
│ Transforms: API → Game Models │
│ - Extracts essential fields │
│ - Flattens nested structures │
│ - Normalizes position data (pos_1-8 → List[str]) │
└────────────────┬──────────────────────┬─────────────────────┘
│ │
↓ ↓
┌─────────────────────────────────────────────────────────────┐
│ Game Models (player_models.py) │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ PdPlayer │ │ SbaPlayer │ │
│ │ - Basic fields │ │ - Basic fields │ │
│ │ - Batting ratings │ │ - Simplified data │ │
│ │ - Pitching ratings │ │ │ │
│ └──────────────────────┘ └──────────────────────┘ │
└────────────────┬──────────────────────┬─────────────────────┘
│ │
↓ ↓
┌─────────────────────────────────────────────────────────────┐
│ Game Engine & Play Resolver │
│ - Uses game models during play resolution │
│ - PD: Uses outcome probabilities from ratings │
│ - SBA: Uses simplified result charts │
└─────────────────────────────────────────────────────────────┘
Why Two Layers?
Problem
- External APIs return massive nested JSON (team, division, cardset, rarity, etc.)
- Game engine only needs minimal data (name, positions, ratings)
- PD needs 3 API calls per player (player + batting ratings + pitching ratings)
Solution
Layer 1: API Models - Match external structure exactly
- Full type safety when deserializing
- All nested objects as Pydantic models
- Easy to maintain when API changes
Layer 2: Game Models - Only what's needed for gameplay
- Clean, minimal data
- Fast serialization for WebSocket
- Easy to work with in game logic
Mapper Layer - Transform between them
- Extract essential fields
- Combine multiple API calls (PD: player + ratings)
- Normalize differences between leagues
Detailed Documentation
This plan is split across multiple focused files:
Core Specifications
-
- All PD API response models
- JSON examples
- Nested structures (Cardset, Rarity, MlbPlayer)
- Batting & pitching ratings
-
- SBA API response models
- JSON examples
- Nested structures (Team, Manager, Division)
-
- BasePlayer abstract class
- SbaPlayer (game-optimized)
- PdPlayer (game-optimized with ratings)
- Field selection rationale
-
- PlayerMapper (API → Game)
- PlayerFactory (create by league)
- Transformation logic
- Position normalization
-
- LeagueApiClient HTTP client
- Methods for each endpoint
- Error handling
- Usage examples
-
- Unit test specifications
- Integration test plans
- Mock data strategy
Implementation Order
Phase 1: API Models (Day 1)
- Create
api_models.py - Define all PD API models
- Define all SBA API models
- Unit tests with real JSON samples
Phase 2: Game Models (Day 1-2)
- Create
player_models.py - Define BasePlayer abstract
- Define SbaPlayer
- Define PdPlayer with ratings
- Unit tests
Phase 3: Mappers (Day 2)
- Create PlayerMapper class
- Implement PD mapping (combine 3 API calls)
- Implement SBA mapping
- Unit tests with transformation examples
Phase 4: API Client (Day 2-3)
- Create
api_client.py - Implement LeagueApiClient with httpx
- Implement PD endpoints
- Implement SBA endpoints
- Integration tests with mocked responses
Phase 5: Integration (Day 3)
- Update
league_configs.pywith API base URLs - Update PlayResolver to use PD ratings
- End-to-end integration tests
- Performance testing
Key Design Decisions
Decision 1: Two-Layer Approach
Chosen: API models + Game models (with mapper)
Alternatives Considered:
- Single model matching API (❌ too much unnecessary data in game engine)
- Single game model with manual dict parsing (❌ no type safety on API responses)
Rationale: Separation of concerns, type safety, performance
Decision 2: Nested Pydantic Models
Chosen: Full Pydantic models for all nested objects (Team, Cardset, etc.)
Alternatives Considered:
- Dict[str, Any] for nested data (❌ loses type safety)
- Flatten everything to top level (❌ complex mapping logic)
Rationale: Type safety, IDE autocomplete, validation
Decision 3: Position Handling
Chosen: Extract pos_1 through pos_8 → positions: List[str]
Rationale: Cleaner interface, easier to work with in game logic
Decision 4: PD Ratings Storage
Chosen: Store ratings as part of PdPlayer model (vs L and vs R)
Rationale: Always needed for play resolution, keep together
Decision 5: API Base URLs
Actual URLs (from your data):
- PD:
https://pd.manticorum.com/ - SBA:
https://api.sba.manticorum.com/
File Structure
After implementation, project structure will be:
backend/app/
├── models/
│ ├── api_models.py # NEW - External API response models
│ ├── player_models.py # NEW - Game-optimized player models
│ ├── game_models.py # Existing - Game state models
│ └── db_models.py # Existing - Database ORM models
│
├── data/
│ ├── __init__.py
│ └── api_client.py # NEW - HTTP client for league APIs
│
├── config/
│ ├── __init__.py
│ ├── base_config.py # To create
│ ├── league_configs.py # To create (with API URLs)
│ └── result_charts.py # To create
│
└── core/
├── play_resolver.py # UPDATE - Use PD ratings for resolution
└── ... # Existing files
tests/
├── unit/
│ ├── models/
│ │ ├── test_api_models.py # NEW
│ │ └── test_player_models.py # NEW
│ └── data/
│ └── test_mappers.py # NEW
│
└── integration/
└── data/
└── test_api_client.py # NEW
Success Criteria
Functional Requirements
- ✅ Can fetch PD player from API and deserialize to PdPlayerApi
- ✅ Can fetch PD batting ratings and deserialize
- ✅ Can fetch PD pitching ratings and deserialize
- ✅ Can fetch SBA player from API and deserialize to SbaPlayerApi
- ✅ Can map PD API models → PdPlayer game model
- ✅ Can map SBA API models → SbaPlayer game model
- ✅ PlayerFactory creates correct player type by league_id
- ✅ Positions correctly extracted from pos_1-8 fields
- ✅ PD ratings correctly attached to PdPlayer
Non-Functional Requirements
- ✅ All API models pass Pydantic validation with real JSON
- ✅ Unit test coverage > 90%
- ✅ API client handles errors gracefully
- ✅ Serialization/deserialization < 10ms per player
- ✅ Type hints validated by mypy
Timeline & Effort
Estimated Total: 2-3 days (16-24 hours)
Breakdown:
- API Models: 4-6 hours
- Game Models: 3-4 hours
- Mappers: 2-3 hours
- API Client: 4-6 hours
- Testing: 3-5 hours
Dependencies:
- httpx library (already in requirements.txt ✅)
- Access to PD and SBA APIs (have URLs ✅)
- Real JSON samples (provided by user ✅)
Next Steps
- Read detailed specifications in
player-model-specs/directory - Start with API models (PD first, then SBA)
- Implement game models
- Create mapper layer
- Build API client
- Write comprehensive tests
- Update PlayResolver to use ratings
Current Status: 📝 Planning Complete - Ready to start implementation Last Updated: 2025-10-25 Week: 6 of Phase 2