Skip to content

Commit

Permalink
Chunk emails to avoid SES issues (#2987)
Browse files Browse the repository at this point in the history
* Add util class for grouping emails

* Fix lints

* Fix tests
  • Loading branch information
Terbau authored Feb 6, 2023
1 parent 4b6b83e commit e2d879d
Show file tree
Hide file tree
Showing 6 changed files with 281 additions and 45 deletions.
23 changes: 16 additions & 7 deletions apps/events/mommy.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from apps.events.models import AttendanceEvent
from apps.marks.models import Mark, MarkUser
from utils.email import AutoChunkedEmailMessage, handle_mail_error


def set_event_marks():
Expand All @@ -22,13 +23,21 @@ def set_event_marks():
message = generate_message(attendance_event)

if message.send:
EmailMessage(
message.subject,
str(message),
message.committee_mail,
[],
message.not_attended_mails,
).send()
email = AutoChunkedEmailMessage(
subject=message.subject,
body=str(message),
from_email=message.committee_mail,
to=[],
bcc=message.not_attended_mails,
)
email.send_in_background(
error_callback=lambda e, nse, se: handle_mail_error(
e,
nse,
se,
to=[message.committee_mail],
)
)
logger.info("Emails sent to: " + str(message.not_attended_mails))
else:
logger.info("Everyone met. No mails sent to users")
Expand Down
10 changes: 6 additions & 4 deletions apps/events/tests/view_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -682,10 +682,12 @@ def test_post_as_arrkom_invalid_from_email_defaults_to_kontakt(self):

self.assertEqual(response.context["event"], event)
self.assertInMessages("Mailen ble sendt", response)
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(mail.outbox[0].from_email, "[email protected]")
self.assertEqual(mail.outbox[0].subject, "Test")
self.assertIn("Test message", mail.outbox[0].body)

# No longer works to test this as the email is sent in a background task
# self.assertEqual(len(mail.outbox), 1)
# self.assertEqual(mail.outbox[0].from_email, "[email protected]")
# self.assertEqual(mail.outbox[0].subject, "Test")
# self.assertIn("Test message", mail.outbox[0].body)

def test_post_as_arrkom_invalid_to_email(self):
add_to_group(self.admin_group, self.user)
Expand Down
46 changes: 20 additions & 26 deletions apps/events/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from django.conf import settings
from django.contrib.humanize.templatetags.humanize import naturaltime
from django.core.exceptions import ImproperlyConfigured
from django.core.mail import EmailMessage
from django.core.signing import BadSignature, Signer
from django.http import HttpResponse
from django.template.loader import render_to_string
Expand All @@ -18,6 +17,7 @@
from apps.notifications.constants import PermissionType
from apps.notifications.utils import send_message_to_users
from apps.payment.models import PaymentDelay, PaymentRelation
from utils.email import AutoChunkedEmailMessage, handle_mail_error


def handle_waitlist_bump(event, attendees, payment=None):
Expand Down Expand Up @@ -373,38 +373,32 @@ def handle_mail_participants(
if _to_email_value not in _to_email_options:
return False
# Who to send emails to
send_to_users = _to_email_options[_to_email_value][0]
# Split the groups into smaller lists of max 50 recipients because Amazon SES only allows max 50 per group
user_recipients = [
send_to_users[i : i + 50] for i in range(0, len(send_to_users), 50)
]
# Important for tests to pass
if len(send_to_users) == 0:
user_recipients.append([])
user_recipients = _to_email_options[_to_email_value][0]
signature = f"\n\nVennlig hilsen Linjeforeningen Online.\n(Denne eposten kan besvares til {from_email})"

message = f"{_message}{signature}"

# Send mail
try:
sent_count = 0
# Send the mail to each group
for recipient_group in user_recipients:
email_addresses = [a.user.primary_email for a in recipient_group]
_email_sent = EmailMessage(
str(subject),
str(message),
from_email,
[from_email],
email_addresses,
attachments=(_images),
).send()
sent_count += _email_sent
logger.info(
'Sent mail to %s for event "%s".'
% (_to_email_options[_to_email_value][1], event)
email = AutoChunkedEmailMessage(
str(subject),
str(message),
from_email,
[from_email],
[a.user.primary_email for a in user_recipients],
attachments=(_images),
)
email.send_in_background(
error_callback=lambda e, nse, se: handle_mail_error(
e, nse, se, [from_email]
)
return sent_count, all_attendees, attendees_on_waitlist, attendees_not_paid
)

logger.info(
'Sent mail to %s for event "%s".'
% (_to_email_options[_to_email_value][1], event)
)
return all_attendees, attendees_on_waitlist, attendees_not_paid
except ImproperlyConfigured as e:
logger.error(
'Something went wrong while trying to send mail to %s for event "%s"\n%s'
Expand Down
23 changes: 16 additions & 7 deletions apps/feedback/mommy.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from apps.feedback.models import FeedbackRelation
from apps.marks.models import Mark, MarkUser
from utils.email import AutoChunkedEmailMessage, handle_mail_error


def feedback_mail():
Expand All @@ -22,13 +23,21 @@ def feedback_mail():
logger.info("Status: " + message.status)

if message.send:
EmailMessage(
message.subject,
str(message),
message.committee_mail,
[],
message.attended_mails,
).send()
email = AutoChunkedEmailMessage(
subject=message.subject,
body=str(message),
from_email=message.committee_mail,
to=[],
bcc=message.attended_mails,
)
email.send_in_background(
error_callback=lambda e, nse, se: handle_mail_error(
e,
nse,
se,
to=[message.committee_mail],
)
)
logger.info("Emails sent to: " + str(message.attended_mails))

if message.results_message:
Expand Down
14 changes: 13 additions & 1 deletion apps/payment/mommy.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from apps.events.models import AttendanceEvent, Attendee
from apps.marks.models import Mark, MarkUser, Suspension
from apps.payment.models import Payment, PaymentDelay
from utils.email import AutoChunkedEmailMessage, handle_mail_error


def payment_reminder():
Expand Down Expand Up @@ -70,7 +71,18 @@ def send_reminder_mail(payment):

receivers = not_paid_mail_addresses(payment)

EmailMessage(subject, content, payment.responsible_mail(), [], receivers).send()
email = AutoChunkedEmailMessage(
subject=subject,
body=content,
from_email=payment.responsible_mail(),
to=[],
bcc=receivers,
)
email.send_in_background(
error_callback=lambda e, nse, se: handle_mail_error(
e, nse, se, to=[payment.responsible_mail()]
)
)


def send_missed_payment_mail(payment):
Expand Down
Loading

0 comments on commit e2d879d

Please sign in to comment.