Add per-user Reminder model and adjust sendNotifications to use it

This commit is contained in:
Adam Goldsmith 2020-12-07 18:02:46 -05:00
parent 6640306113
commit 927094ee41
4 changed files with 83 additions and 10 deletions

View File

@ -1,6 +1,6 @@
from django.contrib import admin from django.contrib import admin
from .models import Tool, Task, Event from .models import Tool, Task, Reminder, Event
admin.site.register(Tool) admin.site.register(Tool)
@ -10,4 +10,5 @@ class TaskAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug": ("name",)} prepopulated_fields = {"slug": ("name",)}
admin.site.register(Reminder)
admin.site.register(Event) admin.site.register(Event)

View File

@ -1,20 +1,49 @@
import smtplib import smtplib
from email.message import EmailMessage from email.message import EmailMessage
from itertools import groupby
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from tasks.models import Tool, Task, Event from tasks.models import Tool, Task, Event, Reminder
class Command(BaseCommand): class Command(BaseCommand):
help = 'Sends any notifications for upcoming and overdue tasks' help = 'Sends any notifications for upcoming and overdue tasks'
# TODO: actually send notifications def _send_email(self, to, subject, content):
msg = EmailMessage()
msg.set_content(content)
msg['Subject'] = subject
msg['From'] = 'adam@adamgoldsmith.name'
msg['To'] = to
with smtplib.SMTP("localhost") as server:
server.send_message(msg)
def _active_reminders(self):
for reminder in Reminder.objects.all():
if reminder.should_remind:
yield reminder
def _format_reminder_lines(self, reminders):
for reminder in reminders:
next_date = reminder.task.next_recurrence.strftime('%Y-%m-%d')
yield f" - {reminder.task.name} - {next_date}"
def handle(self, *args, **options): def handle(self, *args, **options):
for tool in Tool.objects.all(): reminders_per_user = {
print(tool.name) user: sorted(reminders, key=lambda r: r.task.next_recurrence)
for task in tool.task_set.all(): for user, reminders in groupby(self._active_reminders(), lambda r: r.user)
print('==>', task.name, 'next:', task.next_recurrence()) }
if task.is_overdue():
for user, reminders in reminders_per_user.items():
self.stdout.write(self.style.SUCCESS( self.stdout.write(self.style.SUCCESS(
f'Sending Notification for task {task.name}')) f'Sending notification for {len(reminders)} task(s) to {user}'))
contents = "The following tasks are upcoming or overdue:\n\n" + \
('\n'.join(self._format_reminder_lines(reminders)))
self._send_email(
user.email,
f'[CMS Tool Maintenance] {len(reminders)} tasks are upcoming or overdue!',
contents)

View File

@ -0,0 +1,25 @@
# Generated by Django 3.1.4 on 2020-12-07 21:13
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('tasks', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Subscription',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('days_before', models.IntegerField()),
('task', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tasks.task')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]

View File

@ -58,6 +58,24 @@ class Task(models.Model):
return next_rec < datetime.now() return next_rec < datetime.now()
class Reminder(models.Model):
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
task = models.ForeignKey(Task, on_delete=models.CASCADE)
days_before = models.IntegerField()
class Meta:
# Django doesn't support multiple-column primary keys
unique_together = (("user", "task"),)
@property
def should_remind(self):
time_until_overdue = self.task.next_recurrence - datetime.now()
return self.task.is_overdue or (time_until_overdue.days <= self.days_before)
def __str__(self):
return f"{self.user}-{self.task}, {self.days_before} day(s)"
class Event(models.Model): class Event(models.Model):
task = models.ForeignKey(Task, on_delete=models.CASCADE) task = models.ForeignKey(Task, on_delete=models.CASCADE)
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE) user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)