from dataclasses import dataclass from django.contrib.contenttypes.models import ContentType from django.db import transaction from django.db.models import Q from django.utils import timezone from notifications.cache import invalidate_unread_summary from notifications.models import Notification from notifications.realtime import send_notifications_read_all_event from tasks.models import Task, TaskBoard @dataclass(frozen=True) class TaskBoardNotificationReadResult: notification_ids: list[int] count: int class TaskBoardNotificationReadService: """Marks only notifications related to one task board as read.""" def mark_board_notifications_as_read( self, *, user, board: TaskBoard, ) -> TaskBoardNotificationReadResult: queryset = self._get_unread_queryset(user_id=user.id, board=board) notification_ids = list(queryset.values_list("id", flat=True)) if not notification_ids: return TaskBoardNotificationReadResult([], 0) count = Notification.objects.filter(id__in=notification_ids).update( unread=False, timestamp_read=timezone.now(), ) invalidate_unread_summary(user.id) def notify_after_commit(): invalidate_unread_summary(user.id) send_notifications_read_all_event(user.id, notification_ids) transaction.on_commit(notify_after_commit) return TaskBoardNotificationReadResult(notification_ids, count) def _get_unread_queryset(self, *, user_id: int, board: TaskBoard): task_content_type = ContentType.objects.get_for_model(Task) board_content_type = ContentType.objects.get_for_model(TaskBoard) board_id = board.id board_id_text = str(board_id) task_ids = [ str(task_id) for task_id in board.tasks.values_list("id", flat=True) ] board_filter = ( Q(data__board_id=board_id) | Q(data__board_id=board_id_text) | Q( action_object_content_type=board_content_type, action_object_object_id=board_id_text, ) ) if task_ids: board_filter |= Q( target_content_type=task_content_type, target_object_id__in=task_ids, ) | Q( action_object_content_type=task_content_type, action_object_object_id__in=task_ids, ) return ( Notification.objects.filter( recipient_id=user_id, unread=True, deleted=False, verb__startswith="task_", ) .filter(board_filter) .distinct() )