fix: Fix CSV export, season filtering, and position matching in refactored services
Integration testing revealed three issues with the refactored service layer:
1. CSV Export Format
- Nested team/sbaplayer dicts were being dumped as strings
- Now flattens team to abbreviation, sbaplayer to ID
- Matches original CSV format from pre-refactor code
2. Season=0 Filter
- season=0 was filtering for WHERE season=0 (returns nothing)
- Now correctly returns all seasons when season=0 or None
- Affects 13,266 total players across all seasons
3. Generic Position "P"
- pos=P returned no results (players have SP/RP/CP, not P)
- Now expands P to match SP, RP, CP pitcher positions
- Applied to both DB filtering and Python mock filtering
4. Roster Endpoint Enhancement
- Added default /teams/{id}/roster endpoint (assumes 'current')
- Existing /teams/{id}/roster/{which} endpoint unchanged
All changes maintain backward compatibility and pass integration tests.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
2189aea8da
commit
56fca1fa03
@ -49,6 +49,17 @@ async def get_one_team(team_id: int):
|
|||||||
return TeamService.get_team(team_id)
|
return TeamService.get_team(team_id)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get('/{team_id}/roster')
|
||||||
|
@handle_db_errors
|
||||||
|
@cache_result(ttl=30*60, key_prefix='team-roster')
|
||||||
|
async def get_team_roster_default(
|
||||||
|
team_id: int,
|
||||||
|
sort: Optional[str] = None
|
||||||
|
):
|
||||||
|
"""Get team roster with IL lists (defaults to current season)."""
|
||||||
|
return TeamService.get_team_roster(team_id, 'current', sort=sort)
|
||||||
|
|
||||||
|
|
||||||
@router.get('/{team_id}/roster/{which}')
|
@router.get('/{team_id}/roster/{which}')
|
||||||
@handle_db_errors
|
@handle_db_errors
|
||||||
@cache_result(ttl=30*60, key_prefix='team-roster')
|
@cache_result(ttl=30*60, key_prefix='team-roster')
|
||||||
|
|||||||
@ -182,6 +182,13 @@ class PlayerService(BaseService):
|
|||||||
|
|
||||||
if pos:
|
if pos:
|
||||||
p_list = [x.upper() for x in pos]
|
p_list = [x.upper() for x in pos]
|
||||||
|
|
||||||
|
# Expand generic "P" to match all pitcher positions
|
||||||
|
pitcher_positions = ['SP', 'RP', 'CP']
|
||||||
|
if 'P' in p_list:
|
||||||
|
p_list.remove('P')
|
||||||
|
p_list.extend(pitcher_positions)
|
||||||
|
|
||||||
pos_conditions = (
|
pos_conditions = (
|
||||||
(Player.pos_1 << p_list)
|
(Player.pos_1 << p_list)
|
||||||
| (Player.pos_2 << p_list)
|
| (Player.pos_2 << p_list)
|
||||||
@ -216,6 +223,13 @@ class PlayerService(BaseService):
|
|||||||
return False
|
return False
|
||||||
if pos:
|
if pos:
|
||||||
p_list = [p.upper() for p in pos]
|
p_list = [p.upper() for p in pos]
|
||||||
|
|
||||||
|
# Expand generic "P" to match all pitcher positions
|
||||||
|
pitcher_positions = ['SP', 'RP', 'CP']
|
||||||
|
if 'P' in p_list:
|
||||||
|
p_list.remove('P')
|
||||||
|
p_list.extend(pitcher_positions)
|
||||||
|
|
||||||
player_pos = [
|
player_pos = [
|
||||||
player.get(f"pos_{i}")
|
player.get(f"pos_{i}")
|
||||||
for i in range(1, 9)
|
for i in range(1, 9)
|
||||||
@ -547,13 +561,27 @@ class PlayerService(BaseService):
|
|||||||
if not players:
|
if not players:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
# Build CSV from dict data (works with mocks)
|
# Flatten nested objects for CSV export
|
||||||
|
flattened_players = []
|
||||||
|
for player in players:
|
||||||
|
flat_player = player.copy()
|
||||||
|
|
||||||
|
# Flatten team object to just abbreviation
|
||||||
|
if isinstance(flat_player.get('team'), dict):
|
||||||
|
flat_player['team'] = flat_player['team'].get('abbrev', '')
|
||||||
|
|
||||||
|
# Flatten sbaplayer object to just ID
|
||||||
|
if isinstance(flat_player.get('sbaplayer'), dict):
|
||||||
|
flat_player['sbaplayer'] = flat_player['sbaplayer'].get('id', '')
|
||||||
|
|
||||||
|
flattened_players.append(flat_player)
|
||||||
|
|
||||||
|
# Build CSV from flattened data
|
||||||
output = io.StringIO()
|
output = io.StringIO()
|
||||||
if players:
|
if flattened_players:
|
||||||
writer = csv.DictWriter(output, fieldnames=players[0].keys())
|
writer = csv.DictWriter(output, fieldnames=flattened_players[0].keys())
|
||||||
writer.writeheader()
|
writer.writeheader()
|
||||||
writer.writerows(players)
|
writer.writerows(flattened_players)
|
||||||
|
|
||||||
return output.getvalue()
|
return output.getvalue()
|
||||||
|
|
||||||
@ -595,7 +623,9 @@ class RealPlayerRepository:
|
|||||||
self._model = model_class
|
self._model = model_class
|
||||||
|
|
||||||
def select_season(self, season: int):
|
def select_season(self, season: int):
|
||||||
"""Return query for season."""
|
"""Return query for season. Season=0 or None returns all seasons."""
|
||||||
|
if season == 0 or season is None:
|
||||||
|
return self._model.select()
|
||||||
return self._model.select().where(self._model.season == season)
|
return self._model.select().where(self._model.season == season)
|
||||||
|
|
||||||
def get_by_id(self, player_id: int):
|
def get_by_id(self, player_id: int):
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user