pontoon/messaging/management/commands/send_suggestion_notifications.py (88 lines of code) (raw):
import calendar
from collections import defaultdict
from datetime import timedelta
from functools import cached_property
from notifications.signals import notify
from django.conf import settings
from django.contrib.auth.models import User
from django.core.management.base import BaseCommand, CommandError
from django.db.models import Prefetch, Q
from django.template.loader import render_to_string
from django.utils import timezone
from pontoon.base.models import Comment, Locale, ProjectLocale, Translation
class Command(BaseCommand):
help = "Notify contributors about newly added unreviewed suggestions"
def add_arguments(self, parser):
parser.add_argument(
"--force",
action="store_true",
dest="force",
default=False,
help="Force run command, regardless of what day of the week it is",
)
@cached_property
def locale_reviewers(self):
locales = Locale.objects.prefetch_related(
Prefetch("managers_group__user_set", to_attr="fetched_managers"),
Prefetch("translators_group__user_set", to_attr="fetched_translators"),
)
locale_reviewers = {}
for locale in locales:
managers = locale.managers_group.fetched_managers
translators = locale.translators_group.fetched_translators
locale_reviewers[locale] = managers + translators
return locale_reviewers
def extract_notifications_data(self, data, suggestion):
locale = suggestion.locale
entity = suggestion.entity
project = entity.resource.project
project_locale = ProjectLocale.objects.get(project=project, locale=locale)
translations = Translation.objects.filter(entity=entity, locale=locale)
recipients = set()
# Users with permission to review suggestions
recipients = recipients.union(self.locale_reviewers[locale])
# Authors of previous translations of the same string
recipients = recipients.union(User.objects.filter(translation__in=translations))
# Authors of comments of previous translations
translations_comments = Comment.objects.filter(translation__in=translations)
recipients = recipients.union(
User.objects.filter(comment__in=translations_comments)
)
# Authors of team comments of the same string
team_comments = Comment.objects.filter(entity=entity, locale=locale)
recipients = recipients.union(User.objects.filter(comment__in=team_comments))
for recipient in recipients:
data[recipient].add(project_locale)
def get_suggestions(self):
start = timezone.now() - timedelta(days=7)
return Translation.objects.filter(
approved=False, pretranslated=False, rejected=False, fuzzy=False
).filter(
Q(date__gt=start)
| Q(unapproved_date__gt=start)
| Q(unrejected_date__gt=start)
)
def handle(self, *args, **options):
"""
This command sends notifications about newly created unreviewed suggestions that
were submitted, unapproved or unrejected in the last 7 days. Recipients of
notifications are users with permission to review them, as well as authors of
previous translations or comments of the same string.
The command is designed to run on a weekly basis.
"""
self.stdout.write("Sending suggestion notifications.")
today = calendar.day_name[timezone.datetime.today().weekday()]
day = calendar.day_name[settings.SUGGESTION_NOTIFICATIONS_DAY]
if today != day and not options["force"]:
raise CommandError(f"Skipping. Command runs every {day}. Today is {today}.")
suggestions = self.get_suggestions()
data = defaultdict(set)
for suggestion in suggestions:
self.extract_notifications_data(data, suggestion)
pks = [user.pk for user in data.keys()]
recipients = User.objects.filter(
pk__in=pks, profile__unreviewed_suggestion_notifications=True
)
for recipient in recipients:
project_locales = data[recipient]
description = render_to_string(
"messaging/notifications/suggestions_submitted.html",
{"project_locales": project_locales},
)
notify.send(
recipient,
recipient=recipient,
verb="",
description=description,
category="unreviewed_suggestion",
)
self.stdout.write(f"Suggestion notifications sent to {len(recipients)} users.")