strat-gameplay-webapp/backend/app/services/sba_api_client.py
Cal Corum a4b99ee53e CLAUDE: Replace black and flake8 with ruff for formatting and linting
Migrated to ruff for faster, modern code formatting and linting:

Configuration changes:
- pyproject.toml: Added ruff 0.8.6, removed black/flake8
- Configured ruff with black-compatible formatting (88 chars)
- Enabled comprehensive linting rules (pycodestyle, pyflakes, isort,
  pyupgrade, bugbear, comprehensions, simplify, return)
- Updated CLAUDE.md: Changed code quality commands to use ruff

Code improvements (490 auto-fixes):
- Modernized type hints: List[T] → list[T], Dict[K,V] → dict[K,V],
  Optional[T] → T | None
- Sorted all imports (isort integration)
- Removed unused imports
- Fixed whitespace issues
- Reformatted 38 files for consistency

Bug fixes:
- app/core/play_resolver.py: Fixed type hint bug (any → Any)
- tests/unit/core/test_runner_advancement.py: Removed obsolete random mock

Testing:
- All 739 unit tests passing (100%)
- No regressions introduced

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 15:33:21 -06:00

116 lines
3.4 KiB
Python

"""
SBA API client for fetching player data.
Integrates with SBA REST API to retrieve player information
for use in game lineup display.
Author: Claude
Date: 2025-01-10
"""
import logging
import httpx
from app.models.player_models import SbaPlayer
logger = logging.getLogger(f"{__name__}.SbaApiClient")
class SbaApiClient:
"""Client for SBA API player data lookups."""
def __init__(self, base_url: str = "https://api.sba.manticorum.com"):
"""
Initialize SBA API client.
Args:
base_url: Base URL for SBA API (default: production)
"""
self.base_url = base_url
self.timeout = httpx.Timeout(10.0, connect=5.0)
async def get_player(self, player_id: int) -> SbaPlayer:
"""
Fetch player data from SBA API.
Args:
player_id: SBA player ID
Returns:
SbaPlayer instance with all player data
Raises:
httpx.HTTPError: If API request fails
Example:
player = await client.get_player(12288)
print(f"Name: {player.name}") # "Ronald Acuna Jr"
print(f"Positions: {player.get_positions()}") # ['RF']
"""
url = f"{self.base_url}/players/{player_id}"
try:
async with httpx.AsyncClient(timeout=self.timeout) as client:
response = await client.get(url)
response.raise_for_status()
data = response.json()
player = SbaPlayer.from_api_response(data)
logger.info(f"Loaded player {player_id}: {player.name}")
return player
except httpx.HTTPError as e:
logger.error(f"Failed to fetch player {player_id}: {e}")
raise
except Exception as e:
logger.error(f"Unexpected error fetching player {player_id}: {e}")
raise
async def get_players_batch(self, player_ids: list[int]) -> dict[int, SbaPlayer]:
"""
Fetch multiple players in parallel.
Args:
player_ids: List of SBA player IDs to fetch
Returns:
Dictionary mapping player_id to SbaPlayer instance
Players that fail to load will be omitted from the result
Example:
players = await client.get_players_batch([12288, 12289, 12290])
for player_id, player in players.items():
print(f"{player.name}: {player.get_positions()}")
"""
if not player_ids:
return {}
results: dict[int, SbaPlayer] = {}
async with httpx.AsyncClient(timeout=self.timeout) as client:
for player_id in player_ids:
try:
url = f"{self.base_url}/players/{player_id}"
response = await client.get(url)
response.raise_for_status()
data = response.json()
player = SbaPlayer.from_api_response(data)
results[player_id] = player
except httpx.HTTPError as e:
logger.warning(f"Failed to fetch player {player_id}: {e}")
# Continue with other players
except Exception as e:
logger.warning(f"Unexpected error fetching player {player_id}: {e}")
# Continue with other players
logger.info(f"Loaded {len(results)}/{len(player_ids)} players")
return results
# Singleton instance
sba_api_client = SbaApiClient()