Notification emails #80
@ -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:
|
|
||||||
batched_by_recipient[n.recipient].append(n)
|
|
||||||
|
|
||||||
for recipient, batch in batched_by_recipient.items():
|
|
||||||
to_send = []
|
|
||||||
for n in batch:
|
|
||||||
n.processed_by_mailer_at = timezone.now()
|
n.processed_by_mailer_at = timezone.now()
|
||||||
|
recipient = n.recipient
|
||||||
if not recipient.is_subscribed_to_notification_emails:
|
if not recipient.is_subscribed_to_notification_emails:
|
||||||
|
logger.info(f'{recipient} is not subscribed, skipping')
|
||||||
|
n.save()
|
||||||
continue
|
continue
|
||||||
# check that email is confirmed to avoid spamming unsuspecting email owners
|
# check that email is confirmed to avoid spamming unsuspecting email owners
|
||||||
if recipient.confirmed_email_at is None:
|
if recipient.confirmed_email_at is None:
|
||||||
|
logger.info(f'{recipient} has unconfirmed email, skipping')
|
||||||
|
n.save()
|
||||||
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],
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user