Implemented complete REST API to retrieve NHL player statistics for entire teams:
Application Layer:
- Created DTOs for Player, SkaterStats, GoalieStats, and PlayerWithStatsDTO
- Implemented GetTeamPlayerStatsUseCase that orchestrates data retrieval
- Transforms domain entities to API-friendly DTOs
Presentation Layer:
- Created /api/v1/teams/{team_id}/players endpoint (all players)
- Created /api/v1/teams/{team_id}/players/skaters endpoint (skaters only)
- Created /api/v1/teams/{team_id}/players/goalies endpoint (goalies only)
- Integrated routes into main FastAPI application
Features:
- Retrieves complete roster for any NHL team by abbreviation
- Aggregates current season statistics for each player
- Differentiates between skater and goalie statistics
- Proper error handling with 404 for invalid teams
- Follows CLEAN architecture with dependency injection
Documentation:
- Created comprehensive API_GUIDE.md with usage examples
- Includes setup instructions, endpoint documentation
- Python, JavaScript, and cURL examples
- Complete list of NHL team abbreviations
Testing:
- Added test scripts for both direct adapter testing and REST API testing
- Verified functionality with Toronto Maple Leafs data
The REST API is now ready for integration with fantasy hockey analysis tools.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
128 lines
5.0 KiB
Python
128 lines
5.0 KiB
Python
"""Use case for retrieving all player statistics for a team."""
|
|
from typing import List
|
|
import logging
|
|
|
|
from src.domain.repositories import PlayerRepository
|
|
from src.application.dto import (
|
|
PlayerDTO,
|
|
SkaterStatsDTO,
|
|
GoalieStatsDTO,
|
|
PlayerWithStatsDTO,
|
|
)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class GetTeamPlayerStatsUseCase:
|
|
"""
|
|
Use case for retrieving all players and their statistics for a team.
|
|
|
|
This use case demonstrates CLEAN architecture principles:
|
|
- Orchestrates multiple repository calls
|
|
- Transforms domain entities to DTOs for API response
|
|
- Contains business logic independent of frameworks
|
|
"""
|
|
|
|
def __init__(self, player_repository: PlayerRepository):
|
|
"""
|
|
Initializes the use case with required dependencies.
|
|
|
|
Args:
|
|
player_repository: Repository for accessing player data
|
|
"""
|
|
self.player_repository = player_repository
|
|
|
|
async def execute(self, team_id: str) -> List[PlayerWithStatsDTO]:
|
|
"""
|
|
Retrieves all players and their statistics for a team.
|
|
|
|
Args:
|
|
team_id: The team abbreviation or ID (e.g., "TOR")
|
|
|
|
Returns:
|
|
List of players with their current season statistics
|
|
"""
|
|
# Get team roster
|
|
players = await self.player_repository.get_players_by_team(team_id)
|
|
|
|
if not players:
|
|
logger.warning(f"No players found for team {team_id}")
|
|
return []
|
|
|
|
# Get statistics for each player
|
|
players_with_stats = []
|
|
|
|
for player in players:
|
|
# Convert player entity to DTO
|
|
player_dto = PlayerDTO(
|
|
id=player.id,
|
|
first_name=player.first_name,
|
|
last_name=player.last_name,
|
|
full_name=player.full_name,
|
|
jersey_number=player.jersey_number,
|
|
position=player.position,
|
|
team_id=player.team_id,
|
|
)
|
|
|
|
# Get appropriate statistics based on position
|
|
stats_dto = None
|
|
stats_type = "none"
|
|
|
|
try:
|
|
if player.is_goalie():
|
|
goalie_stats = await self.player_repository.get_goalie_stats(
|
|
player.id
|
|
)
|
|
if goalie_stats:
|
|
stats_dto = GoalieStatsDTO(
|
|
player_id=goalie_stats.player_id,
|
|
games_played=goalie_stats.games_played,
|
|
games_started=goalie_stats.games_started,
|
|
wins=goalie_stats.wins,
|
|
losses=goalie_stats.losses,
|
|
overtime_losses=goalie_stats.overtime_losses,
|
|
saves=goalie_stats.saves,
|
|
shots_against=goalie_stats.shots_against,
|
|
goals_against=goalie_stats.goals_against,
|
|
save_percentage=goalie_stats.save_percentage,
|
|
goals_against_average=goalie_stats.goals_against_average,
|
|
shutouts=goalie_stats.shutouts,
|
|
time_on_ice=goalie_stats.time_on_ice,
|
|
)
|
|
stats_type = "goalie"
|
|
else:
|
|
skater_stats = await self.player_repository.get_skater_stats(
|
|
player.id
|
|
)
|
|
if skater_stats:
|
|
stats_dto = SkaterStatsDTO(
|
|
player_id=skater_stats.player_id,
|
|
games_played=skater_stats.games_played,
|
|
goals=skater_stats.goals,
|
|
assists=skater_stats.assists,
|
|
points=skater_stats.points,
|
|
plus_minus=skater_stats.plus_minus,
|
|
penalty_minutes=skater_stats.penalty_minutes,
|
|
shots=skater_stats.shots,
|
|
shooting_percentage=skater_stats.shooting_percentage,
|
|
time_on_ice_per_game=skater_stats.time_on_ice_per_game,
|
|
powerplay_goals=skater_stats.powerplay_goals,
|
|
powerplay_points=skater_stats.powerplay_points,
|
|
shorthanded_goals=skater_stats.shorthanded_goals,
|
|
game_winning_goals=skater_stats.game_winning_goals,
|
|
faceoff_percentage=skater_stats.faceoff_percentage,
|
|
hits=skater_stats.hits,
|
|
blocked_shots=skater_stats.blocked_shots,
|
|
)
|
|
stats_type = "skater"
|
|
except Exception as e:
|
|
logger.error(f"Error fetching stats for player {player.id}: {e}")
|
|
|
|
players_with_stats.append(
|
|
PlayerWithStatsDTO(
|
|
player=player_dto, stats=stats_dto, stats_type=stats_type
|
|
)
|
|
)
|
|
|
|
return players_with_stats
|