Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion todo/models/audit_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
52 changes: 51 additions & 1 deletion todo/services/team_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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),
Expand All @@ -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
3 changes: 2 additions & 1 deletion todo/views/team.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading