strat-gameplay-webapp/.claude/implementation/player-model-specs/api-models-sba.md
Cal Corum f9aa653c37 CLAUDE: Reorganize Week 6 documentation and separate player model specifications
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>
2025-10-25 23:48:57 -05:00

7.2 KiB

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

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:

{
    "id": 3,
    "name": "Cal",
    "image": null,
    "headline": null,
    "bio": null
}

Pydantic Model:

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:

{
    "id": 41,
    "division_name": "Big Chungus",
    "division_abbrev": "BBC",
    "league_name": "SBa",
    "league_abbrev": "SBa",
    "season": 12
}

Pydantic Model:

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:

{
    "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:

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:

{
    "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:

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

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 for game-optimized player models