diff --git a/commands/draft/picks.py b/commands/draft/picks.py index 1f433bb..d24c12e 100644 --- a/commands/draft/picks.py +++ b/commands/draft/picks.py @@ -304,10 +304,15 @@ class DraftPicksCog(commands.Cog): await interaction.followup.send(embed=embed) return - # Update player team + # Get current league state for dem_week calculation + from services.league_service import league_service + current = await league_service.get_current_state() + + # Update player team with dem_week set to current.week + 2 for draft picks updated_player = await player_service.update_player_team( player_obj.id, - team.id + team.id, + dem_week=current.week + 2 if current else None ) if not updated_player: diff --git a/services/player_service.py b/services/player_service.py index 0927b16..d2e5a2c 100644 --- a/services/player_service.py +++ b/services/player_service.py @@ -372,33 +372,65 @@ class PlayerService(BaseService[Player]): return None async def update_player_team( - self, player_id: int, new_team_id: int + self, + player_id: int, + new_team_id: int, + dem_week: Optional[int] = None ) -> Optional[Player]: """ - Update a player's team assignment (for real-time IL moves). - - This is used for immediate roster changes where the player needs to show - up on their new team right away, rather than waiting for transaction processing. + Update a player's team assignment. Args: player_id: Player ID to update new_team_id: New team ID to assign + dem_week: Optional demotion/designation week to set. + - Transaction freeze: Current.week + 2 + - Draft picks: Current.week + 2 + - IL moves: Current.week + - Admin/Other: Not set (None) Returns: Updated player instance or None Raises: APIException: If player update fails + + Examples: + # Transaction freeze (Monday morning) + await player_service.update_player_team( + player_id, new_team_id, dem_week=current.week + 2 + ) + + # Draft pick (manual or auto-draft) + await player_service.update_player_team( + player_id, new_team_id, dem_week=current.week + 2 + ) + + # IL move (/ilmove command) + await player_service.update_player_team( + player_id, new_team_id, dem_week=current.week + ) + + # Admin move (no dem_week) + await player_service.update_player_team(player_id, new_team_id) """ try: - logger.info(f"Updating player {player_id} team to {new_team_id}") - updated_player = await self.update_player( - player_id, {"team_id": new_team_id} + logger.info( + f"Updating player {player_id} team to {new_team_id}" + + (f" with dem_week={dem_week}" if dem_week is not None else "") ) + # Build update dictionary + updates = {"team_id": new_team_id} + if dem_week is not None: + updates["dem_week"] = dem_week + + updated_player = await self.update_player(player_id, updates) + if updated_player: logger.info( f"Successfully updated player {player_id} to team {new_team_id}" + + (f" with dem_week={dem_week}" if dem_week is not None else "") ) return updated_player else: diff --git a/tasks/draft_monitor.py b/tasks/draft_monitor.py index 02a445f..aa5e7a1 100644 --- a/tasks/draft_monitor.py +++ b/tasks/draft_monitor.py @@ -340,11 +340,16 @@ class DraftMonitorTask: self.logger.error(f"Failed to update pick {draft_pick.id}") return False - # Update player team + # Get current league state for dem_week calculation from services.player_service import player_service + from services.league_service import league_service + current = await league_service.get_current_state() + + # Update player team with dem_week set to current.week + 2 for draft picks updated_player = await player_service.update_player_team( player.id, - draft_pick.owner.id + draft_pick.owner.id, + dem_week=current.week + 2 if current else None ) if not updated_player: diff --git a/tasks/transaction_freeze.py b/tasks/transaction_freeze.py index 37bf448..23b5d30 100644 --- a/tasks/transaction_freeze.py +++ b/tasks/transaction_freeze.py @@ -465,11 +465,12 @@ class TransactionFreezeTask: for transaction in transactions: try: - # Update player's team via PATCH /players/{player_id}?team_id={new_team_id} + # Update player's team via PATCH /players/{player_id}?team_id={new_team_id}&dem_week={current.week+2} await self._execute_player_update( player_id=transaction.player.id, new_team_id=transaction.newteam.id, - player_name=transaction.player.name + player_name=transaction.player.name, + dem_week=current.week + 2 ) success_count += 1 @@ -683,7 +684,8 @@ class TransactionFreezeTask: self, player_id: int, new_team_id: int, - player_name: str + player_name: str, + dem_week: Optional[int] = None ) -> bool: """ Execute a player roster update via API PATCH. @@ -692,6 +694,7 @@ class TransactionFreezeTask: player_id: Player database ID new_team_id: New team ID to assign player_name: Player name for logging + dem_week: Optional designation week to set (typically Current.week + 2) Returns: True if update successful, False otherwise @@ -704,10 +707,15 @@ class TransactionFreezeTask: "Updating player roster", player_id=player_id, player_name=player_name, - new_team_id=new_team_id + new_team_id=new_team_id, + dem_week=dem_week ) - updated_player = await player_service.update_player_team(player_id, new_team_id) + updated_player = await player_service.update_player_team( + player_id, + new_team_id, + dem_week=dem_week + ) # Verify response (200 or 204 indicates success) if updated_player is not None: @@ -715,7 +723,8 @@ class TransactionFreezeTask: "Successfully updated player", player_id=player_id, player_name=player_name, - new_team_id=new_team_id + new_team_id=new_team_id, + dem_week=dem_week ) return True else: @@ -723,7 +732,8 @@ class TransactionFreezeTask: "Player update returned no response", player_id=player_id, player_name=player_name, - new_team_id=new_team_id + new_team_id=new_team_id, + dem_week=dem_week ) return False @@ -733,6 +743,7 @@ class TransactionFreezeTask: player_id=player_id, player_name=player_name, new_team_id=new_team_id, + dem_week=dem_week, error=str(e), exc_info=True ) diff --git a/tests/test_services_player_service.py b/tests/test_services_player_service.py index 0eeaa74..e193f67 100644 --- a/tests/test_services_player_service.py +++ b/tests/test_services_player_service.py @@ -392,3 +392,156 @@ class TestGlobalPlayerServiceInstance: # But same configuration assert service1.model_class == service2.model_class assert service1.endpoint == service2.endpoint + + +class TestPlayerTeamUpdateWithDemWeek: + """Test player team update functionality with dem_week parameter.""" + + @pytest.fixture + def mock_client(self): + """Mock API client.""" + client = AsyncMock() + return client + + @pytest.fixture + def player_service_instance(self, mock_client): + """Create PlayerService instance with mocked client.""" + service = PlayerService() + service._client = mock_client + return service + + def create_player_data( + self, player_id: int, name: str, team_id: int = 5, position: str = "C", **kwargs + ): + """Create complete player data for testing.""" + base_data = { + "id": player_id, + "name": name, + "wara": 2.5, + "season": 12, + "team_id": team_id, + "image": f"https://example.com/player{player_id}.jpg", + "pos_1": position, + } + base_data.update(kwargs) + return base_data + + @pytest.mark.asyncio + async def test_update_player_team_with_dem_week( + self, player_service_instance, mock_client + ): + """ + Test player team update with dem_week parameter. + + This test verifies that when dem_week is provided, it is correctly + included in the update dictionary passed to the API. + """ + player_id = 123 + new_team_id = 5 + dem_week = 15 + + # Mock the API response + mock_client.patch.return_value = self.create_player_data( + player_id, "Test Player", team_id=new_team_id + ) + + result = await player_service_instance.update_player_team( + player_id, new_team_id, dem_week=dem_week + ) + + # Verify result + assert result is not None + assert result.team_id == new_team_id + + # Verify API call included dem_week + mock_client.patch.assert_called_once_with( + "players", + {"team_id": new_team_id, "dem_week": dem_week}, + player_id, + use_query_params=True + ) + + @pytest.mark.asyncio + async def test_update_player_team_without_dem_week( + self, player_service_instance, mock_client + ): + """ + Test player team update without dem_week parameter maintains backwards compatibility. + + This test verifies that when dem_week is not provided (None), it is NOT + included in the update dictionary, maintaining backwards compatibility with + admin commands that don't need to set dem_week. + """ + player_id = 123 + new_team_id = 5 + + # Mock the API response + mock_client.patch.return_value = self.create_player_data( + player_id, "Test Player", team_id=new_team_id + ) + + result = await player_service_instance.update_player_team( + player_id, new_team_id + ) + + # Verify result + assert result is not None + assert result.team_id == new_team_id + + # Verify API call did NOT include dem_week + call_args = mock_client.patch.call_args[0][1] + assert "dem_week" not in call_args + assert call_args == {"team_id": new_team_id} + + @pytest.mark.asyncio + async def test_update_player_team_dem_week_zero( + self, player_service_instance, mock_client + ): + """ + Test that dem_week=0 IS included in the update. + + This test verifies that dem_week=0 is treated as a valid value and included + in the update, since 0 is not None and may be a valid week number. + """ + player_id = 123 + new_team_id = 5 + dem_week = 0 + + # Mock the API response + mock_client.patch.return_value = self.create_player_data( + player_id, "Test Player", team_id=new_team_id + ) + + await player_service_instance.update_player_team( + player_id, new_team_id, dem_week=dem_week + ) + + # Verify API call included dem_week=0 + call_args = mock_client.patch.call_args[0][1] + assert call_args == {"team_id": new_team_id, "dem_week": 0} + + @pytest.mark.asyncio + async def test_update_player_team_dem_week_none_explicit( + self, player_service_instance, mock_client + ): + """ + Test that explicitly passing dem_week=None does NOT include it in updates. + + This test verifies that when dem_week=None is explicitly passed, it behaves + the same as not passing the parameter at all. + """ + player_id = 123 + new_team_id = 5 + + # Mock the API response + mock_client.patch.return_value = self.create_player_data( + player_id, "Test Player", team_id=new_team_id + ) + + await player_service_instance.update_player_team( + player_id, new_team_id, dem_week=None + ) + + # Verify API call did NOT include dem_week + call_args = mock_client.patch.call_args[0][1] + assert "dem_week" not in call_args diff --git a/views/transaction_embed.py b/views/transaction_embed.py index 2c63e46..ceea559 100644 --- a/views/transaction_embed.py +++ b/views/transaction_embed.py @@ -289,7 +289,8 @@ class SubmitConfirmationModal(discord.ui.Modal): for txn in created_transactions: updated_player = await player_service.update_player_team( txn.player.id, - txn.newteam.id + txn.newteam.id, + dem_week=current_state.week ) player_updates.append(updated_player)