From 6273ecb07a2523030ad9c898b261ba713979005e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matja=C5=BE=20Horvat?= Date: Tue, 3 Dec 2024 21:55:15 +0100 Subject: [PATCH] Use full URLs in notifications (#3468) Some notification types (suggestions available for review, suggestions have been reviewed and new contributor has joined the team) use relative URLs. That means they won't work in emails. This patch fixes that. It also: * Moves notification content from multiline strings to HTML templates. * Moves management commands for sending notifications to the messaging module. * Moves notification template files to the messaging module. --- .../commands/send_deadline_notifications.py | 0 .../commands/send_review_notifications.py | 68 +++++++++---------- .../commands/send_suggestion_notifications.py | 2 +- .../notifications/new_contributor.html | 7 ++ .../notifications/suggestions_reviewed.html | 11 +++ .../notifications/suggestions_submitted.html | 11 +++ pontoon/projects/management/__init__.py | 0 .../projects/management/commands/__init__.py | 0 .../projects/suggestion_notification.jinja | 6 -- pontoon/translations/views.py | 42 +++--------- 10 files changed, 73 insertions(+), 74 deletions(-) rename pontoon/{projects => messaging}/management/commands/send_deadline_notifications.py (100%) rename pontoon/{projects => messaging}/management/commands/send_review_notifications.py (59%) rename pontoon/{projects => messaging}/management/commands/send_suggestion_notifications.py (98%) create mode 100644 pontoon/messaging/templates/messaging/notifications/new_contributor.html create mode 100644 pontoon/messaging/templates/messaging/notifications/suggestions_reviewed.html create mode 100644 pontoon/messaging/templates/messaging/notifications/suggestions_submitted.html delete mode 100644 pontoon/projects/management/__init__.py delete mode 100644 pontoon/projects/management/commands/__init__.py delete mode 100644 pontoon/projects/templates/projects/suggestion_notification.jinja diff --git a/pontoon/projects/management/commands/send_deadline_notifications.py b/pontoon/messaging/management/commands/send_deadline_notifications.py similarity index 100% rename from pontoon/projects/management/commands/send_deadline_notifications.py rename to pontoon/messaging/management/commands/send_deadline_notifications.py diff --git a/pontoon/projects/management/commands/send_review_notifications.py b/pontoon/messaging/management/commands/send_review_notifications.py similarity index 59% rename from pontoon/projects/management/commands/send_review_notifications.py rename to pontoon/messaging/management/commands/send_review_notifications.py index 5b417045ec..22103ea277 100644 --- a/pontoon/projects/management/commands/send_review_notifications.py +++ b/pontoon/messaging/management/commands/send_review_notifications.py @@ -5,7 +5,7 @@ from django.core.management.base import BaseCommand from django.db.models import Q -from django.urls import reverse +from django.template.loader import render_to_string from django.utils import timezone from pontoon.base.models import Translation @@ -14,37 +14,6 @@ class Command(BaseCommand): help = "Notify translators about their newly reviewed suggestions" - def get_description(self, notifyData): - desc = "Your suggestions have been reviewed:\n" - def handle(self, *args, **options): """ This command sends notifications about newly reviewed @@ -57,6 +26,7 @@ def handle(self, *args, **options): # (author) -> (locale, project) -> (approved, rejected) data = defaultdict(lambda: defaultdict(lambda: (list(), list()))) start = timezone.now() - timedelta(days=1) + for suggestion in Translation.objects.filter( (Q(approved_date__gt=start) | Q(rejected_date__gt=start)) & Q(user__profile__review_notifications=True) @@ -70,13 +40,41 @@ def handle(self, *args, **options): elif suggestion.rejected and suggestion.rejected_user != author: data[author][(locale, project)][1].append(suggestion.entity.pk) - for author, notifyData in data.items(): - desc = self.get_description(notifyData) + for author, notification_data in data.items(): + notifications = [] + + for (locale, project), (approved, rejected) in notification_data.items(): + # Filter out rejections where the author's own suggestion replaced the previous + rejected = [id for id in rejected if id not in approved] + + if len(approved) == 0: + msg = f"{len(rejected)} Rejected" + else: + msg = f"{len(approved)} Approved" + if len(rejected) > 0: + msg += f", {len(rejected)} Rejected" + + notifications.append( + { + "locale": locale, + "project": project, + "ids": ",".join(map(str, approved + rejected)), + "msg": msg, + } + ) + + description = render_to_string( + "messaging/notifications/suggestions_reviewed.html", + { + "notifications": notifications, + }, + ) + notify.send( sender=author, recipient=author, verb="has reviewed suggestions", - description=desc, + description=description, category="review", ) diff --git a/pontoon/projects/management/commands/send_suggestion_notifications.py b/pontoon/messaging/management/commands/send_suggestion_notifications.py similarity index 98% rename from pontoon/projects/management/commands/send_suggestion_notifications.py rename to pontoon/messaging/management/commands/send_suggestion_notifications.py index ae9014edff..a4b8ce29cc 100644 --- a/pontoon/projects/management/commands/send_suggestion_notifications.py +++ b/pontoon/messaging/management/commands/send_suggestion_notifications.py @@ -116,7 +116,7 @@ def handle(self, *args, **options): project_locales = data[recipient] description = render_to_string( - "projects/suggestion_notification.jinja", + "messaging/notifications/suggestions_submitted.html", {"project_locales": project_locales}, ) diff --git a/pontoon/messaging/templates/messaging/notifications/new_contributor.html b/pontoon/messaging/templates/messaging/notifications/new_contributor.html new file mode 100644 index 0000000000..5ce098268d --- /dev/null +++ b/pontoon/messaging/templates/messaging/notifications/new_contributor.html @@ -0,0 +1,7 @@ +{{ user.name_or_email }} has +made their first contribution to {{ locale.name }} ({{ + locale.code }}). + +Please welcome them to the team, and make sure to review + their suggestions. diff --git a/pontoon/messaging/templates/messaging/notifications/suggestions_reviewed.html b/pontoon/messaging/templates/messaging/notifications/suggestions_reviewed.html new file mode 100644 index 0000000000..01f1b95efe --- /dev/null +++ b/pontoon/messaging/templates/messaging/notifications/suggestions_reviewed.html @@ -0,0 +1,11 @@ +Your suggestions have been reviewed: + diff --git a/pontoon/messaging/templates/messaging/notifications/suggestions_submitted.html b/pontoon/messaging/templates/messaging/notifications/suggestions_submitted.html new file mode 100644 index 0000000000..285a7916ba --- /dev/null +++ b/pontoon/messaging/templates/messaging/notifications/suggestions_submitted.html @@ -0,0 +1,11 @@ +Unreviewed suggestions have been submitted for the following projects: + diff --git a/pontoon/projects/management/__init__.py b/pontoon/projects/management/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/pontoon/projects/management/commands/__init__.py b/pontoon/projects/management/commands/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/pontoon/projects/templates/projects/suggestion_notification.jinja b/pontoon/projects/templates/projects/suggestion_notification.jinja deleted file mode 100644 index 7110606a76..0000000000 --- a/pontoon/projects/templates/projects/suggestion_notification.jinja +++ /dev/null @@ -1,6 +0,0 @@ -Unreviewed suggestions have been submitted for the following projects: - diff --git a/pontoon/translations/views.py b/pontoon/translations/views.py index 2cb606cf70..726441cc03 100644 --- a/pontoon/translations/views.py +++ b/pontoon/translations/views.py @@ -5,7 +5,7 @@ from django.db import transaction from django.http import JsonResponse from django.shortcuts import get_object_or_404 -from django.urls import reverse +from django.template.loader import render_to_string from django.utils import timezone from django.utils.datastructures import MultiValueDictKeyError from django.views.decorators.http import require_POST @@ -128,36 +128,14 @@ def create_translation(request): # When user makes their first contribution to the team, notify team managers first_contribution = not project.system_project and user.is_new_contributor(locale) if first_contribution: - desc = """ - {user} has made their first contribution to - {locale} ({locale_code}). - Please welcome them to the team, and make sure to - review their suggestions. - """.format( - user=user.name_or_email, - user_href=reverse( - "pontoon.contributors.contributor.username", - kwargs={ - "username": user.username, - }, - ), - locale=locale.name, - locale_code=locale.code, - locale_href=reverse( - "pontoon.teams.team", - kwargs={ - "locale": locale.code, - }, - ), - review_href=reverse( - "pontoon.translate", - kwargs={ - "locale": locale.code, - "project": project.slug, - "resource": entity.resource.path, - }, - ) - + f"?string={entity.pk}", + description = render_to_string( + "messaging/notifications/new_contributor.html", + { + "entity": entity, + "locale": locale, + "project": project, + "user": user, + }, ) for manager in locale.managers_group.user_set.filter( @@ -167,7 +145,7 @@ def create_translation(request): sender=manager, recipient=manager, verb="has reviewed suggestions", # Triggers render of description only - description=desc, + description=description, category="new_contributor", )