Files
kempe-backend/src/application/use_cases/get_team_player_stats.py
Michael Simard c10fdd594d 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>
2025-11-23 22:44:39 -06:00

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