prompt: You will be provided with a partial code base and an issue statement explaining a problem to resolve.
  <issue>
  Feature request: send email when teacher adds news
  Hi
  
  It would be very nice to be able to notify students via email when teacher adds news. This should be an option so the teacher could decide on case-by-case basis whether to send the email or not.
  
  What do you think?
  
  Thanks!
  Feature request: send email when teacher adds news
  Hi
  
  It would be very nice to be able to notify students via email when teacher adds news. This should be an option so the teacher could decide on case-by-case basis whether to send the email or not.
  
  What do you think?
  
  Thanks!
  
  </issue>
  <code>
  [start of news/forms.py]
  1 from django import forms
  2 
  3 from .models import News
  4 
  5 
  6 class NewsForm(forms.ModelForm):
  7 
  8     class Meta:
  9         model = News
  10         fields = [
  11             'audience',
  12             'publish',
  13             'pin',
  14             'title',
  15             'body',
  16         ]
  17
  [end of news/forms.py]
  [start of lib/email_messages.py]
  1 import logging
  2 import traceback
  3 from django.conf import settings
  4 from django.core.mail import send_mail
  5 from django.urls import reverse
  6 from .helpers import build_aplus_url
  7 
  8 
  9 logger = logging.getLogger('aplus.lib.email_messages')
  10 
  11 
  12 def email_course_instance(instance, subject, message, everyone=False) -> bool:
  13     """
  14     Sends an email to a course instance's technical support emails or teachers if technical support not set.
  15     If everyone == True, sends emails to teachers anyway.
  16     """
  17     recipients = []
  18     if instance.technical_error_emails:
  19         recipients = instance.technical_error_emails.split(",")
  20     if everyone or not recipients:
  21         recipients = instance.teachers.exclude(user__email='').values_list("user__email", flat=True)
  22 
  23     if not recipients:
  24         raise ValueError("No recipients")
  25 
  26     try:
  27         return send_mail(subject, message, settings.SERVER_EMAIL, recipients, True) == 1
  28     except:
  29         logger.exception('Failed to send course instance emails.')
  30         raise
  31 
  32 
  33 def email_course_error(request, exercise, message, exception=True):
  34     """
  35     Sends error message to course instance's teachers or technical support emails if set.
  36     """
  37     instance = exercise.course_instance
  38 
  39     error_trace = "-"
  40     if exception:
  41         error_trace = traceback.format_exc()
  42 
  43     if request:
  44         request_fields = repr(request)
  45     else:
  46         request_fields = "No request available"
  47 
  48     subject = settings.EXERCISE_ERROR_SUBJECT.format(
  49         course=instance.course.code,
  50         exercise=str(exercise))
  51     body = settings.EXERCISE_ERROR_DESCRIPTION.format(
  52         message=message,
  53         exercise_url=build_aplus_url(
  54             exercise.get_absolute_url(), user_url=True),
  55         course_edit_url=build_aplus_url(
  56             instance.get_url('course-details'), user_url=True),
  57         error_trace=error_trace,
  58         request_fields=request_fields)
  59 
  60     try:
  61         email_course_instance(instance, subject, body)
  62     except:
  63         pass
  64
  [end of lib/email_messages.py]
  [start of news/views.py]
  1 from django.core.exceptions import PermissionDenied
  2 from django.http import Http404
  3 from django.shortcuts import get_object_or_404
  4 
  5 from authorization.permissions import ACCESS
  6 from course.viewbase import CourseInstanceBaseView, CourseInstanceMixin
  7 from lib.viewbase import BaseFormView, BaseRedirectView
  8 from .forms import NewsForm
  9 from .models import News
  10 
  11 
  12 class ListNewsView(CourseInstanceBaseView):
  13     access_mode = ACCESS.TEACHER
  14     template_name = "news/list.html"
  15 
  16     def get_common_objects(self):
  17         super().get_common_objects()
  18         self.news = self.instance.news.all()
  19         self.note("news")
  20 
  21 
  22 class EditNewsView(CourseInstanceMixin, BaseFormView):
  23     access_mode = ACCESS.TEACHER
  24     template_name = "news/edit.html"
  25     form_class = NewsForm
  26     news_item_kw = "news_id"
  27 
  28     def get_form_kwargs(self):
  29         kwargs = super().get_form_kwargs()
  30 
  31         news_id = self._get_kwarg(self.news_item_kw, default=None)
  32         if news_id:
  33             self.news_item = get_object_or_404(
  34                 News,
  35                 pk=news_id,
  36                 course_instance=self.instance
  37             )
  38             self.note("news_item")
  39         else:
  40             self.news_item = News(course_instance=self.instance)
  41 
  42         kwargs["instance"] = self.news_item
  43         return kwargs
  44 
  45     def get_success_url(self):
  46         return self.instance.get_url("news-list")
  47 
  48     def form_valid(self, form):
  49         form.save()
  50         return super().form_valid(form)
  51 
  52 
  53 class RemoveNewsView(CourseInstanceMixin, BaseRedirectView):
  54     access_mode = ACCESS.TEACHER
  55     news_item_kw = "news_id"
  56 
  57     def get_resource_objects(self):
  58         super().get_resource_objects()
  59         self.news_item = get_object_or_404(
  60             News,
  61             id=self._get_kwarg(self.news_item_kw),
  62             course_instance=self.instance,
  63         )
  64         self.note("news_item")
  65 
  66     def post(self, request, *args, **kwargs):
  67         self.news_item.delete()
  68         return self.redirect(self.instance.get_url("news-list"))
  69
  [end of news/views.py]
  </code>
  
  I need you to solve this issue by generating a single patch file that I can apply directly to this repository using git apply. Please respond with a single patch file in the following format.
  <patch>
  diff --git a/file.py b/file.py
  --- a/file.py
  +++ b/file.py
  @@ -1,27 +1,35 @@
   def euclidean(a, b):
  -    while b:
  -        a, b = b, a % b
  -    return a
  +    if b == 0:
  +        return a
  +    return euclidean(b, a % b)
   
   
   def bresenham(x0, y0, x1, y1):
       points = []
       dx = abs(x1 - x0)
       dy = abs(y1 - y0)
  -    sx = 1 if x0 < x1 else -1
  -    sy = 1 if y0 < y1 else -1
  -    err = dx - dy
  +    x, y = x0, y0
  +    sx = -1 if x0 > x1 else 1
  +    sy = -1 if y0 > y1 else 1
   
  -    while True:
  -        points.append((x0, y0))
  -        if x0 == x1 and y0 == y1:
  -            break
  -        e2 = 2 * err
  -        if e2 > -dy:
  -            err -= dy
  -            x0 += sx
  -        if e2 < dx:
  -            err += dx
  -            y0 += sy
  +    if dx > dy:
  +        err = dx / 2.0
  +        while x != x1:
  +            points.append((x, y))
  +            err -= dy
  +            if err < 0:
  +                y += sy
  +                err += dx
  +            x += sx
  +    else:
  +        err = dy / 2.0
  +        while y != y1:
  +            points.append((x, y))
  +            err -= dx
  +            if err < 0:
  +                x += sx
  +                err += dy
  +            y += sy
  +
  +    points.append((x, y))
       return points
  
  </patch>
