Skip to content

Commit

Permalink
dev: email notifications (#3421)
Browse files Browse the repository at this point in the history
* dev: create email notification preference model

* dev: intiate models

* dev: user notification preferences

* dev: create notification logs for the user.

* dev: email notification stacking and sending logic

* feat: email notification preference settings page.

* dev: delete subscribers

* dev: issue update ui implementation in email notification

* chore: integrate email notification endpoint.

* chore: remove toggle switch.

* chore: added labels part

* fix: refactored base design with tables

* dev: email notification templates

* dev: template updates

* dev: update models

* dev: update template for labels and new migrations

* fix: profile settings preference sidebar.

* dev: update preference endpoints

* dev: update the schedule to 5 minutes

* dev: update template with priority data

* dev: update templates

* chore: enable `issue subscribe` button for all users.

* chore: notification handling for external api

* dev: update origin request

---------

Co-authored-by: Prateek Shourya <prateekshourya29@gmail.com>
Co-authored-by: LAKHAN BAHETI <lakhanbaheti9@gmail.com>
Co-authored-by: Ramesh Kumar Chandra <rameshkumar2299@gmail.com>
Co-authored-by: NarayanBavisetti <narayan3119@gmail.com>
  • Loading branch information
5 people committed Jan 23, 2024
1 parent c1e1b81 commit f27efb8
Show file tree
Hide file tree
Showing 35 changed files with 2,479 additions and 311 deletions.
2 changes: 1 addition & 1 deletion apiserver/plane/app/serializers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@

from .analytic import AnalyticViewSerializer

from .notification import NotificationSerializer
from .notification import NotificationSerializer, UserNotificationPreferenceSerializer

from .exporter import ExporterHistorySerializer

Expand Down
9 changes: 8 additions & 1 deletion apiserver/plane/app/serializers/notification.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Module imports
from .base import BaseSerializer
from .user import UserLiteSerializer
from plane.db.models import Notification
from plane.db.models import Notification, UserNotificationPreference


class NotificationSerializer(BaseSerializer):
Expand All @@ -12,3 +12,10 @@ class NotificationSerializer(BaseSerializer):
class Meta:
model = Notification
fields = "__all__"


class UserNotificationPreferenceSerializer(BaseSerializer):

class Meta:
model = UserNotificationPreference
fields = "__all__"
6 changes: 6 additions & 0 deletions apiserver/plane/app/urls/notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
NotificationViewSet,
UnreadNotificationEndpoint,
MarkAllReadNotificationViewSet,
UserNotificationPreferenceEndpoint,
)


Expand Down Expand Up @@ -63,4 +64,9 @@
),
name="mark-all-read-notifications",
),
path(
"users/me/notification-preferences/",
UserNotificationPreferenceEndpoint.as_view(),
name="user-notification-preferences",
),
]
1 change: 1 addition & 0 deletions apiserver/plane/app/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@
NotificationViewSet,
UnreadNotificationEndpoint,
MarkAllReadNotificationViewSet,
UserNotificationPreferenceEndpoint,
)

from .exporter import ExportIssuesEndpoint
Expand Down
6 changes: 6 additions & 0 deletions apiserver/plane/app/views/cycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,8 @@ def destroy(self, request, slug, project_id, pk):
project_id=str(project_id),
current_instance=None,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
# Delete the cycle
cycle.delete()
Expand Down Expand Up @@ -721,6 +723,8 @@ def create(self, request, slug, project_id, cycle_id):
}
),
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)

