Skip to content

Commit

Permalink
chore: added files
Browse files Browse the repository at this point in the history
  • Loading branch information
EwoutV committed May 23, 2024
1 parent 2fed6c2 commit 7b9b296
Show file tree
Hide file tree
Showing 28 changed files with 23,782 additions and 131 deletions.
19 changes: 10 additions & 9 deletions backend/api/views/user_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,22 +79,23 @@ def search(self, request: Request) -> Response:
@action(detail=True, methods=["get"], permission_classes=[NotificationPermission])
def notifications(self, request: Request, pk: str):
"""Returns a list of notifications for the given user"""
notifications = Notification.objects.filter(user=pk)
count = min(
int(request.query_params.get("count", 10)), 30
)

# Get the notifications for the user
notifications = Notification.objects.filter(user=pk).order_by("-created_at")[:count]

# Serialize the notifications
serializer = NotificationSerializer(
notifications, many=True, context={"request": request}
)

return Response(serializer.data)

@action(
detail=True,
methods=["post"],
permission_classes=[NotificationPermission],
url_path="notifications/read",
)
def read(self, _: Request, pk: str):
@notifications.mapping.patch
def _read_notifications(self, _: Request, pk: str):
"""Marks all notifications as read for the given user"""
notifications = Notification.objects.filter(user=pk)
notifications.update(is_read=True)

return Response(status=HTTP_200_OK)
4 changes: 2 additions & 2 deletions backend/notifications/locale/en/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ msgstr "Your score has been updated.\nNew score: %(score)s"
msgid "Title: Docker image build success"
msgstr "Docker image successfully build"
msgid "Description: Docker image build success %(name)s"
msgstr "Your docker image, $(name)s, has successfully been build"
msgstr "Your docker image, %(name)s, has successfully been build"
# Docker Image Build Error
msgid "Title: Docker image build error"
msgstr "Docker image failed to build"
Expand All @@ -44,7 +44,7 @@ msgstr "Failed to build your docker image, %(name)s"
msgid "Title: Extra check success"
msgstr "Passed an extra check"
msgid "Description: Extra check success %(name)s"
msgstr "Your submission passed the extra check, $(name)s"
msgstr "Your submission passed the extra check, %(name)s"
# Extra Check Error
msgid "Title: Extra check error"
msgstr "Failed an extra check"
Expand Down
4 changes: 2 additions & 2 deletions backend/notifications/locale/nl/LC_MESSAGES/django.po
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ msgstr "Je score is geupdate.\nNieuwe score: %(score)s"
msgid "Title: Docker image build success"
msgstr "Docker image succesvol gebouwd"
msgid "Description: Docker image build success %(name)s"
msgstr "Jouw docker image, $(name)s, is succesvol gebouwd"
msgstr "Jouw docker image, %(name)s, is succesvol gebouwd"
# Docker Image Build Error
msgid "Title: Docker image build error"
msgstr "Docker image is gefaald om te bouwen"
Expand All @@ -44,7 +44,7 @@ msgstr "Gefaald om jouw docker image, %(name)s, te bouwen"
msgid "Title: Extra check success"
msgstr "Geslaagd voor een extra check"
msgid "Description: Extra check success %(name)s"
msgstr "Jouw indiening is geslaagd voor de extra check: $(name)s"
msgstr "Jouw indiening is geslaagd voor de extra check: %(name)s"
# Extra Check Error
msgid "Title: Extra check error"
msgstr "Gefaald voor een extra check"
Expand Down
15 changes: 9 additions & 6 deletions backend/notifications/logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,26 @@
from ypovoli.settings import EMAIL_CUSTOM


# Returns a dictionary with the title and description of the notification
def get_message_dict(notification: Notification) -> Dict[str, str]:
"""Get the message from the template and arguments."""
return {
"title": _(notification.template_id.title_key),
"description": _(notification.template_id.description_key)
% notification.arguments,
}


# Call the function after 60 seconds and no more than once in that period
def schedule_send_mails():
"""Schedule the sending of emails."""
if not cache.get("notifications_send_mails", False):
cache.set("notifications_send_mails", True)
_send_mails.apply_async(countdown=60)


# Try to send one email and set the result
def _send_mail(mail: mail.EmailMessage, result: List[bool]):
def _send_mail(message: mail.EmailMessage, result: List[bool]):
"""Try to send one email and set the result."""
try:
mail.send(fail_silently=False)
message.send(fail_silently=False)
result[0] = True
except SMTPException:
result[0] = False
Expand All @@ -40,11 +40,13 @@ def _send_mail(mail: mail.EmailMessage, result: List[bool]):
# TODO: Move to tasks module
# TODO: Retry 3
# https://docs.celeryq.dev/en/v5.3.6/getting-started/next-steps.html#next-steps
# Send all unsent emails
@shared_task()
def _send_mails():
"""Send all unsent emails."""

# All notifications that need to be sent
notifications = Notification.objects.filter(is_sent=False)

# Dictionary with the number of errors for each email
errors: DefaultDict[str, int] = cache.get(
"notifications_send_mails_errors", defaultdict(int)
Expand Down Expand Up @@ -105,5 +107,6 @@ def _send_mails():
# Restart the process if there are any notifications left that were not sent
unsent_notifications = Notification.objects.filter(is_sent=False)
cache.set("notifications_send_mails", False)

if unsent_notifications.count() > 0:
schedule_send_mails()
55 changes: 11 additions & 44 deletions backend/notifications/serializers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import re
from django.utils.translation import gettext as _

from authentication.models import User
from notifications.logic import get_message_dict
from notifications.models import Notification, NotificationTemplate
from rest_framework import serializers

Expand All @@ -19,56 +18,24 @@ class NotificationSerializer(serializers.ModelSerializer):
queryset=User.objects.all()
)

# Translate template and arguments into a message
message = serializers.SerializerMethodField()
title = serializers.SerializerMethodField()
content = serializers.SerializerMethodField()

def _get_missing_keys(self, string: str, arguments: dict[str, str]) -> list[str]:
"""Get the missing keys from the arguments for a template string."""
required_keys: list[str] = re.findall(r"%\((\w+)\)", string)
missing_keys = [key for key in required_keys if key not in arguments]
def get_content(self, notification: Notification) -> str:
"""Get the content from the template and arguments."""
return _(notification.template_id.description_key) % notification.arguments

return missing_keys

def validate(self, data: dict[str, str | int | dict[str, str]]) -> dict[str, str]:
"""Validate the notification data."""
data: dict[str, any] = super().validate(data)

# Validate the arguments
if "arguments" not in data:
data["arguments"] = dict()

title_missing = self._get_missing_keys(
data["template_id"].title_key, data["arguments"]
)

description_missing = self._get_missing_keys(
data["template_id"].description_key, data["arguments"]
)

if title_missing or description_missing:
raise serializers.ValidationError(
{
"missing arguments": {
"title": title_missing,
"description": description_missing,
}
}
)

return data

def get_message(self, obj: Notification) -> dict[str, str]:
"""Get the message from the template and arguments."""
return get_message_dict(obj)
def get_title(self, notification: Notification) -> str:
"""Get the title from the template and arguments."""
return _(notification.template_id.title_key)

class Meta:
model = Notification
fields = [
"id",
"user",
"template_id",
"arguments",
"message",
"content",
"title",
"created_at",
"is_read",
"is_sent",
Expand Down
Loading

0 comments on commit 7b9b296

Please sign in to comment.