summaryrefslogtreecommitdiffstats
path: root/dashboard/src/notifier
diff options
context:
space:
mode:
Diffstat (limited to 'dashboard/src/notifier')
-rw-r--r--dashboard/src/notifier/__init__.py8
-rw-r--r--dashboard/src/notifier/admin.py6
-rw-r--r--dashboard/src/notifier/apps.py3
-rw-r--r--dashboard/src/notifier/dispatchers.py33
-rw-r--r--dashboard/src/notifier/manager.py133
-rw-r--r--dashboard/src/notifier/migrations/0001_initial.py25
-rw-r--r--dashboard/src/notifier/migrations/0002_auto_20181102_1631.py44
-rw-r--r--dashboard/src/notifier/migrations/0003_auto_20190123_1741.py23
-rw-r--r--dashboard/src/notifier/migrations/0004_auto_20190124_2115.py23
-rw-r--r--dashboard/src/notifier/migrations/0005_auto_20190306_1616.py18
-rw-r--r--dashboard/src/notifier/models.py31
-rw-r--r--dashboard/src/notifier/tests/test_dispatcher.py15
-rw-r--r--dashboard/src/notifier/tests/test_models.py30
-rw-r--r--dashboard/src/notifier/urls.py19
-rw-r--r--dashboard/src/notifier/views.py58
15 files changed, 406 insertions, 63 deletions
diff --git a/dashboard/src/notifier/__init__.py b/dashboard/src/notifier/__init__.py
index e69de29..d65b13a 100644
--- a/dashboard/src/notifier/__init__.py
+++ b/dashboard/src/notifier/__init__.py
@@ -0,0 +1,8 @@
+##############################################################################
+# Copyright (c) 2018 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
+##############################################################################
diff --git a/dashboard/src/notifier/admin.py b/dashboard/src/notifier/admin.py
index cfbe778..4a2984c 100644
--- a/dashboard/src/notifier/admin.py
+++ b/dashboard/src/notifier/admin.py
@@ -1,5 +1,5 @@
##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
+# 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
@@ -9,6 +9,6 @@
from django.contrib import admin
-from notifier.models import *
+from notifier.models import Notification
-admin.site.register(Notifier)
+admin.site.register(Notification)
diff --git a/dashboard/src/notifier/apps.py b/dashboard/src/notifier/apps.py
index da5d3b0..52902da 100644
--- a/dashboard/src/notifier/apps.py
+++ b/dashboard/src/notifier/apps.py
@@ -1,5 +1,5 @@
##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
+# 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
@@ -7,6 +7,7 @@
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
+
from django.apps import AppConfig
diff --git a/dashboard/src/notifier/dispatchers.py b/dashboard/src/notifier/dispatchers.py
deleted file mode 100644
index c35fe2b..0000000
--- a/dashboard/src/notifier/dispatchers.py
+++ /dev/null
@@ -1,33 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt 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' \ No newline at end of file
diff --git a/dashboard/src/notifier/manager.py b/dashboard/src/notifier/manager.py
new file mode 100644
index 0000000..240cf85
--- /dev/null
+++ b/dashboard/src/notifier/manager.py
@@ -0,0 +1,133 @@
+##############################################################################
+# 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 django.core.mail import send_mail
+from django.template.loader import render_to_string
+
+
+class NotificationHandler(object):
+
+ @classmethod
+ def notify_new_booking(cls, booking):
+ template = "notifier/new_booking.html"
+ titles = ["You have a new booking (" + str(booking.id) + ")", "You have been added to a booking (" + str(booking.id) + ")"]
+ cls.booking_notify(booking, template, titles)
+
+ @classmethod
+ def notify_booking_end(cls, booking):
+ template = "notifier/end_booking.html"
+ titles = ["Your booking (" + str(booking.id) + ") has ended", "A booking (" + str(booking.id) + ") that you collaborate on has ended"]
+ cls.booking_notify(booking, template, titles)
+
+ @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.userprofile)
+ 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.userprofile)
+
+ @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) + "/"
+ }
+
+ # render email template
+ message = render_to_string(template_name, context)
+
+ # 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
+ )
+
+ @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) + "/"
+ }
+
+ 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
+ )
+
+ @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/0001_initial.py b/dashboard/src/notifier/migrations/0001_initial.py
index cac4d04..e5d0009 100644
--- a/dashboard/src/notifier/migrations/0001_initial.py
+++ b/dashboard/src/notifier/migrations/0001_initial.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2017-12-14 21:41
-from __future__ import unicode_literals
+# Generated by Django 2.1 on 2018-09-14 14:48
from django.db import migrations, models
import django.db.models.deletion
@@ -12,11 +10,30 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
- ('dashboard', '0002_auto_20170505_0815'),
+ ('account', '0001_initial'),
+ ('booking', '0001_initial'),
]
operations = [
migrations.CreateModel(
+ name='LabMessage',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('msg', models.TextField()),
+ ('lab', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='account.Lab')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='MetaBooking',
+ fields=[
+ ('id', models.AutoField(primary_key=True, serialize=False)),
+ ('ending_notified', models.BooleanField(default=False)),
+ ('ended_notified', models.BooleanField(default=False)),
+ ('created_notified', models.BooleanField(default=False)),
+ ('booking', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='metabooking', to='booking.Booking')),
+ ],
+ ),
+ migrations.CreateModel(
name='Notifier',
fields=[
('id', models.AutoField(primary_key=True, serialize=False)),
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/migrations/0003_auto_20190123_1741.py b/dashboard/src/notifier/migrations/0003_auto_20190123_1741.py
new file mode 100644
index 0000000..f491993
--- /dev/null
+++ b/dashboard/src/notifier/migrations/0003_auto_20190123_1741.py
@@ -0,0 +1,23 @@
+# Generated by Django 2.1 on 2019-01-23 17:41
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('notifier', '0002_auto_20181102_1631'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='notification',
+ name='is_html',
+ field=models.BooleanField(default=True),
+ ),
+ migrations.AddField(
+ model_name='notification',
+ name='is_read',
+ field=models.BooleanField(default=True),
+ ),
+ ]
diff --git a/dashboard/src/notifier/migrations/0004_auto_20190124_2115.py b/dashboard/src/notifier/migrations/0004_auto_20190124_2115.py
new file mode 100644
index 0000000..306ec7b
--- /dev/null
+++ b/dashboard/src/notifier/migrations/0004_auto_20190124_2115.py
@@ -0,0 +1,23 @@
+# Generated by Django 2.1 on 2019-01-24 21:15
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('account', '0003_publicnetwork'),
+ ('notifier', '0003_auto_20190123_1741'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='notification',
+ name='is_read',
+ ),
+ migrations.AddField(
+ model_name='notification',
+ name='read_by',
+ field=models.ManyToManyField(related_name='read_notifications', to='account.UserProfile'),
+ ),
+ ]
diff --git a/dashboard/src/notifier/migrations/0005_auto_20190306_1616.py b/dashboard/src/notifier/migrations/0005_auto_20190306_1616.py
new file mode 100644
index 0000000..d92c988
--- /dev/null
+++ b/dashboard/src/notifier/migrations/0005_auto_20190306_1616.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.1 on 2019-03-06 16:16
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('notifier', '0004_auto_20190124_2115'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='notification',
+ name='recipients',
+ field=models.ManyToManyField(related_name='notifications', to='account.UserProfile'),
+ ),
+ ]
diff --git a/dashboard/src/notifier/models.py b/dashboard/src/notifier/models.py
index 9ebc6fc..49189e8 100644
--- a/dashboard/src/notifier/models.py
+++ b/dashboard/src/notifier/models.py
@@ -1,5 +1,5 @@
##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
+# 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
@@ -8,31 +8,18 @@
##############################################################################
from django.db import models
-from jira import JIRA, JIRAError
-from dashboard.models import Resource
-from booking.models import Booking
-from django.contrib.auth.models import User
from account.models import UserProfile
-from django.contrib import messages
-from django.db.models.signals import pre_save
-from fernet_fields import EncryptedTextField
-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 = ''
- import notifier.dispatchers
+class Notification(models.Model):
+ title = models.CharField(max_length=150)
+ content = models.TextField()
+ recipients = models.ManyToManyField(UserProfile, related_name='notifications')
+ is_html = models.BooleanField(default=True)
+ read_by = models.ManyToManyField(UserProfile, related_name='read_notifications')
def __str__(self):
return self.title
- 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/tests/test_dispatcher.py b/dashboard/src/notifier/tests/test_dispatcher.py
new file mode 100644
index 0000000..086f621
--- /dev/null
+++ b/dashboard/src/notifier/tests/test_dispatcher.py
@@ -0,0 +1,15 @@
+##############################################################################
+# Copyright (c) 2018 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
+##############################################################################
+
+from django.test import TestCase
+
+
+class DispatchTestCase(TestCase):
+ # This is a stub, it will be filled out as this feature is remade with saner practices.
+ pass
diff --git a/dashboard/src/notifier/tests/test_models.py b/dashboard/src/notifier/tests/test_models.py
new file mode 100644
index 0000000..d332254
--- /dev/null
+++ b/dashboard/src/notifier/tests/test_models.py
@@ -0,0 +1,30 @@
+##############################################################################
+# Copyright (c) 2018 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
+##############################################################################
+
+
+from django.test import TestCase
+from notifier.models import Notifier
+from django.contrib.auth.models import User
+
+
+class NotifierTestCase(TestCase):
+
+ def test_valid_notifier_saves(self):
+
+ sender = User.objects.create()
+ recipient = User.objects.create()
+ self.assertTrue(
+ Notifier.objects.create(
+ title='notification title',
+ content='notification body',
+ user=recipient,
+ sender=sender,
+ message_type='email'
+ )
+ )
diff --git a/dashboard/src/notifier/urls.py b/dashboard/src/notifier/urls.py
new file mode 100644
index 0000000..fedb9e8
--- /dev/null
+++ b/dashboard/src/notifier/urls.py
@@ -0,0 +1,19 @@
+##############################################################################
+# 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.conf.urls import url
+
+from notifier.views import InboxView, NotificationView
+
+app_name = "notifier"
+urlpatterns = [
+ url(r'^$', InboxView, name='messages'),
+ url(r'^notification/(?P<notification_id>[0-9]+)/$', NotificationView, name='notifier_single')
+]
diff --git a/dashboard/src/notifier/views.py b/dashboard/src/notifier/views.py
new file mode 100644
index 0000000..3a85eda
--- /dev/null
+++ b/dashboard/src/notifier/views.py
@@ -0,0 +1,58 @@
+##############################################################################
+# Copyright (c) 2018 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
+##############################################################################
+
+from django.shortcuts import render
+from notifier.models import Notification
+from django.db.models import Q
+
+
+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",
+ {'unread_notifications': Notification.objects.filter(recipients=user.userprofile).order_by('-id').filter(~Q(read_by=user.userprofile)),
+ 'read_notifications': Notification.objects.filter(recipients=user.userprofile).order_by('-id').filter(read_by=user.userprofile)})
+
+
+def NotificationView(request, notification_id):
+
+ if request.user.is_authenticated:
+ user = request.user
+ else:
+ return render(request,
+ "dashboard/login.html",
+ {'title': 'Authentication Required'})
+
+ notification = Notification.objects.get(id=notification_id)
+ if user.userprofile not in notification.recipients.all():
+ return render(request,
+ "dashboard/login.html", {'title': 'Access Denied'})
+
+ notification.read_by.add(user.userprofile)
+ notification.save()
+ if request.method == 'POST':
+ if 'delete' in request.POST:
+ # handle deleting
+ notification.recipients.remove(user.userprofile)
+ if not notification.recipients.exists():
+ notification.delete()
+ else:
+ notification.save()
+
+ if 'unread' in request.POST:
+ notification.read_by.remove(user.userprofile)
+ notification.save()
+
+ return render(request,
+ "notifier/notification.html", {'notification': notification})