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:
120
test_nhl_api.py
Normal file
120
test_nhl_api.py
Normal file
@@ -0,0 +1,120 @@
|
||||
"""Manual test script for NHL API integration."""
|
||||
import asyncio
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
# Add src to path for imports
|
||||
sys.path.insert(0, "/Users/michaelsimard/dev/services/project-kempe-backend")
|
||||
|
||||
from src.infrastructure.adapters.nhl import NHLPlayerAdapter, NHLTeamAdapter
|
||||
|
||||
|
||||
async def test_teams():
|
||||
"""Test team retrieval."""
|
||||
print("\n" + "=" * 60)
|
||||
print("TESTING TEAM ADAPTER")
|
||||
print("=" * 60)
|
||||
|
||||
team_adapter = NHLTeamAdapter()
|
||||
|
||||
# Test getting all teams
|
||||
print("\n1. Getting all NHL teams...")
|
||||
teams = await team_adapter.get_all_teams()
|
||||
print(f" Found {len(teams)} teams")
|
||||
|
||||
if teams:
|
||||
print("\n First 5 teams:")
|
||||
for team in teams[:5]:
|
||||
print(f" - {team.full_name} ({team.abbreviation}) - {team.division}")
|
||||
|
||||
# Test getting a specific team Wings
|
||||
print("\n2. Getting Detroit Red Wings by abbreviation (DET)...")
|
||||
wings = await team_adapter.get_team_by_id("DET")
|
||||
if wings:
|
||||
print(f" ✓ Team: {wings.full_name}")
|
||||
print(f" City: {wings.city}")
|
||||
print(f" Division: {wings.division}")
|
||||
print(f" Conference: {wings.conference}")
|
||||
else:
|
||||
print(" ✗ Team not found")
|
||||
|
||||
# Test getting teams by division
|
||||
print("\n3. Getting teams from Atlantic Division...")
|
||||
atlantic_teams = await team_adapter.get_teams_by_division("Atlantic")
|
||||
print(f" Found {len(atlantic_teams)} teams in Atlantic Division")
|
||||
for team in atlantic_teams:
|
||||
print(f" - {team.full_name}")
|
||||
|
||||
|
||||
async def test_players():
|
||||
"""Test player retrieval."""
|
||||
print("\n" + "=" * 60)
|
||||
print("TESTING PLAYER ADAPTER")
|
||||
print("=" * 60)
|
||||
|
||||
player_adapter = NHLPlayerAdapter()
|
||||
|
||||
# Test getting team roster (Toronto Maple Leafs)
|
||||
print("\n1. Getting Detroit Red Wings roster...")
|
||||
players = await player_adapter.get_players_by_team("DET")
|
||||
print(f" Found {len(players)} players on roster")
|
||||
|
||||
if players:
|
||||
print("\n Sample players:")
|
||||
for player in players[:5]:
|
||||
print(
|
||||
f" - #{player.jersey_number or '??'} {player.full_name} ({player.position})"
|
||||
)
|
||||
|
||||
# Get a specific player for stats testing
|
||||
test_player = players[0]
|
||||
print(f"\n2. Getting stats for {test_player.full_name} (ID: {test_player.id})...")
|
||||
|
||||
if test_player.is_goalie():
|
||||
stats = await player_adapter.get_goalie_stats(test_player.id)
|
||||
if stats:
|
||||
print(f" ✓ Goalie Stats:")
|
||||
print(f" Games Played: {stats.games_played}")
|
||||
print(f" Wins: {stats.wins}")
|
||||
print(f" Save %: {stats.save_percentage:.1f}%")
|
||||
print(f" GAA: {stats.goals_against_average:.2f}")
|
||||
else:
|
||||
print(" ✗ No stats found")
|
||||
else:
|
||||
stats = await player_adapter.get_skater_stats(test_player.id)
|
||||
if stats:
|
||||
print(f" ✓ Skater Stats:")
|
||||
print(f" Games Played: {stats.games_played}")
|
||||
print(f" Goals: {stats.goals}")
|
||||
print(f" Assists: {stats.assists}")
|
||||
print(f" Points: {stats.points}")
|
||||
print(f" Plus/Minus: {stats.plus_minus:+d}")
|
||||
else:
|
||||
print(" ✗ No stats found")
|
||||
|
||||
|
||||
async def main():
|
||||
"""Run all tests."""
|
||||
print("\n" + "=" * 60)
|
||||
print("NHL API INTEGRATION TEST")
|
||||
print(f"Date: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
print("=" * 60)
|
||||
|
||||
try:
|
||||
await test_teams()
|
||||
await test_players()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("ALL TESTS COMPLETED")
|
||||
print("=" * 60 + "\n")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n✗ ERROR: {e}")
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user