diff --git a/todo/models/audit_log.py b/todo/models/audit_log.py index e419ac0f..aa17809c 100644 --- a/todo/models/audit_log.py +++ b/todo/models/audit_log.py @@ -13,7 +13,7 @@ class AuditLogModel(Document): previous_executor_id: PyObjectId | None = None new_executor_id: PyObjectId | None = None spoc_id: PyObjectId | None = None - action: str # e.g., "assigned_to_team", "unassigned_from_team", "status_changed", "reassign_executor" + action: str # e.g., "assigned_to_team", "unassigned_from_team", "status_changed", "reassign_executor", "team_created", "member_joined_team", "member_added_to_team", "member_removed_from_team", "team_updated" timestamp: datetime = Field(default_factory=lambda: datetime.now(timezone.utc)) # For status changes status_from: str | None = None diff --git a/todo/services/team_service.py b/todo/services/team_service.py index df4fd0d0..36010209 100644 --- a/todo/services/team_service.py +++ b/todo/services/team_service.py @@ -8,6 +8,8 @@ from todo.constants.messages import AppMessages from todo.utils.invite_code_utils import generate_invite_code from typing import List +from todo.models.audit_log import AuditLogModel +from todo.repositories.audit_log_repository import AuditLogRepository DEFAULT_ROLE_ID = "1" @@ -90,6 +92,15 @@ def create_team(cls, dto: CreateTeamDTO, created_by_user_id: str) -> CreateTeamR if user_teams: UserTeamDetailsRepository.create_many(user_teams) + # Audit log for team creation + AuditLogRepository.create( + AuditLogModel( + team_id=created_team.id, + action="team_created", + performed_by=PyObjectId(created_by_user_id), + ) + ) + # Convert to DTO team_dto = TeamDTO( id=str(created_team.id), @@ -226,6 +237,15 @@ def join_team_by_invite_code(cls, invite_code: str, user_id: str) -> TeamDTO: ) UserTeamDetailsRepository.create(user_team) + # Audit log for team join + AuditLogRepository.create( + AuditLogModel( + team_id=team.id, + action="member_joined_team", + performed_by=PyObjectId(user_id), + ) + ) + # 4. Return team details return TeamDTO( id=str(team.id), @@ -283,6 +303,15 @@ def update_team(cls, team_id: str, dto: UpdateTeamDTO, updated_by_user_id: str) if not success: raise ValueError(f"Failed to update team members for team with id {team_id}") + # Audit log for team update + AuditLogRepository.create( + AuditLogModel( + team_id=PyObjectId(team_id), + action="team_updated", + performed_by=PyObjectId(updated_by_user_id), + ) + ) + # Convert to DTO return TeamDTO( id=str(updated_team.id), @@ -364,6 +393,17 @@ def add_team_members(cls, team_id: str, member_ids: List[str], added_by_user_id: if new_user_teams: UserTeamDetailsRepository.create_many(new_user_teams) + # Audit log for team member addition + for member_id in member_ids: + AuditLogRepository.create( + AuditLogModel( + team_id=team.id, + action="member_added_to_team", + performed_by=PyObjectId(added_by_user_id), + details={"added_member_id": member_id}, + ) + ) + # Return updated team details return TeamDTO( id=str(team.id), @@ -384,10 +424,20 @@ class TeamOrUserNotFound(Exception): pass @classmethod - def remove_member_from_team(cls, user_id: str, team_id: str): + def remove_member_from_team(cls, user_id: str, team_id: str, removed_by_user_id: str = None): from todo.repositories.user_team_details_repository import UserTeamDetailsRepository success = UserTeamDetailsRepository.remove_member_from_team(user_id=user_id, team_id=team_id) if not success: raise cls.TeamOrUserNotFound() + + # Audit log for team member removal + AuditLogRepository.create( + AuditLogModel( + team_id=PyObjectId(team_id), + action="member_removed_from_team", + performed_by=PyObjectId(removed_by_user_id) if removed_by_user_id else PyObjectId(user_id), + ) + ) + return True diff --git a/todo/views/team.py b/todo/views/team.py index c2d44d7f..e50304b2 100644 --- a/todo/views/team.py +++ b/todo/views/team.py @@ -473,7 +473,8 @@ def delete(self, request, team_id, user_id): from todo.services.team_service import TeamService try: - TeamService.remove_member_from_team(user_id=user_id, team_id=team_id) + # Pass the user performing the removal (request.user_id) and the user being removed (user_id) + TeamService.remove_member_from_team(user_id=user_id, team_id=team_id, removed_by_user_id=request.user_id) return Response(status=status.HTTP_204_NO_CONTENT) except TeamService.TeamOrUserNotFound: return Response({"detail": "Team or user not found."}, status=status.HTTP_404_NOT_FOUND)