# Return all Cycle Issues
Expand Down Expand Up @@ -753,6 +757,8 @@ def destroy(self, request, slug, project_id, cycle_id, issue_id):
project_id=str(self.kwargs.get("project_id", None)),
current_instance=None,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
cycle_issue.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
Expand Down
4 changes: 4 additions & 0 deletions apiserver/plane/app/views/inbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,8 @@ def create(self, request, slug, project_id, inbox_id):
project_id=str(project_id),
current_instance=None,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
# create an inbox issue
InboxIssue.objects.create(
Expand Down Expand Up @@ -277,6 +279,8 @@ def partial_update(self, request, slug, project_id, inbox_id, issue_id):
cls=DjangoJSONEncoder,
),
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
issue_serializer.save()
else:
Expand Down
46 changes: 45 additions & 1 deletion apiserver/plane/app/views/issue.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ def create(self, request, slug, project_id):
project_id=str(project_id),
current_instance=None,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
issue = (
self.get_queryset().filter(pk=serializer.data["id"]).first()
Expand Down Expand Up @@ -297,6 +299,8 @@ def partial_update(self, request, slug, project_id, pk=None):
project_id=str(project_id),
current_instance=current_instance,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
issue = self.get_queryset().filter(pk=pk).first()
return Response(
Expand All @@ -320,6 +324,8 @@ def destroy(self, request, slug, project_id, pk=None):
project_id=str(project_id),
current_instance=current_instance,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
return Response(status=status.HTTP_204_NO_CONTENT)

Expand Down Expand Up @@ -528,7 +534,7 @@ def get(self, request, slug, project_id, issue_id):

if request.GET.get("activity_type", None) == "issue-property":
return Response(issue_activities, status=status.HTTP_200_OK)

if request.GET.get("activity_type", None) == "issue-comment":
return Response(issue_comments, status=status.HTTP_200_OK)

Expand Down Expand Up @@ -595,6 +601,8 @@ def create(self, request, slug, project_id, issue_id):
project_id=str(self.kwargs.get("project_id")),
current_instance=None,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Expand Down Expand Up @@ -624,6 +632,8 @@ def partial_update(self, request, slug, project_id, issue_id, pk):
project_id=str(project_id),
current_instance=current_instance,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Expand All @@ -648,6 +658,8 @@ def destroy(self, request, slug, project_id, issue_id, pk):
project_id=str(project_id),
current_instance=current_instance,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
return Response(status=status.HTTP_204_NO_CONTENT)

Expand Down Expand Up @@ -850,6 +862,8 @@ def post(self, request, slug, project_id, issue_id):
project_id=str(project_id),
current_instance=json.dumps({"parent": str(sub_issue_id)}),
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
for sub_issue_id in sub_issue_ids
]
Expand Down Expand Up @@ -909,6 +923,8 @@ def create(self, request, slug, project_id, issue_id):
project_id=str(self.kwargs.get("project_id")),
current_instance=None,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Expand Down Expand Up @@ -938,6 +954,8 @@ def partial_update(self, request, slug, project_id, issue_id, pk):
project_id=str(project_id),
current_instance=current_instance,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Expand All @@ -961,6 +979,8 @@ def destroy(self, request, slug, project_id, issue_id, pk):
project_id=str(project_id),
current_instance=current_instance,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
issue_link.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
Expand Down Expand Up @@ -1017,6 +1037,8 @@ def post(self, request, slug, project_id, issue_id):
cls=DjangoJSONEncoder,
),
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Expand All @@ -1033,6 +1055,8 @@ def delete(self, request, slug, project_id, issue_id, pk):
project_id=str(self.kwargs.get("project_id", None)),
current_instance=None,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)

return Response(status=status.HTTP_204_NO_CONTENT)
Expand Down Expand Up @@ -1211,6 +1235,8 @@ def unarchive(self, request, slug, project_id, pk=None):
IssueSerializer(issue).data, cls=DjangoJSONEncoder
),
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
issue.archived_at = None
issue.save()
Expand Down Expand Up @@ -1356,6 +1382,8 @@ def create(self, request, slug, project_id, issue_id):
project_id=str(project_id),
current_instance=None,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Expand All @@ -1381,6 +1409,8 @@ def destroy(self, request, slug, project_id, issue_id, reaction_code):
}
),
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
issue_reaction.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
Expand Down Expand Up @@ -1421,6 +1451,8 @@ def create(self, request, slug, project_id, comment_id):
project_id=str(project_id),
current_instance=None,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Expand All @@ -1447,6 +1479,8 @@ def destroy(self, request, slug, project_id, comment_id, reaction_code):
}
),
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
comment_reaction.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
Expand Down Expand Up @@ -1575,6 +1609,8 @@ def create(self, request, slug, project_id, issue_id):
project_id=str(project_id),
current_instance=None,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)

if relation_type == "blocking":
Expand Down Expand Up @@ -1619,6 +1655,8 @@ def remove_relation(self, request, slug, project_id, issue_id):
project_id=str(project_id),
current_instance=current_instance,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
return Response(status=status.HTTP_204_NO_CONTENT)

Expand Down Expand Up @@ -1790,6 +1828,8 @@ def create(self, request, slug, project_id):
project_id=str(project_id),
current_instance=None,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Expand Down Expand Up @@ -1820,6 +1860,8 @@ def partial_update(self, request, slug, project_id, pk):
cls=DjangoJSONEncoder,
),
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Expand All @@ -1846,5 +1888,7 @@ def destroy(self, request, slug, project_id, pk=None):
project_id=str(project_id),
current_instance=current_instance,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
return Response(status=status.HTTP_204_NO_CONTENT)
6 changes: 6 additions & 0 deletions apiserver/plane/app/views/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,8 @@ def destroy(self, request, slug, project_id, pk):
project_id=str(project_id),
current_instance=None,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
module.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
Expand Down Expand Up @@ -488,6 +490,8 @@ def create(self, request, slug, project_id, module_id):
}
),
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)

issues = self.get_queryset().values_list("issue_id", flat=True)
Expand Down Expand Up @@ -519,6 +523,8 @@ def destroy(self, request, slug, project_id, module_id, issue_id):
project_id=str(project_id),
current_instance=None,
epoch=int(timezone.now().timestamp()),
notification=True,
origin=request.META.get("HTTP_ORIGIN"),
)
module_issue.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
Expand Down

0 comments on commit f27efb8

Please sign in to comment.