golden_diff: diff --git a/lib/email_messages.py b/lib/email_messages.py
  --- a/lib/email_messages.py
  +++ b/lib/email_messages.py
  @@ -1,9 +1,10 @@
   import logging
   import traceback
   from django.conf import settings
  -from django.core.mail import send_mail
  -from django.urls import reverse
  -from .helpers import build_aplus_url
  +from django.core.mail import send_mail, send_mass_mail
  +
  +from .helpers import Enum, build_aplus_url
  +from course.models import CourseInstance
   
   
   logger = logging.getLogger('aplus.lib.email_messages')
  @@ -61,3 +62,30 @@
           email_course_instance(instance, subject, body)
       except:
           pass
  +
  +
  +def email_course_students(
  +        instance: CourseInstance,
  +        subject: str,
  +        message: str,
  +        audience: Enum = CourseInstance.ENROLLMENT_AUDIENCE.ALL_USERS,
  +        ) -> int:
  +    """
  +    Sends an email to students on the course. Audience parameter controls whether the mail goes
  +    to all (default), just internal, or just external students.
  +    Returns number of emails sent, or -1 in case of error.
  +    """
  +    students = instance.students
  +    if audience == CourseInstance.ENROLLMENT_AUDIENCE.INTERNAL_USERS:
  +        students = students.filter(organization=settings.LOCAL_ORGANIZATION)
  +    elif audience == CourseInstance.ENROLLMENT_AUDIENCE.EXTERNAL_USERS:
  +        students = students.exclude(organization=settings.LOCAL_ORGANIZATION)
  +
  +    recipients = students.exclude(user__email='').values_list("user__email", flat=True)
  +    emails = tuple(map(lambda x: (subject, message, settings.SERVER_EMAIL, [x]), recipients))
  +
  +    try:
  +        return send_mass_mail(emails)
  +    except:
  +        logger.exception('Failed to send course instance emails.')
  +        return -1
  diff --git a/news/forms.py b/news/forms.py
  --- a/news/forms.py
  +++ b/news/forms.py
  @@ -1,16 +1,25 @@
  +from typing import Any
  +
   from django import forms
  +from django.utils.translation import gettext_lazy as _
   
   from .models import News
   
   
   class NewsForm(forms.ModelForm):
   
  +    email = forms.BooleanField(
  +        required=False,
  +        label=_("SEND_EMAIL_TO_STUDENTS"),
  +    )
  +
       class Meta:
           model = News
           fields = [
               'audience',
               'publish',
               'pin',
  +            'email',
               'title',
               'body',
           ]
  diff --git a/news/views.py b/news/views.py
  --- a/news/views.py
  +++ b/news/views.py
  @@ -1,10 +1,14 @@
  +from django.conf import settings
  +from django.contrib import messages
   from django.core.exceptions import PermissionDenied
   from django.http import Http404
   from django.shortcuts import get_object_or_404
  +from django.utils.translation import gettext_lazy as _
   
   from authorization.permissions import ACCESS
   from course.viewbase import CourseInstanceBaseView, CourseInstanceMixin
   from lib.viewbase import BaseFormView, BaseRedirectView
  +from lib.email_messages import email_course_students
   from .forms import NewsForm
   from .models import News
   
  @@ -47,6 +51,15 @@
   
       def form_valid(self, form):
           form.save()
  +        if form.cleaned_data['email']:
  +            subject = f"[{settings.BRAND_NAME} course news] {self.instance.course.code}: {self.news_item.title}"
  +            if email_course_students(
  +                self.instance,
  +                subject,
  +                self.news_item.body,
  +                self.news_item.audience,
  +            ) < 0:
  +                messages.error(self.request, _('FAILED_TO_SEND_EMAIL'))
           return super().form_valid(form)
  
