Add REST API endpoints for team player statistics
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>
This commit is contained in:
127
src/application/use_cases/get_team_player_stats.py
Normal file
127
src/application/use_cases/get_team_player_stats.py
Normal file
@@ -0,0 +1,127 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user