Repository: svthalia/concrexit

Issue:
Use utils.snippets.send_email in everywhere
<!--

Please add the appropriate label for what change should be made:
docs: changes to the documentation)
refactor: refactoring production code, eg. renaming a variable or rewriting a function
test: adding missing tests, refactoring tests; no production code change
chore: updating poetry etc; no production code change

-->

### Describe the change
Use `utils.snippets.send_email` in all apps. For example, in `registrations` we use our 'own' `_send_email` and `payments` uses the one from `registrations`. They should all use `utils` in fact to centralize this code.

### Motivation
Less code is more better

### Current implementation

### Suggested implementation

### Additional context
Maybe we should also check `events` (or other apps)


Discussion:
Actually I think we should use this everywhere where we send emails

Resolving patch:
diff --git a/website/payments/services.py b/website/payments/services.py
--- a/website/payments/services.py
+++ b/website/payments/services.py
@@ -9,7 +9,7 @@
 from django.utils.translation import gettext_lazy as _
 
 from members.models import Member
-from registrations.emails import _send_email
+from utils.snippets import send_email
 from .exceptions import PaymentError
 from .models import Payment, BankAccount, Payable, PaymentUser
 
@@ -133,7 +133,7 @@ def send_tpay_batch_processing_emails(batch):
         total_amount = member_row["total"]
 
         with translation.override(member.profile.language):
