summaryrefslogtreecommitdiffstats
path: root/dashboard/src/notifier
diff options
context:
space:
mode:
authorParker Berberian <pberberian@iol.unh.edu>2018-10-24 15:12:32 -0400
committerParker Berberian <pberberian@iol.unh.edu>2018-11-07 10:32:56 -0500
commit81cfb043f06ab71da7c021a063f80f6df58305cc (patch)
tree862b6b8a653298d503ecbf082e747211e6d33442 /dashboard/src/notifier
parentd6e337fa62c32155941333fe8fedc28e4f663700 (diff)
Rewrite Notification subsystem
In this commit: - delete a lot of really bad and / or unused code - redesign a much simpler Notification model - create and send notifications to the user's inbox on booking start & end - migrations - emails user when booking is ready and when it ends Not in this commit: - Creating notifications from lab messages - warning messages when a booking is about to end - creating "summary" notifications when e.g. a booking has been fulfilled by a lab Change-Id: I69b4dc36c3f2bce76d810106baadeef5a562cc7d Signed-off-by: Parker Berberian <pberberian@iol.unh.edu>
Diffstat (limited to 'dashboard/src/notifier')
-rw-r--r--dashboard/src/notifier/admin.py6
-rw-r--r--dashboard/src/notifier/dispatchers.py33
-rw-r--r--dashboard/src/notifier/manager.py181
-rw-r--r--dashboard/src/notifier/migrations/0002_auto_20181102_1631.py44
-rw-r--r--dashboard/src/notifier/models.py40
-rw-r--r--dashboard/src/notifier/views.py11
6 files changed, 161 insertions, 154 deletions
diff --git a/dashboard/src/notifier/admin.py b/dashboard/src/notifier/admin.py
index d3e8be5..4a2984c 100644
--- a/dashboard/src/notifier/admin.py
+++ b/dashboard/src/notifier/admin.py
@@ -9,8 +9,6 @@
from django.contrib import admin
-from notifier.models import *
+from notifier.models import Notification
-admin.site.register(Notifier)
-admin.site.register(MetaBooking)
-admin.site.register(LabMessage)
+admin.site.register(Notification)
diff --git a/dashboard/src/notifier/dispatchers.py b/dashboard/src/notifier/dispatchers.py
deleted file mode 100644
index 1b66b37..0000000
--- a/dashboard/src/notifier/dispatchers.py
+++ /dev/null
@@ -1,33 +0,0 @@
-##############################################################################
-# Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-from django.db.models.signals import pre_save
-from django.dispatch import receiver
-from django.contrib import messages
-from django.core.mail import send_mail
-
-class DispatchHandler():
-
- @receiver(pre_save, sender='notifier.Notifier')
- def dispatch(sender, instance, *args, **kwargs):
- try:
- msg_type = getattr(DispatchHandler, instance.message_type)
- msg_type(instance)
- except AttributeError:
- instance.msg_sent = 'no dispatcher by given name exists: sending by email'
- email(instance)
-
- def email(instance):
- if instance.msg_sent != 'no dispatcher by given name exists: sending by email':
- instance.msg_sent = 'by email'
- send_mail(instance.title,instance.content,
- instance.sender,[instance.user.email_addr], fail_silently=False)
-
- def webnotification(instance):
- instance.msg_sent='by web notification'
diff --git a/dashboard/src/notifier/manager.py b/dashboard/src/notifier/manager.py
index a705d00..cc1aa16 100644
--- a/dashboard/src/notifier/manager.py
+++ b/dashboard/src/notifier/manager.py
@@ -1,98 +1,125 @@
##############################################################################
-# Copyright (c) 2018 Sawyer Bergeron and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
+import os
+from notifier.models import Notification
-from booking.models import *
-from notifier.models import Notifier, MetaBooking, LabMessage
-from django.utils import timezone
-from datetime import timedelta
-from django.template import Template, Context
-from account.models import UserProfile
+from django.core.mail import send_mail
+from django.template.loader import render_to_string
-from django.db import models
-class NotifyPeriodic(object):
- def task():
- bookings_new = Booking.objects.filter(metabooking__isnull=True)
- bookings_old = Booking.objects.filter(end__lte=timezone.now() + timedelta(hours=24)).filter(metabooking__ended_notified=False)
+class NotificationHandler(object):
- for booking in bookings_old:
- metabooking = booking.metabooking
- if booking.end <= timezone.now() + timedelta(hours=24):
- if not metabooking.ending_notified:
- Notify().notify(Notify.TOCLEAN, booking)
- metabooking.ending_notified = True
- metabooking.save()
- if booking.end <= timezone.now():
- metabooking = booking.metabooking
- if not metabooking.ended_notified:
- Notify().notify(Notify.CLEANED, booking)
- metabooking.ended_notified = True
- metabooking.save()
+ @classmethod
+ def notify_new_booking(cls, booking):
+ template = "notifier/new_booking.html"
+ titles = ["You have a new Booking", "You have been added to a Booking"]
+ cls.booking_notify(booking, template, titles)
- for booking in bookings_new:
- metabooking = MetaBooking()
- metabooking.booking = booking
- metabooking.created_notified = True
- metabooking.save()
+ @classmethod
+ def notify_booking_end(cls, booking):
+ template = "notifier/end_booking.html"
+ titles = ["Your booking has ended", "A booking you collaborate on has ended"]
+ cls.booking_notify(booking, template, titles)
- Notify().notify(Notify.CREATED, booking)
+ @classmethod
+ def booking_notify(cls, booking, template, titles):
+ """
+ Creates a notification for a booking owner and collaborators
+ using the template.
+ titles is a list - the first is the title for the owner's notification,
+ the last is the title for the collaborators'
+ """
+ owner_notif = Notification.objects.create(
+ title=titles[0],
+ content=render_to_string(template, context={
+ "booking": booking,
+ "owner": True
+ })
+ )
+ owner_notif.recipients.add(booking.owner)
+ if not booking.collaborators.all().exists():
+ return # no collaborators - were done
+ collab_notif = Notification.objects.create(
+ title=titles[-1],
+ content=render_to_string(template, context={
+ "booking": booking,
+ "owner": False
+ })
+ )
+ for c in booking.collaborators.all():
+ collab_notif.recipients.add(c)
-class Notify(object):
+ @classmethod
+ def email_job_fulfilled(cls, job):
+ template_name = "notifier/email_fulfilled.txt"
+ all_tasks = job.get_tasklist()
+ users = list(job.booking.collaborators.all())
+ users.append(job.booking.owner)
+ for user in users:
+ user_tasklist = []
+ # gather up all the relevant messages from the lab
+ for task in all_tasks:
+ if (not hasattr(task, "user")) or task.user == user:
+ user_tasklist.append({
+ "title": task.type_str + " Message: ",
+ "content": task.message
+ })
+ # gather up all the other needed info
+ context = {
+ "user_name": user.userprofile.full_name,
+ "messages": user_tasklist,
+ "booking_url": os.environ.get("DASHBOARD_URL", "<Dashboard url>") + "/booking/detail/" + str(job.booking.id) + "/"
+ }
- CREATED = "created"
- TOCLEAN = "toclean"
- CLEANED = "cleaned"
+ # render email template
+ message = render_to_string(template_name, context)
- TITLES = {}
- TITLES["created"] = "Your booking has been confirmed"
- TITLES["toclean"] = "Your booking is ending soon"
- TITLES["cleaned"] = "Your booking has ended"
+ # finally, send the email
+ send_mail(
+ "Your Booking is Ready",
+ message,
+ os.environ.get("DEFAULT_FROM_EMAIL", "opnfv@pharos-dashboard"),
+ user.userprofile.email_addr,
+ fail_silently=False
+ )
- """
- Lab message is provided with the following context elements:
- * if is for owner or for collaborator (if owner)
- * recipient username (<owner, collaborator>.username)
- * recipient full name (<owner, collaborator>.userprofile.full_name)
- * booking it pertains to (booking)
- * status message should convey (currently "created", "toclean" and "cleaned" as strings)
- It should be a django template that can be rendered with these context elements
- and should generally use all of them in one way or another.
- It should be applicable to email, the web based general view, and should be scalable for
- all device formats across those mediums.
- """
- def notify(self, notifier_type, booking):
- template = Template(LabMessage.objects.filter(lab=booking.lab).first().msg)
+ @classmethod
+ def email_booking_over(cls, booking):
+ template_name = "notifier/email_ended.txt"
+ hostnames = [host.template.resource.name for host in booking.resource.hosts.all()]
+ users = list(booking.collaborators.all())
+ users.append(booking.owner)
+ for user in users:
+ context = {
+ "user_name": user.userprofile.full_name,
+ "booking": booking,
+ "hosts": hostnames,
+ "booking_url": os.environ.get("DASHBOARD_URL", "<Dashboard url>") + "/booking/detail/" + str(booking.id) + "/"
+ }
- context = {}
- context["owner"] = booking.owner
- context["notify_type"] = notifier_type
- context["booking"] = booking
- message = template.render(Context(context))
- notifier = Notifier()
- notifier.title = self.TITLES[notifier_type]
- notifier.content = message
- notifier.user = booking.owner.userprofile
- notifier.sender = str(booking.lab)
- notifier.save()
- notifier.send()
+ message = render_to_string(template_name, context)
+ send_mail(
+ "Your Booking has Expired",
+ message,
+ os.environ.get("DEFAULT_FROM_EMAIL", "opnfv@pharos-dashboard"),
+ user.userprofile.email_addr,
+ fail_silently=False
+ )
- context["owner"] = False
-
- for user in booking.collaborators.all():
- context["collaborator"] = user
- message = template.render(Context(context))
- notifier = Notifier()
- notifier.title = self.TITLES[notifier_type]
- notifier.content = message
- notifier.user = UserProfile.objects.get(user=user)
- notifier.sender = str(booking.lab)
- notifier.save()
- notifier.send()
+ @classmethod
+ def task_updated(cls, task):
+ """
+ called every time a lab updated info about a task.
+ currently only checks if the job is now done so I can send an email,
+ may add more functionality later
+ """
+ if task.job.is_fulfilled():
+ cls.email_job_fulfilled(task.job)
diff --git a/dashboard/src/notifier/migrations/0002_auto_20181102_1631.py b/dashboard/src/notifier/migrations/0002_auto_20181102_1631.py
new file mode 100644
index 0000000..e5fef89
--- /dev/null
+++ b/dashboard/src/notifier/migrations/0002_auto_20181102_1631.py
@@ -0,0 +1,44 @@
+# Generated by Django 2.1 on 2018-11-02 16:31
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('account', '0003_publicnetwork'),
+ ('notifier', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Notification',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('title', models.CharField(max_length=150)),
+ ('content', models.TextField()),
+ ('recipients', models.ManyToManyField(to='account.UserProfile')),
+ ],
+ ),
+ migrations.RemoveField(
+ model_name='labmessage',
+ name='lab',
+ ),
+ migrations.RemoveField(
+ model_name='metabooking',
+ name='booking',
+ ),
+ migrations.RemoveField(
+ model_name='notifier',
+ name='user',
+ ),
+ migrations.DeleteModel(
+ name='LabMessage',
+ ),
+ migrations.DeleteModel(
+ name='MetaBooking',
+ ),
+ migrations.DeleteModel(
+ name='Notifier',
+ ),
+ ]
diff --git a/dashboard/src/notifier/models.py b/dashboard/src/notifier/models.py
index ed0edeb..5e7c60e 100644
--- a/dashboard/src/notifier/models.py
+++ b/dashboard/src/notifier/models.py
@@ -8,44 +8,16 @@
##############################################################################
from django.db import models
-from booking.models import Booking
from account.models import UserProfile
-from fernet_fields import EncryptedTextField
-from account.models import Lab
-class MetaBooking(models.Model):
- id = models.AutoField(primary_key=True)
- booking = models.OneToOneField(Booking, on_delete=models.CASCADE, related_name="metabooking")
- ending_notified = models.BooleanField(default=False)
- ended_notified = models.BooleanField(default=False)
- created_notified = models.BooleanField(default=False)
-
-
-class LabMessage(models.Model):
- lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
- msg = models.TextField() # django template should be put here
-
-
-class Notifier(models.Model):
- id = models.AutoField(primary_key=True)
- title = models.CharField(max_length=240)
- content = EncryptedTextField()
- user = models.ForeignKey(UserProfile, on_delete=models.CASCADE, null=True, blank=True)
- sender = models.CharField(max_length=240, default='unknown')
- message_type = models.CharField(max_length=240, default='email', choices=(
- ('email', 'Email'),
- ('webnotification', 'Web Notification')))
- msg_sent = ''
+class Notification(models.Model):
+ title = models.CharField(max_length=150)
+ content = models.TextField()
+ recipients = models.ManyToManyField(UserProfile)
def __str__(self):
return self.title
- """
- Implement for next PR: send Notifier by media agreed to by user
- """
- def send(self):
- pass
-
- def getEmail(self):
- return self.user.email_addr
+ def to_preview_html(self):
+ return "<h3>" + self.title + "</h3>" # TODO - template?
diff --git a/dashboard/src/notifier/views.py b/dashboard/src/notifier/views.py
index 026894a..c1a2f7e 100644
--- a/dashboard/src/notifier/views.py
+++ b/dashboard/src/notifier/views.py
@@ -7,28 +7,27 @@
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-from notifier.models import *
+from notifier.models import Notification
from django.shortcuts import render
+
def InboxView(request):
if request.user.is_authenticated:
user = request.user
else:
return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
- return render(request, "notifier/inbox.html", {'notifier_messages': Notifier.objects.filter(user=user.userprofile)})
+ return render(request, "notifier/inbox.html", {'notifications': Notification.objects.filter(recipient=user.userprofile)})
def NotificationView(request, notification_id):
- if notification_id == 0:
- pass
if request.user.is_authenticated:
user = request.user
else:
return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
- notification = Notifier.objects.get(id=notification_id)
- if not notification.user.user.username == user.username:
+ notification = Notification.objects.get(id=notification_id)
+ if user not in notification.recipients:
return render(request, "dashboard/login.html", {'title': 'Access Denied'})
return render(request, "notifier/notification.html", {'notification': notification})