prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  [notifications] Notify project Translators when suggestions are submitted
  *This issue was created automatically by a [script](https://github.com/mathjazz/bugzilla2github/).*
  ## [Bug 1398861](https://bugzilla.mozilla.org/show_bug.cgi?id=1398861)
  Bug Reporter: @mathjazz
  CC:   @MikkCZ, mvasicek@mozilla.cz
  See also:     https://bugzilla.mozilla.org/show_bug.cgi?id=1625102
  
  We should probably do that periodically and aggregate notifications.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `pontoon/projects/management/commands/send_suggestion_notifications.py`
  Content:
  ```
  1 import calendar
  2 
  3 from collections import defaultdict
  4 from datetime import timedelta
  5 from functools import cached_property
  6 
  7 from django.conf import settings
  8 from django.contrib.auth.models import User
  9 from django.core.management.base import BaseCommand, CommandError
  10 from django.db.models import Prefetch, Q
  11 from django.template.loader import render_to_string
  12 from django.utils import timezone
  13 from notifications.signals import notify
  14 
  15 from pontoon.base.models import Comment, Locale, ProjectLocale, Translation
  16 
  17 
  18 class Command(BaseCommand):
  19     help = "Notify contributors about newly added unreviewed suggestions"
  20 
  21     def add_arguments(self, parser):
  22         parser.add_argument(
  23             "--force",
  24             action="store_true",
  25             dest="force",
  26             default=False,
  27             help="Force run command, regardless of what day of the week it is",
  28         )
  29 
  30     @cached_property
  31     def locale_reviewers(self):
  32         locales = Locale.objects.prefetch_related(
  33             Prefetch("managers_group__user_set", to_attr="fetched_managers"),
  34             Prefetch("translators_group__user_set", to_attr="fetched_translators"),
  35         )
  36 
  37         locale_reviewers = {}
  38 
  39         for locale in locales:
  40             managers = locale.managers_group.fetched_managers
  41             translators = locale.translators_group.fetched_translators
  42             locale_reviewers[locale] = managers + translators
  43 
  44         return locale_reviewers
  45 
  46     def extract_notifications_data(self, data, suggestion):
  47         locale = suggestion.locale
  48         entity = suggestion.entity
  49         project = entity.resource.project
  50         project_locale = ProjectLocale.objects.get(project=project, locale=locale)
  51 
  52         translations = Translation.objects.filter(entity=entity, locale=locale)
  53         recipients = set()
  54 
  55         # Users with permission to review suggestions
  56         recipients = recipients.union(self.locale_reviewers[locale])
  57 
  58         # Authors of previous translations of the same string
  59         recipients = recipients.union(User.objects.filter(translation__in=translations))
  60 
  61         # Authors of comments of previous translations
  62         translations_comments = Comment.objects.filter(translation__in=translations)
  63         recipients = recipients.union(
  64             User.objects.filter(comment__in=translations_comments)
  65         )
  66 
  67         # Authors of team comments of the same string
  68         team_comments = Comment.objects.filter(entity=entity, locale=locale)
  69         recipients = recipients.union(User.objects.filter(comment__in=team_comments))
  70 
  71         for recipient in recipients:
  72             data[recipient].add(project_locale)
  73 
  74     def get_suggestions(self):
  75         start = timezone.now() - timedelta(days=7)
  76 
  77         return Translation.objects.filter(
  78             approved=False, rejected=False, fuzzy=False
  79         ).filter(
  80             Q(date__gt=start)
  81             | Q(unapproved_date__gt=start)
  82             | Q(unrejected_date__gt=start)
  83         )
  84 
  85     def handle(self, *args, **options):
  86         """
  87         This command sends notifications about newly created unreviewed suggestions that
  88         were submitted, unapproved or unrejected in the last 7 days. Recipients of
  89         notifications are users with permission to review them, as well as authors of
  90         previous translations or comments of the same string.
  91 
  92         The command is designed to run on a weekly basis.
  93         """
  94         self.stdout.write("Sending suggestion notifications.")
  95 
  96         today = calendar.day_name[timezone.datetime.today().weekday()]
  97         day = calendar.day_name[settings.SUGGESTION_NOTIFICATIONS_DAY]
  98 
  99         if today != day and not options["force"]:
  100             raise CommandError(f"Skipping. Command runs every {day}. Today is {today}.")
  101 
  102         suggestions = self.get_suggestions()
  103 
  104         data = defaultdict(set)
  105 
  106         for suggestion in suggestions:
  107             self.extract_notifications_data(data, suggestion)
  108 
  109         for recipient, project_locales in data.items():
  110             verb = render_to_string(
  111                 "projects/suggestion_notification.jinja",
  112                 {"project_locales": project_locales},
  113             )
  114 
  115             notify.send(recipient, recipient=recipient, verb=verb)
  116 
  117         self.stdout.write("Suggestion notifications sent.")
  118
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/pontoon/projects/management/commands/send_suggestion_notifications.py b/pontoon/projects/management/commands/send_suggestion_notifications.py
  --- a/pontoon/projects/management/commands/send_suggestion_notifications.py
  +++ b/pontoon/projects/management/commands/send_suggestion_notifications.py
  @@ -107,11 +107,13 @@
               self.extract_notifications_data(data, suggestion)
   
           for recipient, project_locales in data.items():
  -            verb = render_to_string(
  +            description = render_to_string(
                   "projects/suggestion_notification.jinja",
                   {"project_locales": project_locales},
               )
   
  -            notify.send(recipient, recipient=recipient, verb=verb)
  +            notify.send(
  +                recipient, recipient=recipient, verb="", description=description
  +            )
   
  -        self.stdout.write("Suggestion notifications sent.")
  +        self.stdout.write(f"Suggestion notifications sent to {len(data)} users.")
  
verification_info: {"golden_diff": "diff --git a/pontoon/projects/management/commands/send_suggestion_notifications.py b/pontoon/projects/management/commands/send_suggestion_notifications.py\n--- a/pontoon/projects/management/commands/send_suggestion_notifications.py\n+++ b/pontoon/projects/management/commands/send_suggestion_notifications.py\n@@ -107,11 +107,13 @@\n             self.extract_notifications_data(data, suggestion)\n \n         for recipient, project_locales in data.items():\n-            verb = render_to_string(\n+            description = render_to_string(\n                 \"projects/suggestion_notification.jinja\",\n                 {\"project_locales\": project_locales},\n             )\n \n-            notify.send(recipient, recipient=recipient, verb=verb)\n+            notify.send(\n+                recipient, recipient=recipient, verb=\"\", description=description\n+            )\n \n-        self.stdout.write(\"Suggestion notifications sent.\")\n+        self.stdout.write(f\"Suggestion notifications sent to {len(data)} users.\")\n", "issue": "[notifications] Notify project Translators when suggestions are submitted\n*This issue was created automatically by a [script](https://github.com/mathjazz/bugzilla2github/).*\n## [Bug 1398861](https://bugzilla.mozilla.org/show_bug.cgi?id=1398861)\nBug Reporter: @mathjazz\nCC:   @MikkCZ, mvasicek@mozilla.cz\nSee also:     https://bugzilla.mozilla.org/show_bug.cgi?id=1625102\n\nWe should probably do that periodically and aggregate notifications.\n", "before_files": [{"content": "import calendar\n\nfrom collections import defaultdict\nfrom datetime import timedelta\nfrom functools import cached_property\n\nfrom django.conf import settings\nfrom django.contrib.auth.models import User\nfrom django.core.management.base import BaseCommand, CommandError\nfrom django.db.models import Prefetch, Q\nfrom django.template.loader import render_to_string\nfrom django.utils import timezone\nfrom notifications.signals import notify\n\nfrom pontoon.base.models import Comment, Locale, ProjectLocale, Translation\n\n\nclass Command(BaseCommand):\n    help = \"Notify contributors about newly added unreviewed suggestions\"\n\n    def add_arguments(self, parser):\n        parser.add_argument(\n            \"--force\",\n            action=\"store_true\",\n            dest=\"force\",\n            default=False,\n            help=\"Force run command, regardless of what day of the week it is\",\n        )\n\n    @cached_property\n    def locale_reviewers(self):\n        locales = Locale.objects.prefetch_related(\n            Prefetch(\"managers_group__user_set\", to_attr=\"fetched_managers\"),\n            Prefetch(\"translators_group__user_set\", to_attr=\"fetched_translators\"),\n        )\n\n        locale_reviewers = {}\n\n        for locale in locales:\n            managers = locale.managers_group.fetched_managers\n            translators = locale.translators_group.fetched_translators\n            locale_reviewers[locale] = managers + translators\n\n        return locale_reviewers\n\n    def extract_notifications_data(self, data, suggestion):\n        locale = suggestion.locale\n        entity = suggestion.entity\n        project = entity.resource.project\n        project_locale = ProjectLocale.objects.get(project=project, locale=locale)\n\n        translations = Translation.objects.filter(entity=entity, locale=locale)\n        recipients = set()\n\n        # Users with permission to review suggestions\n        recipients = recipients.union(self.locale_reviewers[locale])\n\n        # Authors of previous translations of the same string\n        recipients = recipients.union(User.objects.filter(translation__in=translations))\n\n        # Authors of comments of previous translations\n        translations_comments = Comment.objects.filter(translation__in=translations)\n        recipients = recipients.union(\n            User.objects.filter(comment__in=translations_comments)\n        )\n\n        # Authors of team comments of the same string\n        team_comments = Comment.objects.filter(entity=entity, locale=locale)\n        recipients = recipients.union(User.objects.filter(comment__in=team_comments))\n\n        for recipient in recipients:\n            data[recipient].add(project_locale)\n\n    def get_suggestions(self):\n        start = timezone.now() - timedelta(days=7)\n\n        return Translation.objects.filter(\n            approved=False, rejected=False, fuzzy=False\n        ).filter(\n            Q(date__gt=start)\n            | Q(unapproved_date__gt=start)\n            | Q(unrejected_date__gt=start)\n        )\n\n    def handle(self, *args, **options):\n        \"\"\"\n        This command sends notifications about newly created unreviewed suggestions that\n        were submitted, unapproved or unrejected in the last 7 days. Recipients of\n        notifications are users with permission to review them, as well as authors of\n        previous translations or comments of the same string.\n\n        The command is designed to run on a weekly basis.\n        \"\"\"\n        self.stdout.write(\"Sending suggestion notifications.\")\n\n        today = calendar.day_name[timezone.datetime.today().weekday()]\n        day = calendar.day_name[settings.SUGGESTION_NOTIFICATIONS_DAY]\n\n        if today != day and not options[\"force\"]:\n            raise CommandError(f\"Skipping. Command runs every {day}. Today is {today}.\")\n\n        suggestions = self.get_suggestions()\n\n        data = defaultdict(set)\n\n        for suggestion in suggestions:\n            self.extract_notifications_data(data, suggestion)\n\n        for recipient, project_locales in data.items():\n            verb = render_to_string(\n                \"projects/suggestion_notification.jinja\",\n                {\"project_locales\": project_locales},\n            )\n\n            notify.send(recipient, recipient=recipient, verb=verb)\n\n        self.stdout.write(\"Suggestion notifications sent.\")\n", "path": "pontoon/projects/management/commands/send_suggestion_notifications.py"}], "after_files": [{"content": "import calendar\n\nfrom collections import defaultdict\nfrom datetime import timedelta\nfrom functools import cached_property\n\nfrom django.conf import settings\nfrom django.contrib.auth.models import User\nfrom django.core.management.base import BaseCommand, CommandError\nfrom django.db.models import Prefetch, Q\nfrom django.template.loader import render_to_string\nfrom django.utils import timezone\nfrom notifications.signals import notify\n\nfrom pontoon.base.models import Comment, Locale, ProjectLocale, Translation\n\n\nclass Command(BaseCommand):\n    help = \"Notify contributors about newly added unreviewed suggestions\"\n\n    def add_arguments(self, parser):\n        parser.add_argument(\n            \"--force\",\n            action=\"store_true\",\n            dest=\"force\",\n            default=False,\n            help=\"Force run command, regardless of what day of the week it is\",\n        )\n\n    @cached_property\n    def locale_reviewers(self):\n        locales = Locale.objects.prefetch_related(\n            Prefetch(\"managers_group__user_set\", to_attr=\"fetched_managers\"),\n            Prefetch(\"translators_group__user_set\", to_attr=\"fetched_translators\"),\n        )\n\n        locale_reviewers = {}\n\n        for locale in locales:\n            managers = locale.managers_group.fetched_managers\n            translators = locale.translators_group.fetched_translators\n            locale_reviewers[locale] = managers + translators\n\n        return locale_reviewers\n\n    def extract_notifications_data(self, data, suggestion):\n        locale = suggestion.locale\n        entity = suggestion.entity\n        project = entity.resource.project\n        project_locale = ProjectLocale.objects.get(project=project, locale=locale)\n\n        translations = Translation.objects.filter(entity=entity, locale=locale)\n        recipients = set()\n\n        # Users with permission to review suggestions\n        recipients = recipients.union(self.locale_reviewers[locale])\n\n        # Authors of previous translations of the same string\n        recipients = recipients.union(User.objects.filter(translation__in=translations))\n\n        # Authors of comments of previous translations\n        translations_comments = Comment.objects.filter(translation__in=translations)\n        recipients = recipients.union(\n            User.objects.filter(comment__in=translations_comments)\n        )\n\n        # Authors of team comments of the same string\n        team_comments = Comment.objects.filter(entity=entity, locale=locale)\n        recipients = recipients.union(User.objects.filter(comment__in=team_comments))\n\n        for recipient in recipients:\n            data[recipient].add(project_locale)\n\n    def get_suggestions(self):\n        start = timezone.now() - timedelta(days=7)\n\n        return Translation.objects.filter(\n            approved=False, rejected=False, fuzzy=False\n        ).filter(\n            Q(date__gt=start)\n            | Q(unapproved_date__gt=start)\n            | Q(unrejected_date__gt=start)\n        )\n\n    def handle(self, *args, **options):\n        \"\"\"\n        This command sends notifications about newly created unreviewed suggestions that\n        were submitted, unapproved or unrejected in the last 7 days. Recipients of\n        notifications are users with permission to review them, as well as authors of\n        previous translations or comments of the same string.\n\n        The command is designed to run on a weekly basis.\n        \"\"\"\n        self.stdout.write(\"Sending suggestion notifications.\")\n\n        today = calendar.day_name[timezone.datetime.today().weekday()]\n        day = calendar.day_name[settings.SUGGESTION_NOTIFICATIONS_DAY]\n\n        if today != day and not options[\"force\"]:\n            raise CommandError(f\"Skipping. Command runs every {day}. Today is {today}.\")\n\n        suggestions = self.get_suggestions()\n\n        data = defaultdict(set)\n\n        for suggestion in suggestions:\n            self.extract_notifications_data(data, suggestion)\n\n        for recipient, project_locales in data.items():\n            description = render_to_string(\n                \"projects/suggestion_notification.jinja\",\n                {\"project_locales\": project_locales},\n            )\n\n            notify.send(\n                recipient, recipient=recipient, verb=\"\", description=description\n            )\n\n        self.stdout.write(f\"Suggestion notifications sent to {len(data)} users.\")\n", "path": "pontoon/projects/management/commands/send_suggestion_notifications.py"}]}