diff --git a/apiserver/plane/api/serializers/notification.py b/apiserver/plane/api/serializers/notification.py index 529cb9f9c..56dcc0dd8 100644 --- a/apiserver/plane/api/serializers/notification.py +++ b/apiserver/plane/api/serializers/notification.py @@ -1,9 +1,12 @@ # Module imports from .base import BaseSerializer +from .user import UserLiteSerializer from plane.db.models import Notification class NotificationSerializer(BaseSerializer): + triggered_by_details = UserLiteSerializer(read_only=True, source="triggered_by") + class Meta: model = Notification fields = "__all__" diff --git a/apiserver/plane/api/urls.py b/apiserver/plane/api/urls.py index dc5b0e1dc..04bbc2a47 100644 --- a/apiserver/plane/api/urls.py +++ b/apiserver/plane/api/urls.py @@ -153,6 +153,7 @@ from plane.api.views import ( ## End Analytics # Notification NotificationViewSet, + UnreadNotificationEndpoint, ## End Notification ) @@ -1382,5 +1383,10 @@ urlpatterns = [ ), name="notifications", ), + path( + "workspaces//users/notifications/unread/", + UnreadNotificationEndpoint.as_view(), + name="unread-notifications", + ), ## End Notification ] diff --git a/apiserver/plane/api/views/__init__.py b/apiserver/plane/api/views/__init__.py index 2f0a54c1d..076cdd006 100644 --- a/apiserver/plane/api/views/__init__.py +++ b/apiserver/plane/api/views/__init__.py @@ -145,4 +145,4 @@ from .analytic import ( DefaultAnalyticsEndpoint, ) -from .notification import NotificationViewSet \ No newline at end of file +from .notification import NotificationViewSet, UnreadNotificationEndpoint \ No newline at end of file diff --git a/apiserver/plane/api/views/notification.py b/apiserver/plane/api/views/notification.py index ac0082430..802cbb03f 100644 --- a/apiserver/plane/api/views/notification.py +++ b/apiserver/plane/api/views/notification.py @@ -8,7 +8,7 @@ from rest_framework.response import Response from sentry_sdk import capture_exception # Module imports -from .base import BaseViewSet +from .base import BaseViewSet, BaseAPIView from plane.db.models import Notification, IssueAssignee, IssueSubscriber, Issue from plane.api.serializers import NotificationSerializer @@ -25,7 +25,7 @@ class NotificationViewSet(BaseViewSet): workspace__slug=self.kwargs.get("slug"), receiver_id=self.request.user.id, ) - .select_related("workspace") + .select_related("workspace", "project," "triggered_by", "receiver") ) def list(self, request, slug): @@ -123,7 +123,7 @@ class NotificationViewSet(BaseViewSet): {"error": "Something went wrong please try again later"}, status=status.HTTP_400_BAD_REQUEST, ) - + def mark_read(self, request, slug, pk): try: notification = Notification.objects.get( @@ -166,7 +166,6 @@ class NotificationViewSet(BaseViewSet): status=status.HTTP_400_BAD_REQUEST, ) - def archive(self, request, slug, pk): try: notification = Notification.objects.get( @@ -209,3 +208,48 @@ class NotificationViewSet(BaseViewSet): status=status.HTTP_400_BAD_REQUEST, ) + +class UnreadNotificationEndpoint(BaseAPIView): + def get(self, request, slug): + try: + # Watching Issues Count + watching_notification_count = Notification.objects.filter( + workspace__slug=slug, + receiver_id=request.user.id, + entity_identifier__in=IssueSubscriber.objects.filter( + workspace__slug=slug, subscriber_id=request.user.id + ).values_list("issue_id", flat=True), + ).count() + + # My Issues Count + my_issues_count = Notification.objects.filter( + workspace__slug=slug, + receiver_id=request.user.id, + entity_identifier__in=IssueAssignee.objects.filter( + workspace__slug=slug, assignee_id=request.user.id + ).values_list("issue_id", flat=True), + ).count() + + # Created Issues Count + created_issues_count = Notification.objects.filter( + workspace__slug=slug, + receiver_id=request.user.id, + entity_identifier__in=Issue.objects.filter( + workspace__slug=slug, created_by=request.user + ).values_list("pk", flat=True), + ).count() + + return Response( + { + "watching_notifications": watching_notification_count, + "my_issues": my_issues_count, + "created_issues": created_issues_count, + }, + 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, + ) diff --git a/apiserver/plane/bgtasks/issue_activites_task.py b/apiserver/plane/bgtasks/issue_activites_task.py index 28b25b00b..abbbb5f5f 100644 --- a/apiserver/plane/bgtasks/issue_activites_task.py +++ b/apiserver/plane/bgtasks/issue_activites_task.py @@ -1109,7 +1109,8 @@ def issue_activity( issue_subscribers = issue_subscribers + issue_assignees - if issue.created_by_id: + # Add bot filtering + if issue.created_by_id is not None and not issue.created_by.is_bot: issue_subscribers = issue_subscribers + [issue.created_by_id] issue = Issue.objects.get(project=project, pk=issue_id) @@ -1134,7 +1135,17 @@ def issue_activity( "state_name": issue.state.name, "state_group": issue.state.group, }, - "issue_activity": str(issue_activity.id), + "issue_activity": { + "id": str(issue_activity.id), + "verb": str(issue_activity.verb), + "field": str(issue_activity.field), + "actor": str(issue_activity.actor_id), + "new_value": str(issue_activity.new_value), + "old_value": str(issue_activity.old_value), + "issue_comment": str( + issue_activity.issue_comment.comment_stripped if issue_activity.issue_comment is not None else "" + ), + }, }, ) )