diff options
author | Gergely Csatari <gergely.csatari@nokia.com> | 2023-10-26 10:33:28 +0300 |
---|---|---|
committer | Gergely Csatari <gergely.csatari@nokia.com> | 2023-10-26 10:34:28 +0300 |
commit | 2ec0d7b9f5c1354977b821c6b06c24a3ffa13142 (patch) | |
tree | 6e449d92ddfc880ed007e9d8a8f25bda8fc7cb0f /src/notifier | |
parent | 0d3dd290aa6e7f39e7b0b3cbe448b6622f924240 (diff) |
that the development continues in GitHub
Change-Id: I25c58a679dbf92b2367d826429b7cda936bf9f0e
Signed-off-by: Gergely Csatari <gergely.csatari@nokia.com>
Diffstat (limited to 'src/notifier')
-rw-r--r-- | src/notifier/__init__.py | 8 | ||||
-rw-r--r-- | src/notifier/admin.py | 15 | ||||
-rw-r--r-- | src/notifier/apps.py | 15 | ||||
-rw-r--r-- | src/notifier/manager.py | 162 | ||||
-rw-r--r-- | src/notifier/migrations/0001_initial.py | 47 | ||||
-rw-r--r-- | src/notifier/migrations/0002_auto_20181102_1631.py | 44 | ||||
-rw-r--r-- | src/notifier/migrations/0003_auto_20190123_1741.py | 23 | ||||
-rw-r--r-- | src/notifier/migrations/0004_auto_20190124_2115.py | 23 | ||||
-rw-r--r-- | src/notifier/migrations/0005_auto_20190306_1616.py | 18 | ||||
-rw-r--r-- | src/notifier/migrations/0006_emailed.py | 24 | ||||
-rw-r--r-- | src/notifier/migrations/0007_email.py | 23 | ||||
-rw-r--r-- | src/notifier/migrations/__init__.py | 0 | ||||
-rw-r--r-- | src/notifier/models.py | 56 | ||||
-rw-r--r-- | src/notifier/tasks.py | 51 | ||||
-rw-r--r-- | src/notifier/tests/test_dispatcher.py | 15 | ||||
-rw-r--r-- | src/notifier/tests/test_models.py | 30 | ||||
-rw-r--r-- | src/notifier/urls.py | 19 | ||||
-rw-r--r-- | src/notifier/views.py | 58 |
18 files changed, 0 insertions, 631 deletions
diff --git a/src/notifier/__init__.py b/src/notifier/__init__.py deleted file mode 100644 index d65b13a..0000000 --- a/src/notifier/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -############################################################################## -# 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/src/notifier/admin.py b/src/notifier/admin.py deleted file mode 100644 index f6dbfd1..0000000 --- a/src/notifier/admin.py +++ /dev/null @@ -1,15 +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.contrib import admin - -from notifier.models import Notification, Emailed - -admin.site.register(Notification) -admin.site.register(Emailed) diff --git a/src/notifier/apps.py b/src/notifier/apps.py deleted file mode 100644 index 52902da..0000000 --- a/src/notifier/apps.py +++ /dev/null @@ -1,15 +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.apps import AppConfig - - -class NotifierConfig(AppConfig): - name = 'notifier' diff --git a/src/notifier/manager.py b/src/notifier/manager.py deleted file mode 100644 index e2afdec..0000000 --- a/src/notifier/manager.py +++ /dev/null @@ -1,162 +0,0 @@ -############################################################################## -# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron and others. -# Copyright (c) 2020 Sawyer Bergeron, Sean Smith, 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, Emailed, Email - -from django.template.loader import render_to_string -from django.utils import timezone - - -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 notify_booking_expiring(cls, booking): - template = "notifier/expiring_booking.html" - titles = ["Your booking (" + str(booking.id) + ") is about to expire", "A booking (" + str(booking.id) + ") that you collaborate on is about to expire"] - cls.booking_notify(booking, template, titles) - cls.email_booking_expiring(booking) - - @classmethod - def booking_notify(cls, booking, template, titles): - """ - Create 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.config, "user")) or task.config.user == user: - user_tasklist.append( - { - "title": task.type_str() + " Message: ", - "content": task.message - } - ) - # gather up all the other needed info - context = { - "owner": user == job.booking.owner, - "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, queue email for sending - Email.objects.create(title="Your Booking is Ready", message=message, recipient=user.userprofile.email_addr) - - @classmethod - def email_booking_over(cls, booking): - template_name = "notifier/email_ended.txt" - hostnames = [host.name for host in booking.resource.get_resources()] - 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) - - Email.objects.create(title="Your Booking has Expired", message=message, recipient=user.userprofile.email_addr) - - @classmethod - def email_booking_expiring(cls, booking): - template_name = "notifier/email_expiring.txt" - hostnames = [host.name for host in booking.resource.get_resources()] - 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) - - Email.objects.create(title="Your Booking is Expiring", message=message, recipient=user.userprofile.email_addr) - - @classmethod - def task_updated(cls, task): - """ - Notification of task changing. - - called every time a lab updated info about a task. - sends an email when 'task' changing state means a booking has - just been fulfilled (all tasks done, servers ready to use) - or is over. - """ - if task.job is None or task.job.booking is None: - return - if task.job.is_fulfilled(): - if task.job.booking.end < timezone.now(): - if Emailed.objects.filter(end_booking=task.job.booking).exists(): - return - Emailed.objects.create(end_booking=task.job.booking) - cls.email_booking_over(task.job.booking) - if task.job.booking.end > timezone.now() and task.job.booking.start < timezone.now(): - if Emailed.objects.filter(begin_booking=task.job.booking).exists(): - return - Emailed.objects.create(begin_booking=task.job.booking) - cls.email_job_fulfilled(task.job) diff --git a/src/notifier/migrations/0001_initial.py b/src/notifier/migrations/0001_initial.py deleted file mode 100644 index e5d0009..0000000 --- a/src/notifier/migrations/0001_initial.py +++ /dev/null @@ -1,47 +0,0 @@ -# Generated by Django 2.1 on 2018-09-14 14:48 - -from django.db import migrations, models -import django.db.models.deletion -import fernet_fields.fields - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('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)), - ('title', models.CharField(max_length=240)), - ('content', fernet_fields.fields.EncryptedTextField()), - ('sender', models.CharField(default='unknown', max_length=240)), - ('message_type', models.CharField(choices=[('email', 'Email'), ('webnotification', 'Web Notification')], default='email', max_length=240)), - ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='account.UserProfile')), - ], - ), - ] diff --git a/src/notifier/migrations/0002_auto_20181102_1631.py b/src/notifier/migrations/0002_auto_20181102_1631.py deleted file mode 100644 index e5fef89..0000000 --- a/src/notifier/migrations/0002_auto_20181102_1631.py +++ /dev/null @@ -1,44 +0,0 @@ -# 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/migrations/0003_auto_20190123_1741.py b/src/notifier/migrations/0003_auto_20190123_1741.py deleted file mode 100644 index f491993..0000000 --- a/src/notifier/migrations/0003_auto_20190123_1741.py +++ /dev/null @@ -1,23 +0,0 @@ -# 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/src/notifier/migrations/0004_auto_20190124_2115.py b/src/notifier/migrations/0004_auto_20190124_2115.py deleted file mode 100644 index 306ec7b..0000000 --- a/src/notifier/migrations/0004_auto_20190124_2115.py +++ /dev/null @@ -1,23 +0,0 @@ -# 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/src/notifier/migrations/0005_auto_20190306_1616.py b/src/notifier/migrations/0005_auto_20190306_1616.py deleted file mode 100644 index d92c988..0000000 --- a/src/notifier/migrations/0005_auto_20190306_1616.py +++ /dev/null @@ -1,18 +0,0 @@ -# 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/src/notifier/migrations/0006_emailed.py b/src/notifier/migrations/0006_emailed.py deleted file mode 100644 index 22ba9c5..0000000 --- a/src/notifier/migrations/0006_emailed.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 2.2 on 2019-11-21 18:55 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('booking', '0006_booking_opnfv_config'), - ('notifier', '0005_auto_20190306_1616'), - ] - - operations = [ - migrations.CreateModel( - name='Emailed', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('almost_end_booking', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='warning_mail', to='booking.Booking')), - ('begin_booking', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='begin_mail', to='booking.Booking')), - ('end_booking', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='over_mail', to='booking.Booking')), - ], - ), - ] diff --git a/src/notifier/migrations/0007_email.py b/src/notifier/migrations/0007_email.py deleted file mode 100644 index aaac048..0000000 --- a/src/notifier/migrations/0007_email.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 2.2 on 2020-12-09 20:02 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('notifier', '0006_emailed'), - ] - - operations = [ - migrations.CreateModel( - name='Email', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('sent', models.BooleanField(default=False)), - ('title', models.CharField(max_length=150)), - ('message', models.TextField()), - ('recipient', models.CharField(max_length=150)), - ], - ), - ] diff --git a/src/notifier/migrations/__init__.py b/src/notifier/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/src/notifier/migrations/__init__.py +++ /dev/null diff --git a/src/notifier/models.py b/src/notifier/models.py deleted file mode 100644 index 03e23b3..0000000 --- a/src/notifier/models.py +++ /dev/null @@ -1,56 +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 import models -from account.models import UserProfile -from booking.models import Booking - - -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 to_preview_html(self): - return "<h3>" + self.title + "</h3>" # TODO - template? - - -class Emailed(models.Model): - """A simple record to remember who has already gotten an email to avoid resending.""" - - begin_booking = models.OneToOneField( - Booking, - null=True, - on_delete=models.CASCADE, - related_name="begin_mail" - ) - almost_end_booking = models.OneToOneField( - Booking, - null=True, - on_delete=models.CASCADE, - related_name="warning_mail" - ) - end_booking = models.OneToOneField( - Booking, - null=True, - on_delete=models.CASCADE, - related_name="over_mail" - ) - - -class Email(models.Model): - sent = models.BooleanField(default=False) - title = models.CharField(max_length=150) - message = models.TextField() - recipient = models.CharField(max_length=150) diff --git a/src/notifier/tasks.py b/src/notifier/tasks.py deleted file mode 100644 index 64d7574..0000000 --- a/src/notifier/tasks.py +++ /dev/null @@ -1,51 +0,0 @@ -############################################################################## -# Copyright (c) 2016 Max Breitenfeldt 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 -############################################################################## - - -from celery import shared_task -from django.utils import timezone -from django.conf import settings -from booking.models import Booking -from notifier.models import Emailed, Email -from notifier.manager import NotificationHandler -from django.core.mail import send_mail - -import os - - -@shared_task -def notify_expiring(): - """Notify users if their booking is within 48 hours of expiring.""" - expire_time = timezone.now() + timezone.timedelta(hours=settings.EXPIRE_HOURS) - # Don't email people about bookings that have started recently - start_time = timezone.now() - timezone.timedelta(hours=settings.EXPIRE_LIFETIME) - bookings = Booking.objects.filter( - end__lte=expire_time, - end__gte=timezone.now(), - start__lte=start_time - ) - for booking in bookings: - if Emailed.objects.filter(almost_end_booking=booking).exists(): - continue - NotificationHandler.notify_booking_expiring(booking) - Emailed.objects.create(almost_end_booking=booking) - - -@shared_task -def dispatch_emails(): - for email in Email.objects.filter(sent=False): - email.sent = True - email.save() - send_mail( - email.title, - email.message, - os.environ.get("DEFAULT_FROM_EMAIL", "opnfv@laas-dashboard"), - [email.recipient], - fail_silently=False) diff --git a/src/notifier/tests/test_dispatcher.py b/src/notifier/tests/test_dispatcher.py deleted file mode 100644 index 086f621..0000000 --- a/src/notifier/tests/test_dispatcher.py +++ /dev/null @@ -1,15 +0,0 @@ -############################################################################## -# 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/src/notifier/tests/test_models.py b/src/notifier/tests/test_models.py deleted file mode 100644 index d332254..0000000 --- a/src/notifier/tests/test_models.py +++ /dev/null @@ -1,30 +0,0 @@ -############################################################################## -# 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/src/notifier/urls.py b/src/notifier/urls.py deleted file mode 100644 index 923cc33..0000000 --- a/src/notifier/urls.py +++ /dev/null @@ -1,19 +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.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/src/notifier/views.py b/src/notifier/views.py deleted file mode 100644 index 3a85eda..0000000 --- a/src/notifier/views.py +++ /dev/null @@ -1,58 +0,0 @@ -############################################################################## -# 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}) |