-            _send_email(
+            send_email(
                 member.email,
                 _("Thalia Pay withdrawal notice"),
                 "payments/email/tpay_withdrawal_notice_mail.txt",
diff --git a/website/registrations/emails.py b/website/registrations/emails.py
--- a/website/registrations/emails.py
+++ b/website/registrations/emails.py
@@ -2,14 +2,13 @@
 from typing import Union
 
 from django.conf import settings
-from django.core import mail
-from django.template import loader
 from django.template.defaultfilters import floatformat
 from django.urls import reverse
 from django.utils import translation
 from django.utils.translation import gettext_lazy as _
 
 from registrations.models import Registration, Renewal
+from utils.snippets import send_email
 
 
 def send_registration_email_confirmation(registration: Registration) -> None:
@@ -18,7 +17,7 @@ def send_registration_email_confirmation(registration: Registration) -> None:
     :param registration: the registration entry
     """
     with translation.override(registration.language):
-        _send_email(
+        send_email(
             registration.email,
             _("Confirm email address"),
             "registrations/email/registration_confirm_mail.txt",
@@ -38,7 +37,7 @@ def send_registration_accepted_message(registration: Registration) -> None:
     :param registration: the registration entry
     """
     with translation.override(registration.language):
-        _send_email(
+        send_email(
             registration.email,
             _("Registration accepted"),
             "registrations/email/registration_accepted.txt",
@@ -55,7 +54,7 @@ def send_registration_rejected_message(registration: Registration) -> None:
     :param registration: the registration entry
     """
     with translation.override(registration.language):
-        _send_email(
+        send_email(
             registration.email,
             _("Registration rejected"),
             "registrations/email/registration_rejected.txt",
@@ -68,7 +67,7 @@ def send_new_registration_board_message(registration: Registration) -> None:
 
     :param registration: the registration entry
     """
-    _send_email(
+    send_email(
         settings.BOARD_NOTIFICATION_ADDRESS,
         "New registration",
         "registrations/email/registration_board.txt",
@@ -90,7 +89,7 @@ def send_renewal_accepted_message(renewal: Renewal) -> None:
     :param renewal: the renewal entry
     """
     with translation.override(renewal.member.profile.language):
-        _send_email(
+        send_email(
             renewal.member.email,
             _("Renewal accepted"),
             "registrations/email/renewal_accepted.txt",
@@ -109,7 +108,7 @@ def send_renewal_rejected_message(renewal: Renewal) -> None:
     :param renewal: the renewal entry
     """
     with translation.override(renewal.member.profile.language):
-        _send_email(
+        send_email(
             renewal.member.email,
             _("Renewal rejected"),
             "registrations/email/renewal_rejected.txt",
@@ -123,7 +122,7 @@ def send_renewal_complete_message(renewal: Renewal) -> None:
     :param renewal: the renewal entry
     """
     with translation.override(renewal.member.profile.language):
-        _send_email(
+        send_email(
             renewal.member.email,
             _("Renewal successful"),
             "registrations/email/renewal_complete.txt",
@@ -136,7 +135,7 @@ def send_new_renewal_board_message(renewal: Renewal) -> None:
 
     :param renewal: the renewal entry
     """
-    _send_email(
+    send_email(
         settings.BOARD_NOTIFICATION_ADDRESS,
         "New renewal",
         "registrations/email/renewal_board.txt",
@@ -168,7 +167,7 @@ def send_references_information_message(entry: Union[Registration, Renewal]) ->
         language = entry.member.profile.language
 
     with translation.override(language):
-        _send_email(
+        send_email(
             email,
             _("Information about references"),
             "registrations/email/references_information.txt",
@@ -180,19 +179,3 @@ def send_references_information_message(entry: Union[Registration, Renewal]) ->
                 ),
             },
         )
-
-
-def _send_email(to: str, subject: str, body_template: str, context: dict) -> None:
-    """Easily send an email with the right subject and a body template.
-
-    :param to: where should the email go?
-    :param subject: what is the email about?
-    :param body_template: what is the content of the email?
-    :param context: add some context to the body
-    """
-    mail.EmailMessage(
-        "[THALIA] {}".format(subject),
-        loader.render_to_string(body_template, context),
-        settings.DEFAULT_FROM_EMAIL,
-        [to],
-    ).send()
diff --git a/website/utils/snippets.py b/website/utils/snippets.py
--- a/website/utils/snippets.py
+++ b/website/utils/snippets.py
@@ -6,6 +6,8 @@
 from _sha1 import sha1
 
 from django.conf import settings
+from django.core import mail
+from django.template import loader
 from django.template.defaultfilters import urlencode
 from django.templatetags.static import static
 from django.utils import timezone, dateparse
@@ -182,3 +184,19 @@ def overlaps(check, others, can_equal=True):
             return True
 
     return False
+
+
+def send_email(to: str, subject: str, body_template: str, context: dict) -> None:
+    """Easily send an email with the right subject and a body template.
+
+    :param to: where should the email go?
+    :param subject: what is the email about?
+    :param body_template: what is the content of the email?
+    :param context: add some context to the body
+    """
+    mail.EmailMessage(
+        "[THALIA] {}".format(subject),
+        loader.render_to_string(body_template, context),
+        settings.DEFAULT_FROM_EMAIL,
+        [to],
+    ).send()


Test patch:
diff --git a/website/registrations/tests/test_emails.py b/website/registrations/tests/test_emails.py
--- a/website/registrations/tests/test_emails.py
+++ b/website/registrations/tests/test_emails.py
@@ -12,7 +12,7 @@
 
 from members.models import Member, Profile
 from registrations import emails
-from registrations.emails import _send_email
+from utils.snippets import send_email
 from registrations.models import Registration, Renewal
 
 
@@ -20,7 +20,7 @@ class EmailsTest(TestCase):
     def setUp(self):
         translation.activate("en")
 
-    @mock.patch("registrations.emails._send_email")
+    @mock.patch("registrations.emails.send_email")
     def test_send_registration_email_confirmation(self, send_email):
         reg = Registration(
             email="test@example.org",
@@ -45,7 +45,7 @@ def test_send_registration_email_confirmation(self, send_email):
                 },
             )
 
-    @mock.patch("registrations.emails._send_email")
+    @mock.patch("registrations.emails.send_email")
     def test_send_registration_accepted_message(self, send_email):
         reg = Registration(
             email="test@example.org",
@@ -65,7 +65,7 @@ def test_send_registration_accepted_message(self, send_email):
                 {"name": reg.get_full_name(), "fees": floatformat(reg.contribution, 2)},
             )
 
-    @mock.patch("registrations.emails._send_email")
+    @mock.patch("registrations.emails.send_email")
     def test_send_registration_rejected_message(self, send_email):
         reg = Registration(
             email="test@example.org", first_name="John", last_name="Doe", pk=0,
@@ -81,7 +81,7 @@ def test_send_registration_rejected_message(self, send_email):
                 {"name": reg.get_full_name()},
             )
 
-    @mock.patch("registrations.emails._send_email")
+    @mock.patch("registrations.emails.send_email")
     def test_send_new_registration_board_message(self, send_email):
         registration = Registration(
             email="test@example.org", first_name="John", last_name="Doe", pk=0,
@@ -105,7 +105,7 @@ def test_send_new_registration_board_message(self, send_email):
             },
         )
 
-    @mock.patch("registrations.emails._send_email")
+    @mock.patch("registrations.emails.send_email")
     def test_send_renewal_accepted_message(self, send_email):
         member = Member(
             email="test@example.org",
@@ -131,7 +131,7 @@ def test_send_renewal_accepted_message(self, send_email):
                 },
             )
 
-    @mock.patch("registrations.emails._send_email")
+    @mock.patch("registrations.emails.send_email")
     def test_send_renewal_rejected_message(self, send_email):
         member = Member(
             email="test@example.org",
@@ -152,7 +152,7 @@ def test_send_renewal_rejected_message(self, send_email):
                 {"name": renewal.member.get_full_name()},
             )
 
-    @mock.patch("registrations.emails._send_email")
+    @mock.patch("registrations.emails.send_email")
     def test_send_renewal_complete_message(self, send_email):
         member = Member(
             email="test@example.org",
@@ -173,7 +173,7 @@ def test_send_renewal_complete_message(self, send_email):
                 {"name": renewal.member.get_full_name()},
             )
 
-    @mock.patch("registrations.emails._send_email")
+    @mock.patch("registrations.emails.send_email")
     def test_send_new_renewal_board_message(self, send_email):
         member = Member(
             email="test@example.org",
@@ -199,7 +199,7 @@ def test_send_new_renewal_board_message(self, send_email):
             },
         )
 
-    @mock.patch("registrations.emails._send_email")
+    @mock.patch("registrations.emails.send_email")
     def test_send_references_information_message(self, send_email):
         with self.subTest("Registrations"):
             registration = Registration(
@@ -252,7 +252,7 @@ def test_send_references_information_message(self, send_email):
             )
 
     def test_send_email(self):
-        _send_email(
+        send_email(
             subject="Subject",
             to="test@example.org",
             body_template="registrations/email/renewal_board.txt",