strat-gameplay-webapp/backend/app/services/pd_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

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()