Repository: svthalia/concrexit

Issue:
Notification when moving from queue to participants list
### Describe the bug
When number of spots was increased and participants move from queue to participants list, they do not get notification. The system works fine in case of cancellation.

### How to reproduce
Steps to reproduce the behaviour:
1. Create event 
2. Participants are added to the participants list and queue 
3. Number of places is increased
4. People in queue move to participants list
5. Notification about it not sent

### Expected behaviour
Thalia member should receive a notification email about their participation in event.


Discussion:
I have a deja vu. I think we had this issue before. And I remember something about implementation a manual warning that said something along the lines of 'no email is sent'. Moreover, I thought we actually did that.

Mostly because sending an email is quite a 'destructive' operation, you could possible send an email to a full queue even and then find out the number was a mistake. And then you cannot go back. Adding warnings _before_ saving may possibly be an option since Django changed some stuff with the forms in the admin.
`self.message_user` in the admin.py file for the events should work and then we could show a warning like this:
<img width="901" alt="Screenshot 2022-03-30 at 20 10 15" src="https://user-images.githubusercontent.com/1799914/160902902-86e691c4-0d12-4b51-b2e1-c7271295186d.png">

I think we never implemented this and only talked about it.


Resolving patch:
diff --git a/website/events/admin/event.py b/website/events/admin/event.py
--- a/website/events/admin/event.py
+++ b/website/events/admin/event.py
@@ -1,13 +1,13 @@
 """Registers admin interfaces for the event model."""
 
-from django.contrib import admin
+from django.contrib import admin, messages
 from django.template.defaultfilters import date as _date
 from django.urls import path, reverse
 from django.utils import timezone
 from django.utils.html import format_html
 from django.utils.translation import gettext_lazy as _
 
-from events import models, services
+from events import emails, models, services
 from events.admin.filters import LectureYearFilter
 from events.admin.forms import EventAdminForm, RegistrationInformationFieldForm
 from events.admin.inlines import (
@@ -197,6 +197,39 @@ def save_formset(self, request, form, formset, change):
         )
         form.instance.save()
 
+    def save_model(self, request, obj, form, change):
+        if change and "max_participants" in form.changed_data:
+            prev = self.model.objects.get(id=obj.id)
+            prev_limit = prev.max_participants
+            self_limit = obj.max_participants
+            if prev_limit is None:
+                prev_limit = prev.participant_count
+            if self_limit is None:
+                self_limit = obj.participant_count
+
+            if prev_limit < self_limit and prev_limit < obj.participant_count:
+                diff = self_limit - prev_limit
+                joiners = prev.queue[:diff]
+                for registration in joiners:
+                    emails.notify_waiting(obj, registration)
+                messages.info(
+                    request,
+                    "The maximum number of participants was increased. Any members that moved from the waiting list to the participants list have been notified.",
+                )
+            elif self_limit < prev_limit and self_limit < obj.participant_count:
+                diff = self_limit - prev_limit
+                leavers = prev.registrations[self_limit:]
+                address = map(lambda r: r.email, leavers)
+                link = "mailto:?bcc=" + ",".join(address)
+                messages.warning(
+                    request,
+                    format_html(
+                        "The maximum number of participants was decreased and some members moved to the waiting list. <a href='{}' style='text-decoration: underline;'>Use this link to send them an email.</a>",
+                        link,
+                    ),
+                )
+        super().save_model(request, obj, form, change)
+
     def get_actions(self, request):
         actions = super().get_actions(request)
         if "delete_selected" in actions:
diff --git a/website/events/emails.py b/website/events/emails.py
--- a/website/events/emails.py
+++ b/website/events/emails.py
@@ -4,8 +4,6 @@
 from django.template.loader import get_template
 from django.utils.translation import gettext_lazy as _
 
-from events.models import EventRegistration
-
 
 def notify_first_waiting(event):
     """Send an email to the first person on the waiting list when someone cancels their registration.
@@ -14,14 +12,13 @@ def notify_first_waiting(event):
     """
     if (
         event.max_participants is not None
-        and EventRegistration.objects.filter(event=event, date_cancelled=None).count()
+        and event.eventregistration_set.filter(date_cancelled=None).count()
         > event.max_participants
     ):
         # Prepare email to send to the first person on the waiting list
-        first_waiting = EventRegistration.objects.filter(
-            event=event, date_cancelled=None
+        first_waiting = event.eventregistration_set.filter(
+            date_cancelled=None
         ).order_by("date")[event.max_participants]
-        first_waiting_member = first_waiting.member
 
         text_template = get_template("events/member_email.txt")
 
@@ -32,7 +29,7 @@ def notify_first_waiting(event):
             {
                 "event": event,
                 "registration": first_waiting,
-                "member": first_waiting_member,
+                "name": first_waiting.name or first_waiting.member.first_name,
                 "base_url": settings.BASE_URL,
             }
         )
@@ -61,3 +58,19 @@ def notify_organiser(event, registration):
             for organiser in event.organisers.all()
         ],
     ).send()
+
+
+def notify_waiting(event, registration):
+    text_template = get_template("events/more_places_email.txt")
+    subject = _("[THALIA] Notification about your registration for '{}'").format(
+        event.title
+    )
+    text_message = text_template.render(
+        {
+            "event": event,
+            "registration": registration,
+            "name": registration.name or registration.member.first_name,
+            "base_url": settings.BASE_URL,
+        }
+    )
+    EmailMessage(subject, text_message, to=[registration.email]).send()


Test patch: