Split player model architecture into dedicated documentation files for clarity and maintainability. Added Phase 1 status tracking and comprehensive player model specs covering API models, game models, mappers, and testing strategy. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
313 lines
7.2 KiB
Markdown
313 lines
7.2 KiB
Markdown
# SBA API Models Specification
|
|
|
|
**Purpose**: Pydantic models that exactly match SBA API responses
|
|
|
|
**File**: `backend/app/models/api_models.py` (SBA section)
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
SBA player data comes from **1 API call**:
|
|
- `/players/:player_id?short_output=false` - Player with nested team data
|
|
|
|
Much simpler than PD - no separate ratings calls needed.
|
|
|
|
---
|
|
|
|
## Base URL
|
|
|
|
```python
|
|
SBA_API_BASE_URL = "https://api.sba.manticorum.com/"
|
|
```
|
|
|
|
---
|
|
|
|
## Model Hierarchy
|
|
|
|
```
|
|
SbaPlayerApi (player data)
|
|
└─ team: SbaTeamApi
|
|
├─ manager1: SbaManagerApi
|
|
├─ manager2: Optional[SbaManagerApi]
|
|
└─ division: SbaDivisionApi
|
|
```
|
|
|
|
---
|
|
|
|
## Nested Models
|
|
|
|
### SbaManagerApi
|
|
|
|
**JSON Example**:
|
|
```json
|
|
{
|
|
"id": 3,
|
|
"name": "Cal",
|
|
"image": null,
|
|
"headline": null,
|
|
"bio": null
|
|
}
|
|
```
|
|
|
|
**Pydantic Model**:
|
|
```python
|
|
class SbaManagerApi(BaseModel):
|
|
"""SBA Manager (nested in team response)"""
|
|
id: int
|
|
name: str
|
|
image: Optional[str] = None
|
|
headline: Optional[str] = None
|
|
bio: Optional[str] = None
|
|
```
|
|
|
|
### SbaDivisionApi
|
|
|
|
**JSON Example**:
|
|
```json
|
|
{
|
|
"id": 41,
|
|
"division_name": "Big Chungus",
|
|
"division_abbrev": "BBC",
|
|
"league_name": "SBa",
|
|
"league_abbrev": "SBa",
|
|
"season": 12
|
|
}
|
|
```
|
|
|
|
**Pydantic Model**:
|
|
```python
|
|
class SbaDivisionApi(BaseModel):
|
|
"""SBA Division (nested in team response)"""
|
|
id: int
|
|
division_name: str
|
|
division_abbrev: str
|
|
league_name: str
|
|
league_abbrev: str
|
|
season: int
|
|
```
|
|
|
|
### SbaTeamApi
|
|
|
|
**JSON Example**:
|
|
```json
|
|
{
|
|
"id": 499,
|
|
"abbrev": "WV",
|
|
"sname": "Black Bears",
|
|
"lname": "West Virginia Black Bears",
|
|
"manager_legacy": null,
|
|
"division_legacy": null,
|
|
"gmid": "258104532423147520",
|
|
"gmid2": null,
|
|
"manager1": {
|
|
"id": 3,
|
|
"name": "Cal",
|
|
"image": null,
|
|
"headline": null,
|
|
"bio": null
|
|
},
|
|
"manager2": null,
|
|
"division": {
|
|
"id": 41,
|
|
"division_name": "Big Chungus",
|
|
"division_abbrev": "BBC",
|
|
"league_name": "SBa",
|
|
"league_abbrev": "SBa",
|
|
"season": 12
|
|
},
|
|
"mascot": null,
|
|
"stadium": "https://i.postimg.cc/rpRZ2NNF/wvpark.png",
|
|
"gsheet": null,
|
|
"thumbnail": "https://i.postimg.cc/HjDc8bBF/blackbears-transparent.png",
|
|
"color": "6699FF",
|
|
"dice_color": null,
|
|
"season": 12,
|
|
"auto_draft": null
|
|
}
|
|
```
|
|
|
|
**Pydantic Model**:
|
|
```python
|
|
class SbaTeamApi(BaseModel):
|
|
"""SBA Team (nested in player response)"""
|
|
id: int
|
|
abbrev: str
|
|
sname: str # Short name
|
|
lname: str # Long name
|
|
manager_legacy: Optional[str] = None
|
|
division_legacy: Optional[str] = None
|
|
gmid: str # Discord Guild/Manager ID
|
|
gmid2: Optional[str] = None
|
|
manager1: SbaManagerApi
|
|
manager2: Optional[SbaManagerApi] = None
|
|
division: SbaDivisionApi
|
|
mascot: Optional[str] = None
|
|
stadium: Optional[str] = None
|
|
gsheet: Optional[str] = None
|
|
thumbnail: Optional[str] = None
|
|
color: str # Hex color without #
|
|
dice_color: Optional[str] = None
|
|
season: int
|
|
auto_draft: Optional[bool] = None
|
|
```
|
|
|
|
---
|
|
|
|
## Player Model
|
|
|
|
### SbaPlayerApi
|
|
|
|
**API Endpoint**: `GET /players/:player_id?short_output=false`
|
|
|
|
**JSON Example**:
|
|
```json
|
|
{
|
|
"id": 12288,
|
|
"name": "Ronald Acuna Jr",
|
|
"wara": 0.0,
|
|
"image": "https://sba-cards-2024.s3.us-east-1.amazonaws.com/2024-cards/ronald-acuna-jr.png",
|
|
"image2": null,
|
|
"team": {
|
|
"id": 499,
|
|
"abbrev": "WV",
|
|
"sname": "Black Bears",
|
|
"lname": "West Virginia Black Bears",
|
|
"manager_legacy": null,
|
|
"division_legacy": null,
|
|
"gmid": "258104532423147520",
|
|
"gmid2": null,
|
|
"manager1": {
|
|
"id": 3,
|
|
"name": "Cal",
|
|
"image": null,
|
|
"headline": null,
|
|
"bio": null
|
|
},
|
|
"manager2": null,
|
|
"division": {
|
|
"id": 41,
|
|
"division_name": "Big Chungus",
|
|
"division_abbrev": "BBC",
|
|
"league_name": "SBa",
|
|
"league_abbrev": "SBa",
|
|
"season": 12
|
|
},
|
|
"mascot": null,
|
|
"stadium": "https://i.postimg.cc/rpRZ2NNF/wvpark.png",
|
|
"gsheet": null,
|
|
"thumbnail": "https://i.postimg.cc/HjDc8bBF/blackbears-transparent.png",
|
|
"color": "6699FF",
|
|
"dice_color": null,
|
|
"season": 12,
|
|
"auto_draft": null
|
|
},
|
|
"season": 12,
|
|
"pitcher_injury": null,
|
|
"pos_1": "RF",
|
|
"pos_2": null,
|
|
"pos_3": null,
|
|
"pos_4": null,
|
|
"pos_5": null,
|
|
"pos_6": null,
|
|
"pos_7": null,
|
|
"pos_8": null,
|
|
"last_game": null,
|
|
"last_game2": null,
|
|
"il_return": null,
|
|
"demotion_week": 16,
|
|
"headshot": null,
|
|
"vanity_card": null,
|
|
"strat_code": "Acuna,R",
|
|
"bbref_id": "acunaro01",
|
|
"injury_rating": "5p30",
|
|
"sbaplayer": null
|
|
}
|
|
```
|
|
|
|
**Pydantic Model**:
|
|
```python
|
|
class SbaPlayerApi(BaseModel):
|
|
"""SBA Player API response"""
|
|
id: int
|
|
name: str
|
|
wara: float # Wins Above Replacement Average
|
|
image: str # Card image URL
|
|
image2: Optional[str] = None
|
|
team: SbaTeamApi
|
|
season: int
|
|
|
|
# Positions (up to 8)
|
|
pos_1: Optional[str] = None
|
|
pos_2: Optional[str] = None
|
|
pos_3: Optional[str] = None
|
|
pos_4: Optional[str] = None
|
|
pos_5: Optional[str] = None
|
|
pos_6: Optional[str] = None
|
|
pos_7: Optional[str] = None
|
|
pos_8: Optional[str] = None
|
|
|
|
# Injury/status tracking
|
|
pitcher_injury: Optional[str] = None
|
|
last_game: Optional[str] = None
|
|
last_game2: Optional[str] = None
|
|
il_return: Optional[str] = None # Injured list return date
|
|
demotion_week: Optional[int] = None
|
|
|
|
# Additional metadata
|
|
headshot: Optional[str] = None
|
|
vanity_card: Optional[str] = None
|
|
strat_code: Optional[str] = None # Strat-O-Matic code
|
|
bbref_id: str # Baseball Reference ID
|
|
injury_rating: Optional[str] = None # e.g., "5p30"
|
|
sbaplayer: Optional[int] = None # Link to another player record?
|
|
```
|
|
|
|
---
|
|
|
|
## Usage Example
|
|
|
|
```python
|
|
import httpx
|
|
from app.models.api_models import SbaPlayerApi
|
|
|
|
# Fetch and deserialize player
|
|
async with httpx.AsyncClient() as client:
|
|
response = await client.get(
|
|
"https://api.sba.manticorum.com/players/12288?short_output=false"
|
|
)
|
|
player = SbaPlayerApi(**response.json())
|
|
|
|
# Access nested data with full type safety
|
|
print(f"{player.name} plays for {player.team.lname}")
|
|
print(f"Manager: {player.team.manager1.name}")
|
|
print(f"Division: {player.team.division.division_name}")
|
|
```
|
|
|
|
---
|
|
|
|
## Differences from PD
|
|
|
|
| Aspect | SBA | PD |
|
|
|--------|-----|-----|
|
|
| **API Calls** | 1 call | 3 calls (player + batting + pitching) |
|
|
| **Primary Key** | `id` | `player_id` |
|
|
| **Name Field** | `name` | `p_name` |
|
|
| **Team Data** | Full nested object | Just `mlbclub` string |
|
|
| **Ratings** | None (simplified gameplay) | Detailed outcome probabilities |
|
|
| **Complexity** | Simple | Complex |
|
|
|
|
---
|
|
|
|
## Notes
|
|
|
|
1. **Single API call**: Much simpler than PD - all data in one response
|
|
2. **Rich team data**: Full team/manager/division hierarchy included
|
|
3. **No ratings**: SBA uses simplified result charts, not detailed probabilities
|
|
4. **Position handling**: Same pos_1-8 pattern as PD
|
|
5. **Injury tracking**: Additional fields for roster management (IL, demotion)
|
|
|
|
---
|
|
|
|
**Next**: See [game-models.md](./game-models.md) for game-optimized player models
|