From c8b0ecf76580d0b094ee8785bc5773dd1ba5bed5 Mon Sep 17 00:00:00 2001 From: NarayanBavisetti Date: Thu, 24 Aug 2023 13:18:07 +0530 Subject: [PATCH 1/3] feat: mark all read notifications --- apiserver/plane/api/urls.py | 10 ++++ apiserver/plane/api/views/__init__.py | 2 +- apiserver/plane/api/views/notification.py | 73 +++++++++++++++++++++++ 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/apiserver/plane/api/urls.py b/apiserver/plane/api/urls.py index a6beac6931d..0fbb97966c9 100644 --- a/apiserver/plane/api/urls.py +++ b/apiserver/plane/api/urls.py @@ -164,6 +164,7 @@ # Notification NotificationViewSet, UnreadNotificationEndpoint, + MarkAllReadNotificationViewSet, ## End Notification # Public Boards ProjectDeployBoardViewSet, @@ -1494,6 +1495,15 @@ UnreadNotificationEndpoint.as_view(), name="unread-notifications", ), + path( + "workspaces//users/notifications/mark-all-read/", + MarkAllReadNotificationViewSet.as_view( + { + "post": "create", + } + ), + name="mark-all-read-notifications", + ), ## End Notification # Public Boards path( diff --git a/apiserver/plane/api/views/__init__.py b/apiserver/plane/api/views/__init__.py index 9572c552f8a..47e7c6f8574 100644 --- a/apiserver/plane/api/views/__init__.py +++ b/apiserver/plane/api/views/__init__.py @@ -162,7 +162,7 @@ DefaultAnalyticsEndpoint, ) -from .notification import NotificationViewSet, UnreadNotificationEndpoint +from .notification import NotificationViewSet, UnreadNotificationEndpoint, MarkAllReadNotificationViewSet from .exporter import ( ExportIssuesEndpoint, diff --git a/apiserver/plane/api/views/notification.py b/apiserver/plane/api/views/notification.py index 2abc82631de..f59bc37c038 100644 --- a/apiserver/plane/api/views/notification.py +++ b/apiserver/plane/api/views/notification.py @@ -274,3 +274,76 @@ def get(self, request, slug): {"error": "Something went wrong please try again later"}, status=status.HTTP_400_BAD_REQUEST, ) + +class MarkAllReadNotificationViewSet(BaseViewSet): + def create(self, request, slug): + try: + snoozed = request.data.get("snoozed", "false") + archived = request.data.get("archived", "false") + type = request.data.get("type", "all") + + notifications = ( + Notification.objects.filter( + workspace__slug=slug, receiver_id=request.user.id, read_at__isnull=True + ) + .select_related("workspace", "project", "triggered_by", "receiver") + .order_by("snoozed_till", "-created_at") + ) + + # Filter for snoozed notifications + if snoozed == "false": + notifications = notifications.filter( + Q(snoozed_till__gte=timezone.now()) | Q(snoozed_till__isnull=True), + ) + + if snoozed == "true": + notifications = notifications.filter( + Q(snoozed_till__lt=timezone.now()) | Q(snoozed_till__isnull=False) + ) + + # Filter for archived or unarchive + if archived == "false": + notifications = notifications.filter(archived_at__isnull=True) + + if archived == "true": + notifications = notifications.filter(archived_at__isnull=False) + + # Subscribed issues + if type == "watching": + issue_ids = IssueSubscriber.objects.filter( + workspace__slug=slug, subscriber_id=request.user.id + ).values_list("issue_id", flat=True) + notifications = notifications.filter(entity_identifier__in=issue_ids) + + # Assigned Issues + if type == "assigned": + issue_ids = IssueAssignee.objects.filter( + workspace__slug=slug, assignee_id=request.user.id + ).values_list("issue_id", flat=True) + notifications = notifications.filter(entity_identifier__in=issue_ids) + + # Created issues + if type == "created": + if WorkspaceMember.objects.filter(workspace__slug=slug, member=request.user, role__lt=15).exists(): + notifications = Notification.objects.none() + else: + issue_ids = Issue.objects.filter( + workspace__slug=slug, created_by=request.user + ).values_list("pk", flat=True) + notifications = notifications.filter(entity_identifier__in=issue_ids) + + + updated_notifications = [] + for notification in notifications: + notification.read_at = timezone.now() + updated_notifications.append(notification) + Notification.objects.bulk_update( + updated_notifications, ["read_at"], batch_size=100 + ) + return Response({"message": "successful"}, status=status.HTTP_200_OK) + except Exception as e: + capture_exception(e) + return Response( + {"error": "Something went wrong please try again later"}, + status=status.HTTP_400_BAD_REQUEST, + ) \ No newline at end of file From f848645b78470088d303c44cec328dd8142a1524 Mon Sep 17 00:00:00 2001 From: NarayanBavisetti Date: Fri, 25 Aug 2023 17:14:16 +0530 Subject: [PATCH 2/3] fix: changed string to boolean --- apiserver/plane/api/views/notification.py | 54 ++++++++++++++--------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/apiserver/plane/api/views/notification.py b/apiserver/plane/api/views/notification.py index f59bc37c038..ec44f3ec460 100644 --- a/apiserver/plane/api/views/notification.py +++ b/apiserver/plane/api/views/notification.py @@ -10,7 +10,13 @@ # Module imports from .base import BaseViewSet, BaseAPIView -from plane.db.models import Notification, IssueAssignee, IssueSubscriber, Issue, WorkspaceMember +from plane.db.models import ( + Notification, + IssueAssignee, + IssueSubscriber, + Issue, + WorkspaceMember, +) from plane.api.serializers import NotificationSerializer @@ -83,13 +89,17 @@ def list(self, request, slug): # Created issues if type == "created": - if WorkspaceMember.objects.filter(workspace__slug=slug, member=request.user, role__lt=15).exists(): + if WorkspaceMember.objects.filter( + workspace__slug=slug, member=request.user, role__lt=15 + ).exists(): notifications = Notification.objects.none() else: issue_ids = Issue.objects.filter( workspace__slug=slug, created_by=request.user ).values_list("pk", flat=True) - notifications = notifications.filter(entity_identifier__in=issue_ids) + notifications = notifications.filter( + entity_identifier__in=issue_ids + ) # Pagination if request.GET.get("per_page", False) and request.GET.get("cursor", False): @@ -275,38 +285,39 @@ def get(self, request, slug): status=status.HTTP_400_BAD_REQUEST, ) + class MarkAllReadNotificationViewSet(BaseViewSet): def create(self, request, slug): try: - snoozed = request.data.get("snoozed", "false") - archived = request.data.get("archived", "false") + snoozed = request.data.get("snoozed", False) + archived = request.data.get("archived", False) type = request.data.get("type", "all") notifications = ( Notification.objects.filter( - workspace__slug=slug, receiver_id=request.user.id, read_at__isnull=True + workspace__slug=slug, + receiver_id=request.user.id, + read_at__isnull=True, ) .select_related("workspace", "project", "triggered_by", "receiver") .order_by("snoozed_till", "-created_at") ) # Filter for snoozed notifications - if snoozed == "false": + if snoozed: notifications = notifications.filter( Q(snoozed_till__gte=timezone.now()) | Q(snoozed_till__isnull=True), ) - - if snoozed == "true": + else: notifications = notifications.filter( Q(snoozed_till__lt=timezone.now()) | Q(snoozed_till__isnull=False) ) # Filter for archived or unarchive - if archived == "false": - notifications = notifications.filter(archived_at__isnull=True) - - if archived == "true": + if archived: notifications = notifications.filter(archived_at__isnull=False) + else: + notifications = notifications.filter(archived_at__isnull=True) # Subscribed issues if type == "watching": @@ -324,26 +335,29 @@ def create(self, request, slug): # Created issues if type == "created": - if WorkspaceMember.objects.filter(workspace__slug=slug, member=request.user, role__lt=15).exists(): + if WorkspaceMember.objects.filter( + workspace__slug=slug, member=request.user, role__lt=15 + ).exists(): notifications = Notification.objects.none() else: issue_ids = Issue.objects.filter( workspace__slug=slug, created_by=request.user ).values_list("pk", flat=True) - notifications = notifications.filter(entity_identifier__in=issue_ids) + notifications = notifications.filter( + entity_identifier__in=issue_ids + ) - - updated_notifications = [] + updated_notifications = [] for notification in notifications: notification.read_at = timezone.now() updated_notifications.append(notification) Notification.objects.bulk_update( updated_notifications, ["read_at"], batch_size=100 - ) - return Response({"message": "successful"}, status=status.HTTP_200_OK) + ) + return Response({"message": "Successful"}, status=status.HTTP_200_OK) except Exception as e: capture_exception(e) return Response( {"error": "Something went wrong please try again later"}, status=status.HTTP_400_BAD_REQUEST, - ) \ No newline at end of file + ) From 1cf5e14eb6fb7318003ae30a74b8e52b32112d8d Mon Sep 17 00:00:00 2001 From: NarayanBavisetti Date: Mon, 28 Aug 2023 15:36:23 +0530 Subject: [PATCH 3/3] fix: changed snoozed condition --- apiserver/plane/api/views/notification.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apiserver/plane/api/views/notification.py b/apiserver/plane/api/views/notification.py index ec44f3ec460..75b94f034bd 100644 --- a/apiserver/plane/api/views/notification.py +++ b/apiserver/plane/api/views/notification.py @@ -306,11 +306,11 @@ def create(self, request, slug): # Filter for snoozed notifications if snoozed: notifications = notifications.filter( - Q(snoozed_till__gte=timezone.now()) | Q(snoozed_till__isnull=True), + Q(snoozed_till__lt=timezone.now()) | Q(snoozed_till__isnull=False) ) else: notifications = notifications.filter( - Q(snoozed_till__lt=timezone.now()) | Q(snoozed_till__isnull=False) + Q(snoozed_till__gte=timezone.now()) | Q(snoozed_till__isnull=True), ) # Filter for archived or unarchive