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>
108 lines
3.3 KiB
Python
108 lines
3.3 KiB
Python
"""
|
|
PD API client for fetching player position ratings.
|
|
|
|
Integrates with Paper Dynasty API to retrieve defensive ratings
|
|
for use in X-Check resolution.
|
|
|
|
Author: Claude
|
|
Date: 2025-11-03
|
|
"""
|
|
|
|
import logging
|
|
|
|
import httpx
|
|
|
|
from app.models.player_models import PositionRating
|
|
|
|
logger = logging.getLogger(f"{__name__}.PdApiClient")
|
|
|
|
|
|
class PdApiClient:
|
|
"""Client for PD API position rating lookups."""
|
|
|
|
def __init__(self, base_url: str = "https://pd.manticorum.com"):
|
|
"""
|
|
Initialize PD API client.
|
|
|
|
Args:
|
|
base_url: Base URL for PD API (default: production)
|
|
"""
|
|
self.base_url = base_url
|
|
self.timeout = httpx.Timeout(10.0, connect=5.0)
|
|
|
|
async def get_position_ratings(
|
|
self, player_id: int, positions: list[str] | None = None
|
|
) -> list[PositionRating]:
|
|
"""
|
|
Fetch all position ratings for a player.
|
|
|
|
Args:
|
|
player_id: PD player ID
|
|
positions: Optional list of positions to filter (e.g., ['SS', '2B', 'LF'])
|
|
If None, returns all positions for player
|
|
|
|
Returns:
|
|
List of PositionRating objects (one per position played)
|
|
|
|
Raises:
|
|
httpx.HTTPError: If API request fails
|
|
|
|
Example:
|
|
# Get all positions for player 8807
|
|
ratings = await client.get_position_ratings(8807)
|
|
|
|
# Get only infield positions
|
|
ratings = await client.get_position_ratings(8807, ['SS', '2B', '3B'])
|
|
"""
|
|
# Build URL with query parameters
|
|
url = f"{self.base_url}/api/v2/cardpositions"
|
|
params = {"player_id": player_id}
|
|
|
|
# Add position filters if specified
|
|
if positions:
|
|
# httpx will handle multiple values for same parameter
|
|
params["position"] = positions
|
|
|
|
try:
|
|
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
|
response = await client.get(url, params=params)
|
|
response.raise_for_status()
|
|
|
|
data = response.json()
|
|
|
|
# Parse positions from API response
|
|
position_ratings = []
|
|
|
|
# API returns list of position objects directly
|
|
if isinstance(data, list):
|
|
for pos_data in data:
|
|
position_ratings.append(
|
|
PositionRating.from_api_response(pos_data)
|
|
)
|
|
# Or may be wrapped in 'positions' key
|
|
elif isinstance(data, dict) and "positions" in data:
|
|
for pos_data in data["positions"]:
|
|
position_ratings.append(
|
|
PositionRating.from_api_response(pos_data)
|
|
)
|
|
|
|
logger.info(
|
|
f"Loaded {len(position_ratings)} position ratings for player {player_id}"
|
|
)
|
|
return position_ratings
|
|
|
|
except httpx.HTTPError as e:
|
|
logger.error(
|
|
f"Failed to fetch position ratings for player {player_id}: {e}"
|
|
)
|
|
raise
|
|
except Exception as e:
|
|
logger.error(
|
|
f"Unexpected error fetching ratings for player {player_id}: {e}"
|
|
)
|
|
raise
|
|
|
|
|
|
# Singleton instance
|
|
pd_api_client = PdApiClient()
|