Notification emails #80

Merged
Oleg-Komarov merged 31 commits from notifications into main 2024-04-18 16:11:20 +02:00
3 changed files with 17 additions and 10 deletions
Showing only changes of commit eccfbd3baa - Show all commits

View File

@ -1,4 +1,4 @@
# Generated by Django 4.2.11 on 2024-04-12 18:14
# Generated by Django 4.2.11 on 2024-04-16 15:56
from django.conf import settings
from django.db import migrations, models
@ -10,8 +10,8 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('actstream', '0003_add_follow_flag'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('actstream', '0003_add_follow_flag'),
]
operations = [
@ -21,10 +21,12 @@ class Migration(migrations.Migration):
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('recipient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
('action', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='actstream.action')),
('sent', models.BooleanField(default=False)),
('email_sent', models.BooleanField(default=False)),
('processed_by_mailer_at', models.DateTimeField(default=None, null=True)),
('read_at', models.DateTimeField(default=None, null=True)),
],
options={
'indexes': [models.Index(fields=['processed_by_mailer_at'], name='notificatio_process_fc95bc_idx'), models.Index(fields=['recipient', 'read_at'], name='notificatio_recipie_564b1f_idx')],
'unique_together': {('recipient', 'action')},
},
),

View File

@ -10,19 +10,24 @@ User = get_user_model()
class Notification(models.Model):
"""Notification records are created in Action's post_save signal.
When a user marks a notification as read, a record is deleted.
While a notification exists it can be picked up by a send_notifications management command
that runs periodically in background.
If a recipient opted out from receiving notifications of particular type (based on action.verb),
we shouldn't include it in the email, leaving sent=False.
When a user marks a notification as read, read_at is set.
send_notification_emails management command runs periodically in background and sends all
notifications that haven't been processed yet, read_at is not checked when sending emails.
email_sent flag is used only to record the fact that we attempted to send an email.
A user can unsubscribe from notification emails in their profile settings.
"""
recipient = models.ForeignKey(User, null=False, on_delete=models.CASCADE)
action = models.ForeignKey(Action, null=False, on_delete=models.CASCADE)
sent = models.BooleanField(default=False, null=False)
email_sent = models.BooleanField(default=False, null=False)
processed_by_mailer_at = models.DateTimeField(default=None, null=True)
read_at = models.DateTimeField(default=None, null=True)
class Meta:
indexes = [
models.Index(fields=['processed_by_mailer_at']),
models.Index(fields=['recipient', 'read_at']),
]
unique_together = ['recipient', 'action']
def format_email_txt(self):

View File

@ -28,7 +28,7 @@ class Command(BaseCommand):
logger.info(f'{recipient} has unconfirmed email, skipping')
n.save()
continue
n.sent = True
n.email_sent = True
# first mark as processed, then send: avoid spamming in case of a crash-loop
n.save()
logger.info(f'sending an email to {recipient}: {n.action}')