verification_info: {"golden_diff": "diff --git a/lib/email_messages.py b/lib/email_messages.py\n--- a/lib/email_messages.py\n+++ b/lib/email_messages.py\n@@ -1,9 +1,10 @@\n import logging\n import traceback\n from django.conf import settings\n-from django.core.mail import send_mail\n-from django.urls import reverse\n-from .helpers import build_aplus_url\n+from django.core.mail import send_mail, send_mass_mail\n+\n+from .helpers import Enum, build_aplus_url\n+from course.models import CourseInstance\n \n \n logger = logging.getLogger('aplus.lib.email_messages')\n@@ -61,3 +62,30 @@\n         email_course_instance(instance, subject, body)\n     except:\n         pass\n+\n+\n+def email_course_students(\n+        instance: CourseInstance,\n+        subject: str,\n+        message: str,\n+        audience: Enum = CourseInstance.ENROLLMENT_AUDIENCE.ALL_USERS,\n+        ) -> int:\n+    \"\"\"\n+    Sends an email to students on the course. Audience parameter controls whether the mail goes\n+    to all (default), just internal, or just external students.\n+    Returns number of emails sent, or -1 in case of error.\n+    \"\"\"\n+    students = instance.students\n+    if audience == CourseInstance.ENROLLMENT_AUDIENCE.INTERNAL_USERS:\n+        students = students.filter(organization=settings.LOCAL_ORGANIZATION)\n+    elif audience == CourseInstance.ENROLLMENT_AUDIENCE.EXTERNAL_USERS:\n+        students = students.exclude(organization=settings.LOCAL_ORGANIZATION)\n+\n+    recipients = students.exclude(user__email='').values_list(\"user__email\", flat=True)\n+    emails = tuple(map(lambda x: (subject, message, settings.SERVER_EMAIL, [x]), recipients))\n+\n+    try:\n+        return send_mass_mail(emails)\n+    except:\n+        logger.exception('Failed to send course instance emails.')\n+        return -1\ndiff --git a/news/forms.py b/news/forms.py\n--- a/news/forms.py\n+++ b/news/forms.py\n@@ -1,16 +1,25 @@\n+from typing import Any\n+\n from django import forms\n+from django.utils.translation import gettext_lazy as _\n \n from .models import News\n \n \n class NewsForm(forms.ModelForm):\n \n+    email = forms.BooleanField(\n+        required=False,\n+        label=_(\"SEND_EMAIL_TO_STUDENTS\"),\n+    )\n+\n     class Meta:\n         model = News\n         fields = [\n             'audience',\n             'publish',\n             'pin',\n+            'email',\n             'title',\n             'body',\n         ]\ndiff --git a/news/views.py b/news/views.py\n--- a/news/views.py\n+++ b/news/views.py\n@@ -1,10 +1,14 @@\n+from django.conf import settings\n+from django.contrib import messages\n from django.core.exceptions import PermissionDenied\n from django.http import Http404\n from django.shortcuts import get_object_or_404\n+from django.utils.translation import gettext_lazy as _\n \n from authorization.permissions import ACCESS\n from course.viewbase import CourseInstanceBaseView, CourseInstanceMixin\n from lib.viewbase import BaseFormView, BaseRedirectView\n+from lib.email_messages import email_course_students\n from .forms import NewsForm\n from .models import News\n \n@@ -47,6 +51,15 @@\n \n     def form_valid(self, form):\n         form.save()\n+        if form.cleaned_data['email']:\n+            subject = f\"[{settings.BRAND_NAME} course news] {self.instance.course.code}: {self.news_item.title}\"\n+            if email_course_students(\n+                self.instance,\n+                subject,\n+                self.news_item.body,\n+                self.news_item.audience,\n+            ) < 0:\n+                messages.error(self.request, _('FAILED_TO_SEND_EMAIL'))\n         return super().form_valid(form)\n", "issue": "Feature request: send email when teacher adds news\nHi\r\n\r\nIt would be very nice to be able to notify students via email when teacher adds news. This should be an option so the teacher could decide on case-by-case basis whether to send the email or not.\r\n\r\nWhat do you think?\r\n\r\nThanks!\nFeature request: send email when teacher adds news\nHi\r\n\r\nIt would be very nice to be able to notify students via email when teacher adds news. This should be an option so the teacher could decide on case-by-case basis whether to send the email or not.\r\n\r\nWhat do you think?\r\n\r\nThanks!\n", "before_files": [{"content": "from django import forms\n\nfrom .models import News\n\n\nclass NewsForm(forms.ModelForm):\n\n    class Meta:\n        model = News\n        fields = [\n            'audience',\n            'publish',\n            'pin',\n            'title',\n            'body',\n        ]\n", "path": "news/forms.py"}, {"content": "import logging\nimport traceback\nfrom django.conf import settings\nfrom django.core.mail import send_mail\nfrom django.urls import reverse\nfrom .helpers import build_aplus_url\n\n\nlogger = logging.getLogger('aplus.lib.email_messages')\n\n\ndef email_course_instance(instance, subject, message, everyone=False) -> bool:\n    \"\"\"\n    Sends an email to a course instance's technical support emails or teachers if technical support not set.\n    If everyone == True, sends emails to teachers anyway.\n    \"\"\"\n    recipients = []\n    if instance.technical_error_emails:\n        recipients = instance.technical_error_emails.split(\",\")\n    if everyone or not recipients:\n        recipients = instance.teachers.exclude(user__email='').values_list(\"user__email\", flat=True)\n\n    if not recipients:\n        raise ValueError(\"No recipients\")\n\n    try:\n        return send_mail(subject, message, settings.SERVER_EMAIL, recipients, True) == 1\n    except:\n        logger.exception('Failed to send course instance emails.')\n        raise\n\n\ndef email_course_error(request, exercise, message, exception=True):\n    \"\"\"\n    Sends error message to course instance's teachers or technical support emails if set.\n    \"\"\"\n    instance = exercise.course_instance\n\n    error_trace = \"-\"\n    if exception:\n        error_trace = traceback.format_exc()\n\n    if request:\n        request_fields = repr(request)\n    else:\n        request_fields = \"No request available\"\n\n    subject = settings.EXERCISE_ERROR_SUBJECT.format(\n        course=instance.course.code,\n        exercise=str(exercise))\n    body = settings.EXERCISE_ERROR_DESCRIPTION.format(\n        message=message,\n        exercise_url=build_aplus_url(\n            exercise.get_absolute_url(), user_url=True),\n        course_edit_url=build_aplus_url(\n            instance.get_url('course-details'), user_url=True),\n        error_trace=error_trace,\n        request_fields=request_fields)\n\n    try:\n        email_course_instance(instance, subject, body)\n    except:\n        pass\n", "path": "lib/email_messages.py"}, {"content": "from django.core.exceptions import PermissionDenied\nfrom django.http import Http404\nfrom django.shortcuts import get_object_or_404\n\nfrom authorization.permissions import ACCESS\nfrom course.viewbase import CourseInstanceBaseView, CourseInstanceMixin\nfrom lib.viewbase import BaseFormView, BaseRedirectView\nfrom .forms import NewsForm\nfrom .models import News\n\n\nclass ListNewsView(CourseInstanceBaseView):\n    access_mode = ACCESS.TEACHER\n    template_name = \"news/list.html\"\n\n    def get_common_objects(self):\n        super().get_common_objects()\n        self.news = self.instance.news.all()\n        self.note(\"news\")\n\n\nclass EditNewsView(CourseInstanceMixin, BaseFormView):\n    access_mode = ACCESS.TEACHER\n    template_name = \"news/edit.html\"\n    form_class = NewsForm\n    news_item_kw = \"news_id\"\n\n    def get_form_kwargs(self):\n        kwargs = super().get_form_kwargs()\n\n        news_id = self._get_kwarg(self.news_item_kw, default=None)\n        if news_id:\n            self.news_item = get_object_or_404(\n                News,\n                pk=news_id,\n                course_instance=self.instance\n            )\n            self.note(\"news_item\")\n        else:\n            self.news_item = News(course_instance=self.instance)\n\n        kwargs[\"instance\"] = self.news_item\n        return kwargs\n\n    def get_success_url(self):\n        return self.instance.get_url(\"news-list\")\n\n    def form_valid(self, form):\n        form.save()\n        return super().form_valid(form)\n\n\nclass RemoveNewsView(CourseInstanceMixin, BaseRedirectView):\n    access_mode = ACCESS.TEACHER\n    news_item_kw = \"news_id\"\n\n    def get_resource_objects(self):\n        super().get_resource_objects()\n        self.news_item = get_object_or_404(\n            News,\n            id=self._get_kwarg(self.news_item_kw),\n            course_instance=self.instance,\n        )\n        self.note(\"news_item\")\n\n    def post(self, request, *args, **kwargs):\n        self.news_item.delete()\n        return self.redirect(self.instance.get_url(\"news-list\"))\n", "path": "news/views.py"}]}