Notification emails #80

Merged
Oleg-Komarov merged 31 commits from notifications into main 2024-04-18 16:11:20 +02:00
Showing only changes of commit 0e0e8df024 - Show all commits

View File

@ -1,5 +1,4 @@
"""Send user notifications as emails, at most once delivery.""" """Send user notifications as emails, at most once delivery."""
from collections import defaultdict
import logging import logging
from django.conf import settings from django.conf import settings
@ -15,37 +14,33 @@ logger.setLevel(logging.INFO)
class Command(BaseCommand): class Command(BaseCommand):
def handle(self, *args, **options): # noqa: D102 def handle(self, *args, **options): # noqa: D102
logger.info('start, checking for outstanding notifications') unprocessed_notifications = Notification.objects.filter(processed_by_mailer_at=None)
notifications = Notification.objects.filter(processed_by_mailer_at=None) for n in unprocessed_notifications:
batched_by_recipient = defaultdict(list) logger.info(f'processing Notification pk={n.pk}')
for n in notifications: n.processed_by_mailer_at = timezone.now()
batched_by_recipient[n.recipient].append(n) recipient = n.recipient
if not recipient.is_subscribed_to_notification_emails:
for recipient, batch in batched_by_recipient.items(): logger.info(f'{recipient} is not subscribed, skipping')
to_send = [] n.save()
for n in batch: continue
n.processed_by_mailer_at = timezone.now() # check that email is confirmed to avoid spamming unsuspecting email owners
if not recipient.is_subscribed_to_notification_emails: if recipient.confirmed_email_at is None:
continue logger.info(f'{recipient} has unconfirmed email, skipping')
# check that email is confirmed to avoid spamming unsuspecting email owners n.save()
if recipient.confirmed_email_at is None: continue
continue n.sent = True
n.sent = True
to_send.append(n)
# first mark as processed, then send: avoid spamming in case of a crash-loop # first mark as processed, then send: avoid spamming in case of a crash-loop
Notification.objects.bulk_update(batch, ['processed_by_mailer_at', 'sent']) n.save()
if len(to_send) > 0: logger.info(f'sending an email to {recipient}: {n.action}')
logger.info(f'sending an email to {recipient} about {len(to_send)} notifications') send_notification_email(n)
send_batch_notification_email(recipient, to_send)
logger.info('finish')
def send_batch_notification_email(recipient, notifications): def send_notification_email(notification):
subject = 'New activity' subject = 'New activity'
message = '\n\n'.join([n.format_email_txt() for n in notifications]) message = notification.format_email_txt()
send_mail( send_mail(
subject, subject,
message, message,
settings.DEFAULT_FROM_EMAIL, settings.DEFAULT_FROM_EMAIL,
[recipient.email], [notification.recipient.email],
) )