summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTrevor Bramwell <tbramwell@linuxfoundation.org>2018-11-07 22:05:28 +0000
committerGerrit Code Review <gerrit@opnfv.org>2018-11-07 22:05:28 +0000
commit6a90796de1df8d74c79415c39c867ffe6cd80fd0 (patch)
treee192b4149cb535da0a8b928be65f2529a250b5ca /src
parentc589b15fbf1b5f386d0d6af453d2388e1a0ee1ad (diff)
parent7b15aed77c6675286fd75b8832af58c992717ef9 (diff)
Merge "Rewrite Notification subsystem"
Diffstat (limited to 'src')
-rw-r--r--src/api/models.py12
-rw-r--r--src/api/serializers/old_serializers.py7
-rw-r--r--src/api/urls.py1
-rw-r--r--src/api/views.py10
-rw-r--r--src/dashboard/tasks.py9
-rw-r--r--src/notifier/admin.py6
-rw-r--r--src/notifier/dispatchers.py33
-rw-r--r--src/notifier/manager.py181
-rw-r--r--src/notifier/migrations/0002_auto_20181102_1631.py44
-rw-r--r--src/notifier/models.py40
-rw-r--r--src/notifier/views.py11
-rw-r--r--src/pharos_dashboard/settings.py4
-rw-r--r--src/templates/notifier/email_ended.txt21
-rw-r--r--src/templates/notifier/email_fulfilled.txt17
-rw-r--r--src/templates/notifier/end_booking.html36
-rw-r--r--src/templates/notifier/inbox.html2
-rw-r--r--src/templates/notifier/new_booking.html34
-rw-r--r--src/workflow/models.py7
18 files changed, 291 insertions, 184 deletions
diff --git a/src/api/models.py b/src/api/models.py
index 9afc89a..4237559 100644
--- a/src/api/models.py
+++ b/src/api/models.py
@@ -217,6 +217,18 @@ class Job(models.Model):
tasklist += list(cls.objects.filter(job=self).filter(status=status))
return tasklist
+ def is_fulfilled(self):
+ """
+ This method should return true if all of the job's tasks are done,
+ and false otherwise
+ """
+ my_tasks = self.get_tasklist()
+ for task in my_tasks:
+ if task.status != JobStatus.DONE:
+ return False
+ return True
+
+
def get_delta(self, status):
d = {}
j = {}
diff --git a/src/api/serializers/old_serializers.py b/src/api/serializers/old_serializers.py
index f50b90b..0944881 100644
--- a/src/api/serializers/old_serializers.py
+++ b/src/api/serializers/old_serializers.py
@@ -11,13 +11,6 @@
from rest_framework import serializers
from account.models import UserProfile
-from notifier.models import Notifier
-
-
-class NotifierSerializer(serializers.ModelSerializer):
- class Meta:
- model = Notifier
- fields = ('id', 'title', 'content', 'user', 'sender', 'message_type', 'msg_sent')
class UserSerializer(serializers.ModelSerializer):
diff --git a/src/api/urls.py b/src/api/urls.py
index 94f8279..4b1fe40 100644
--- a/src/api/urls.py
+++ b/src/api/urls.py
@@ -32,7 +32,6 @@ from api.views import *
router = routers.DefaultRouter()
router.register(r'bookings', BookingViewSet)
-router.register(r'notifier', NotifierViewSet)
router.register(r'user', UserViewSet)
urlpatterns = [
diff --git a/src/api/views.py b/src/api/views.py
index 072354f..cc3a668 100644
--- a/src/api/views.py
+++ b/src/api/views.py
@@ -21,11 +21,11 @@ from django.views.decorators.csrf import csrf_exempt
import json
from api.serializers.booking_serializer import *
-from api.serializers.old_serializers import NotifierSerializer, UserSerializer
+from api.serializers.old_serializers import UserSerializer
from account.models import UserProfile
from booking.models import Booking
-from notifier.models import Notifier
from api.models import *
+from notifier.manager import NotificationHandler
class BookingViewSet(viewsets.ModelViewSet):
@@ -34,11 +34,6 @@ class BookingViewSet(viewsets.ModelViewSet):
filter_fields = ('resource', 'id')
-class NotifierViewSet(viewsets.ModelViewSet):
- queryset = Notifier.objects.none()
- serializer_class = NotifierSerializer
-
-
class UserViewSet(viewsets.ModelViewSet):
queryset = UserProfile.objects.all()
serializer_class = UserSerializer
@@ -87,6 +82,7 @@ def specific_task(request, lab_name="", job_id="", task_id=""):
if 'message' in request.POST:
task.message = request.POST.get('message')
task.save()
+ NotificationHandler.task_updated(task)
d = {}
d['task'] = task.config.get_delta()
m = {}
diff --git a/src/dashboard/tasks.py b/src/dashboard/tasks.py
index 0f7af1c..d4b4189 100644
--- a/src/dashboard/tasks.py
+++ b/src/dashboard/tasks.py
@@ -13,18 +13,12 @@ from celery import shared_task
from django.utils import timezone
from django.db.models import Q
from booking.models import Booking
-from notifier.manager import *
-from notifier.models import *
+from notifier.manager import NotificationHandler
from api.models import *
from resource_inventory.resource_manager import ResourceManager
@shared_task
-def conjure_aggregate_notifiers():
- NotifyPeriodic.task()
-
-
-@shared_task
def booking_poll():
def cleanup_hardware(qs):
for hostrelation in qs:
@@ -90,6 +84,7 @@ def booking_poll():
cleanup_access(AccessRelation.objects.filter(job=job))
job.complete = True
job.save()
+ NotificationHandler.notify_booking_end(booking)
@shared_task
diff --git a/src/notifier/admin.py b/src/notifier/admin.py
index d3e8be5..4a2984c 100644
--- a/src/notifier/admin.py
+++ b/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/src/notifier/dispatchers.py b/src/notifier/dispatchers.py
deleted file mode 100644
index 1b66b37..0000000
--- a/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/src/notifier/manager.py b/src/notifier/manager.py
index a705d00..cc1aa16 100644
--- a/src/notifier/manager.py
+++ b/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/src/notifier/migrations/0002_auto_20181102_1631.py b/src/notifier/migrations/0002_auto_20181102_1631.py
new file mode 100644
index 0000000..e5fef89
--- /dev/null
+++ b/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/src/notifier/models.py b/src/notifier/models.py
index ed0edeb..5e7c60e 100644
--- a/src/notifier/models.py
+++ b/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/src/notifier/views.py b/src/notifier/views.py
index 026894a..c1a2f7e 100644
--- a/src/notifier/views.py
+++ b/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})
diff --git a/src/pharos_dashboard/settings.py b/src/pharos_dashboard/settings.py
index 7fccb32..91fc93e 100644
--- a/src/pharos_dashboard/settings.py
+++ b/src/pharos_dashboard/settings.py
@@ -194,10 +194,6 @@ CELERYBEAT_SCHEDULE = {
'task': 'dashboard.tasks.free_hosts',
'schedule': timedelta(minutes=1)
},
- 'conjure_notifiers': {
- 'task': 'dashboard.tasks.conjure_aggregate_notifiers',
- 'schedule': timedelta(seconds=30)
- },
}
# Notifier Settings
diff --git a/src/templates/notifier/email_ended.txt b/src/templates/notifier/email_ended.txt
new file mode 100644
index 0000000..7467a0e
--- /dev/null
+++ b/src/templates/notifier/email_ended.txt
@@ -0,0 +1,21 @@
+{{user_name|default:"Developer"}},
+
+The booking you requested of the OPNFV Lab as a Service has ended.
+
+booking information:
+ start: {{booking.start}}
+ end: {{booking.end}}
+ machines:
+ {% for host in hosts %}
+ - {{host}}
+ {% endfor %}
+ purpose: {{booking.purpose}}
+
+You may visit the following link for more information:
+{{booking_url}}
+
+Feel free to create another booking with us!
+
+Thank you for contributing to the OPNFV platform!
+
+ - The Lab-as-a-Service team
diff --git a/src/templates/notifier/email_fulfilled.txt b/src/templates/notifier/email_fulfilled.txt
new file mode 100644
index 0000000..d473961
--- /dev/null
+++ b/src/templates/notifier/email_fulfilled.txt
@@ -0,0 +1,17 @@
+{{user_name|default:"Developer"}},
+
+The booking you requested of the OPNFV Lab as a Service has finished deploying and is ready for you to use.
+
+The lab that fulfilled your booking request has sent you the following messages:
+ {% for message in messages %}
+ {% message.title %}
+ {% message.content %}
+ --------------------
+ {% endfor %}
+
+You may visit the following link for more information:
+{{booking_url}}
+
+Thank you for contributing to the OPNFV platform!
+
+ - The Lab-as-a-Service team
diff --git a/src/templates/notifier/end_booking.html b/src/templates/notifier/end_booking.html
new file mode 100644
index 0000000..22014fb
--- /dev/null
+++ b/src/templates/notifier/end_booking.html
@@ -0,0 +1,36 @@
+<html>
+ <body>
+ <div id="message_content_wrapper">
+ {% if owner %}
+ <h3>Your booking has expired</h3>
+ <p>Your booking has ended and the machines have been cleaned up.</p>
+ <p>Thank you for working on OPNFV, and feel free to book more machines if you need them.</p>
+ {% else %}
+ <h3>A booking that you collaborated on has expired</h3>
+ <p>The booking owned by {{booking.owner.username}} that you worked on has ended</p>
+ <p>Thank you for contributing to OPNFV!</p>
+ {% endif %}
+ <p>Booking information:</p>
+ <ul>
+ <li>owner: {{booking.owner.username}}</li>
+ <li>id: {{booking.id}}</li>
+ <li>lab: {{booking.resource.template.lab.lab_user.username}}</li>
+ <li>resource: {{booking.resource.template.name}}</li>
+ <li>start: {{booking.start}}</li>
+ <li>end: {{booking.end}}</li>
+ <li>purpose: {{booking.purpose}}</li>
+ <li>collaborators:
+ <ul>
+ {% for user in booking.collaborators.all %}
+ <li>user.username</li>
+ {% empty %}
+ <li>No collaborators</li>
+ {% endfor %}
+ </ul>
+ </li>
+ </ul>
+
+ <p>You can find more detailed information <a href=/booking/detail/{{booking.id/>Here</a></p>
+ </div>
+ </body>
+</html>
diff --git a/src/templates/notifier/inbox.html b/src/templates/notifier/inbox.html
index ee0f27a..c0ee1ba 100644
--- a/src/templates/notifier/inbox.html
+++ b/src/templates/notifier/inbox.html
@@ -65,7 +65,7 @@
</div>
<div class="iframe-panel inbox-expanded-view">
<div class="inbox-iframe-div">
- <iframe id="inbox-iframe" src="notification/0" frameBorder="0" width="100%" height="100vh" scrolling="yes" onload="sizetoiframe(this);">Please select a notification</iframe>
+ <iframe id="inbox-iframe" frameBorder="0" width="100%" height="100vh" scrolling="yes" onload="sizetoiframe(this);">Please select a notification</iframe>
</div>
</div>
</div>
diff --git a/src/templates/notifier/new_booking.html b/src/templates/notifier/new_booking.html
new file mode 100644
index 0000000..4b53875
--- /dev/null
+++ b/src/templates/notifier/new_booking.html
@@ -0,0 +1,34 @@
+<html>
+ <body>
+ <div id="message_content_wrapper">
+ {% if owner %}
+ <h3>You have created a new booking</h3>
+ <p>We have recieved your booking request and will start working on it right away.</p>
+ {% else %}
+ <h3>You have been added as a collaborator to a booking</h3>
+ <p>{{booking.owner.username}} has given you access to thier booking.</p>
+ {% endif %}
+ <p>Booking information:</p>
+ <ul>
+ <li>owner: {{booking.owner.username}}</li>
+ <li>id: {{booking.id}}</li>
+ <li>lab: {{booking.resource.template.lab.lab_user.username}}</li>
+ <li>resource: {{booking.resource.template.name}}</li>
+ <li>start: {{booking.start}}</li>
+ <li>end: {{booking.end}}</li>
+ <li>purpose: {{booking.purpose}}</li>
+ <li>collaborators:
+ <ul>
+ {% for user in booking.collaborators.all %}
+ <li>user.username</li>
+ {% empty %}
+ <li>No collaborators</li>
+ {% endfor %}
+ </ul>
+ </li>
+ </ul>
+
+ <p>You can find more detailed information <a href=/booking/detail/{{booking.id/>Here</a></p>
+ </div>
+ </body>
+</html>
diff --git a/src/workflow/models.py b/src/workflow/models.py
index e862957..e5a23b2 100644
--- a/src/workflow/models.py
+++ b/src/workflow/models.py
@@ -14,8 +14,6 @@ from django.shortcuts import render
from django.contrib import messages
import yaml
-import json
-import traceback
import requests
from workflow.forms import ConfirmationForm
@@ -23,6 +21,7 @@ from api.models import *
from dashboard.exceptions import *
from resource_inventory.models import *
from resource_inventory.resource_manager import ResourceManager
+from notifier.manager import NotificationHandler
class BookingAuthManager():
@@ -282,6 +281,9 @@ class Repository():
errors = self.make_booking()
if errors:
return errors
+ # create notification
+ booking = self.el[self.BOOKING_MODELS]['booking']
+ NotificationHandler.notify_new_booking(booking)
def make_snapshot(self):
@@ -465,7 +467,6 @@ class Repository():
for collaborator in collaborators:
booking.collaborators.add(collaborator)
-
try:
JobFactory.makeCompleteJob(booking)
except Exception as e: