diff options
Diffstat (limited to 'dashboard')
29 files changed, 190 insertions, 201 deletions
diff --git a/dashboard/config.env.sample b/dashboard/config.env.sample index b370fe2..edd2bf0 100644 --- a/dashboard/config.env.sample +++ b/dashboard/config.env.sample @@ -25,3 +25,9 @@ RABBITMQ_PASSWORD=opnfvopnfv #Jenkins Build Server JENKINS_URL=https://build.opnfv.org/ci + +# Email Settings +EMAIL_HOST= +EMAIL_PORT= +EMAIL_HOST_USER= +EMAIL_HOST_PASSWORD= diff --git a/dashboard/src/account/migrations/0002_userprofile_email_addr.py b/dashboard/src/account/migrations/0002_userprofile_email_addr.py new file mode 100644 index 0000000..bfbed17 --- /dev/null +++ b/dashboard/src/account/migrations/0002_userprofile_email_addr.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10 on 2017-12-14 20:37 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='userprofile', + name='email_addr', + field=models.CharField(default='email@mail.com', max_length=300), + ), + ] diff --git a/dashboard/src/account/models.py b/dashboard/src/account/models.py index bfc0bbe..47b012b 100644 --- a/dashboard/src/account/models.py +++ b/dashboard/src/account/models.py @@ -20,7 +20,7 @@ class UserProfile(models.Model): timezone = models.CharField(max_length=100, blank=False, default='UTC') ssh_public_key = models.FileField(upload_to=upload_to, null=True, blank=True) pgp_public_key = models.FileField(upload_to=upload_to, null=True, blank=True) - email_addr = models.CharField(max_length=300, blank=false, default='email@mail.com') + email_addr = models.CharField(max_length=300, blank=False, default='email@mail.com') company = models.CharField(max_length=200, blank=False) oauth_token = models.CharField(max_length=1024, blank=False) diff --git a/dashboard/src/api/serializers.py b/dashboard/src/api/serializers.py index 4478175..c371a92 100644 --- a/dashboard/src/api/serializers.py +++ b/dashboard/src/api/serializers.py @@ -10,16 +10,18 @@ from rest_framework import serializers +from notifier.models import Notifier from booking.models import Booking from dashboard.models import Server, Resource, ResourceStatus class BookingSerializer(serializers.ModelSerializer): installer_name = serializers.CharField(source='installer.name') scenario_name = serializers.CharField(source='scenario.name') + opsys_name = serializers.CharField(source='opsys.name') class Meta: model = Booking - fields = ('id', 'resource_id', 'start', 'end', 'installer_name', 'scenario_name', 'purpose') + fields = ('id', 'resource_id', 'start', 'end', 'opsys_name', 'installer_name', 'scenario_name', 'purpose') class ServerSerializer(serializers.ModelSerializer): @@ -37,3 +39,8 @@ class ResourceStatusSerializer(serializers.ModelSerializer): class Meta: model = ResourceStatus fields = ('id', 'resource', 'timestamp','type', 'title', 'content') + +class NotifierSerializer(serializers.ModelSerializer): + class Meta: + model = Notifier + fields = ('id', 'title', 'content', 'user', 'sender', 'message_type', 'msg_sent') diff --git a/dashboard/src/api/urls.py b/dashboard/src/api/urls.py index a4a4b2f..71cd3ef 100644 --- a/dashboard/src/api/urls.py +++ b/dashboard/src/api/urls.py @@ -33,8 +33,9 @@ router.register(r'resources', ResourceViewSet) router.register(r'servers', ServerViewSet) router.register(r'bookings', BookingViewSet) router.register(r'resource_status', ResourceStatusViewSet) +router.register(r'notifier', NotifierViewSet) urlpatterns = [ url(r'^', include(router.urls)), url(r'^token$', GenerateTokenView.as_view(), name='generate_token'), -]
\ No newline at end of file +] diff --git a/dashboard/src/api/views.py b/dashboard/src/api/views.py index 84fa1b5..c16d57d 100644 --- a/dashboard/src/api/views.py +++ b/dashboard/src/api/views.py @@ -8,7 +8,7 @@ ############################################################################## -from django.contrib.auth.decorators import login_required +from django.contrib.auth.decorators import login_required, user_passes_test from django.shortcuts import redirect from django.utils.decorators import method_decorator from django.views import View @@ -41,6 +41,9 @@ class ResourceStatusViewSet(viewsets.ModelViewSet): queryset = ResourceStatus.objects.all() serializer_class = ResourceStatusSerializer +class NotifierViewSet(viewsets.ModelViewSet): + queryset = Notifier.objects.none() + serializer_class = NotifierSerializer @method_decorator(login_required, name='dispatch') class GenerateTokenView(View): diff --git a/dashboard/src/booking/admin.py b/dashboard/src/booking/admin.py index d883be1..51e1031 100644 --- a/dashboard/src/booking/admin.py +++ b/dashboard/src/booking/admin.py @@ -13,5 +13,6 @@ from django.contrib import admin from booking.models import * admin.site.register(Booking) +admin.site.register(Opsys) admin.site.register(Installer) -admin.site.register(Scenario)
\ No newline at end of file +admin.site.register(Scenario) diff --git a/dashboard/src/booking/forms.py b/dashboard/src/booking/forms.py index 2dbfacb..1f09c05 100644 --- a/dashboard/src/booking/forms.py +++ b/dashboard/src/booking/forms.py @@ -10,14 +10,15 @@ import django.forms as forms -from booking.models import Installer, Scenario +from booking.models import Installer, Scenario, Opsys class BookingForm(forms.Form): - fields = ['start', 'end', 'purpose', 'installer', 'scenario'] + fields = ['start', 'end', 'purpose', 'opsys', 'installer', 'scenario'] start = forms.DateTimeField() end = forms.DateTimeField() purpose = forms.CharField(max_length=300) + opsys = forms.ModelChoiceField(queryset=Opsys.objects.all(), required=False) installer = forms.ModelChoiceField(queryset=Installer.objects.all(), required=False) - scenario = forms.ModelChoiceField(queryset=Scenario.objects.all(), required=False)
\ No newline at end of file + scenario = forms.ModelChoiceField(queryset=Scenario.objects.all(), required=False) diff --git a/dashboard/src/notification/migrations/0001_initial.py b/dashboard/src/booking/migrations/0002_auto_20171213_1506.py index 8b8414e..3e0a5fa 100644 --- a/dashboard/src/notification/migrations/0001_initial.py +++ b/dashboard/src/booking/migrations/0002_auto_20171213_1506.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.10 on 2016-11-03 13:33 +# Generated by Django 1.10 on 2017-12-13 15:06 from __future__ import unicode_literals from django.db import migrations, models @@ -8,21 +8,21 @@ import django.db.models.deletion class Migration(migrations.Migration): - initial = True - dependencies = [ ('booking', '0001_initial'), ] operations = [ migrations.CreateModel( - name='BookingNotification', + name='Opsys', fields=[ ('id', models.AutoField(primary_key=True, serialize=False)), - ('type', models.CharField(max_length=100)), - ('submit_time', models.DateTimeField()), - ('submitted', models.BooleanField(default=False)), - ('booking', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='booking.Booking')), + ('name', models.CharField(max_length=100)), ], ), + migrations.AddField( + model_name='booking', + name='opsys', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='booking.Opsys'), + ), ] diff --git a/dashboard/src/booking/models.py b/dashboard/src/booking/models.py index 0b3fa3b..0bf5961 100644 --- a/dashboard/src/booking/models.py +++ b/dashboard/src/booking/models.py @@ -31,6 +31,12 @@ class Scenario(models.Model): def __str__(self): return self.name +class Opsys(models.Model): + id = models.AutoField(primary_key=True) + name = models.CharField(max_length=100) + + def __str__(self): + return self.name class Booking(models.Model): id = models.AutoField(primary_key=True) @@ -41,6 +47,7 @@ class Booking(models.Model): jira_issue_id = models.IntegerField(null=True) jira_issue_status = models.CharField(max_length=50) + opsys = models.ForeignKey(Opsys, models.DO_NOTHING, null=True) installer = models.ForeignKey(Installer, models.DO_NOTHING, null=True) scenario = models.ForeignKey(Scenario, models.DO_NOTHING, null=True) purpose = models.CharField(max_length=300, blank=False) diff --git a/dashboard/src/booking/views.py b/dashboard/src/booking/views.py index 6fdca0e..da2fe11 100644 --- a/dashboard/src/booking/views.py +++ b/dashboard/src/booking/views.py @@ -70,6 +70,7 @@ class BookingFormView(FormView): booking = Booking(start=form.cleaned_data['start'], end=form.cleaned_data['end'], purpose=form.cleaned_data['purpose'], + opsys=form.cleaned_data['opsys'], installer=form.cleaned_data['installer'], scenario=form.cleaned_data['scenario'], resource=self.resource, user=user) @@ -117,6 +118,6 @@ class ResourceBookingsJSON(View): def get(self, request, *args, **kwargs): resource = get_object_or_404(Resource, id=self.kwargs['resource_id']) bookings = resource.booking_set.get_queryset().values('id', 'start', 'end', 'purpose', - 'jira_issue_status', + 'jira_issue_status', 'opsys__name', 'installer__name', 'scenario__name') return JsonResponse({'bookings': list(bookings)}) diff --git a/dashboard/src/notification/__init__.py b/dashboard/src/notification/__init__.py deleted file mode 100644 index 37dcbdd..0000000 --- a/dashboard/src/notification/__init__.py +++ /dev/null @@ -1,11 +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 -############################################################################## - - -default_app_config = 'notification.apps.NotificationConfig'
\ No newline at end of file diff --git a/dashboard/src/notification/migrations/__init__.py b/dashboard/src/notification/migrations/__init__.py deleted file mode 100644 index b5914ce..0000000 --- a/dashboard/src/notification/migrations/__init__.py +++ /dev/null @@ -1,10 +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 -############################################################################## - - diff --git a/dashboard/src/notification/models.py b/dashboard/src/notification/models.py deleted file mode 100644 index 89b3023..0000000 --- a/dashboard/src/notification/models.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 import models - -class BookingNotification(models.Model): - id = models.AutoField(primary_key=True) - type = models.CharField(max_length=100) - booking = models.ForeignKey('booking.Booking', on_delete=models.CASCADE) - submit_time = models.DateTimeField() - submitted = models.BooleanField(default=False) - - def get_content(self): - return { - 'resource_id': self.booking.resource.id, - 'booking_id': self.booking.id, - 'user': self.booking.user.username, - 'user_id': self.booking.user.id, - } - - def save(self, *args, **kwargs): - notifications = self.booking.bookingnotification_set.filter(type=self.type).exclude( - id=self.id) - #if notifications.count() > 0: - # raise ValueError('Doubled Notification') - return super(BookingNotification, self).save(*args, **kwargs) diff --git a/dashboard/src/notification/signals.py b/dashboard/src/notification/signals.py deleted file mode 100644 index 936c25b..0000000 --- a/dashboard/src/notification/signals.py +++ /dev/null @@ -1,25 +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 post_save -from django.dispatch import receiver - -from booking.models import Booking -from notification.models import BookingNotification - - -@receiver(post_save, sender=Booking) -def booking_notification_handler(sender, instance, **kwargs): - BookingNotification.objects.update_or_create( - booking=instance, type='booking_start', defaults={'submit_time': instance.start} - ) - BookingNotification.objects.update_or_create( - booking=instance, type='booking_end', defaults={'submit_time': instance.end} - )
\ No newline at end of file diff --git a/dashboard/src/notification/tasks.py b/dashboard/src/notification/tasks.py deleted file mode 100644 index 7f73762..0000000 --- a/dashboard/src/notification/tasks.py +++ /dev/null @@ -1,49 +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 -############################################################################## - - -import os -import sys -from datetime import timedelta - -from celery import shared_task -from django.conf import settings -from django.utils import timezone - -from notification.models import BookingNotification - -# this adds the top level directory to the python path, this is needed so that we can access the -# notification library -sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..')) - -from dashboard_notification.notification import Notification, Message - - -@shared_task -def send_booking_notifications(): - with Notification(dashboard_url=settings.RABBITMQ_URL, user=settings.RABBITMQ_USER, password=settings.RABBITMQ_PASSWORD) as messaging: - now = timezone.now() - notifications = BookingNotification.objects.filter(submitted=False, - submit_time__gt=now - timedelta(minutes=1), - submit_time__lt=now + timedelta(minutes=5)) - for notification in notifications: - message = Message(type=notification.type, topic=notification.booking.resource.name, - content=notification.get_content()) - messaging.send(message) - notification.submitted = True - notification.save() - -@shared_task -def notification_debug(): - with Notification(dashboard_url=settings.RABBITMQ_URL) as messaging: - notifications = BookingNotification.objects.all() - for notification in notifications: - message = Message(type=notification.type, topic=notification.booking.resource.name, - content=notification.get_content()) - messaging.send(message) diff --git a/dashboard/src/notification/tests.py b/dashboard/src/notification/tests.py deleted file mode 100644 index 9df9aa6..0000000 --- a/dashboard/src/notification/tests.py +++ /dev/null @@ -1,41 +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 datetime import timedelta -from unittest import TestCase - -from django.contrib.auth.models import User -from django.utils import timezone - -from booking.models import Booking -from dashboard.models import Resource -from jenkins.models import JenkinsSlave -from notification.models import * - - -class JenkinsModelTestCase(TestCase): - def setUp(self): - self.slave = JenkinsSlave.objects.create(name='test1', url='test') - self.res1 = Resource.objects.create(name='res1', slave=self.slave, description='x', - url='x') - self.user1 = User.objects.create(username='user1') - - start = timezone.now() - end = start + timedelta(days=1) - self.booking = Booking.objects.create(start=start, end=end, purpose='test', - resource=self.res1, user=self.user1) - - def test_booking_notification(self): - BookingNotification.objects.create(type='test', booking=self.booking, - submit_time=timezone.now()) - - self.assertRaises(ValueError, BookingNotification.objects.create, type='test', - booking=self.booking, - submit_time=timezone.now()) diff --git a/dashboard/src/notifier/__init__.py b/dashboard/src/notifier/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dashboard/src/notifier/__init__.py diff --git a/dashboard/src/notification/admin.py b/dashboard/src/notifier/admin.py index bcaa1ab..cfbe778 100644 --- a/dashboard/src/notification/admin.py +++ b/dashboard/src/notifier/admin.py @@ -7,11 +7,8 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## - -from django.conf import settings from django.contrib import admin -from notification.models import BookingNotification +from notifier.models import * -if settings.DEBUG: - admin.site.register(BookingNotification)
\ No newline at end of file +admin.site.register(Notifier) diff --git a/dashboard/src/notification/apps.py b/dashboard/src/notifier/apps.py index 2de22c4..da5d3b0 100644 --- a/dashboard/src/notification/apps.py +++ b/dashboard/src/notifier/apps.py @@ -7,12 +7,8 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## - from django.apps import AppConfig -class NotificationConfig(AppConfig): - name = 'notification' - - def ready(self): - import notification.signals #noqa
\ No newline at end of file +class NotifierConfig(AppConfig): + name = 'notifier' diff --git a/dashboard/src/notifier/dispatchers.py b/dashboard/src/notifier/dispatchers.py new file mode 100644 index 0000000..64be2a5 --- /dev/null +++ b/dashboard/src/notifier/dispatchers.py @@ -0,0 +1,34 @@ +############################################################################## +# 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 + + '\n\n This message pertains to the following resource: ' + + instance.resource.name,instance.sender,[instance.user.email_addr], fail_silently=False) + + def webnotification(instance): + instance.msg_sent='by web notification' diff --git a/dashboard/src/notifier/migrations/0001_initial.py b/dashboard/src/notifier/migrations/0001_initial.py new file mode 100644 index 0000000..78d2bad --- /dev/null +++ b/dashboard/src/notifier/migrations/0001_initial.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10 on 2017-12-14 21:41 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import fernet_fields.fields + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('account', '0002_userprofile_email_addr'), + ('dashboard', '0002_auto_20170505_0815'), + ] + + operations = [ + 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/dashboard/src/notifier/migrations/__init__.py b/dashboard/src/notifier/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dashboard/src/notifier/migrations/__init__.py diff --git a/dashboard/src/notifier/models.py b/dashboard/src/notifier/models.py new file mode 100644 index 0000000..9ebc6fc --- /dev/null +++ b/dashboard/src/notifier/models.py @@ -0,0 +1,38 @@ +############################################################################## +# 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 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 + + def __str__(self): + return self.title + + def getEmail(self): + return self.user.email_addr + diff --git a/dashboard/src/pharos_dashboard/settings.py b/dashboard/src/pharos_dashboard/settings.py index 83ad172..240f68e 100644 --- a/dashboard/src/pharos_dashboard/settings.py +++ b/dashboard/src/pharos_dashboard/settings.py @@ -14,7 +14,7 @@ INSTALLED_APPS = [ 'booking', 'account', 'jenkins', - 'notification', + 'notifier', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', @@ -188,3 +188,9 @@ CI_SLAVES_URL = os.environ['JENKINS_URL'] + '/label/ci-pod/api/json?tree=nodes[n ALL_JOBS_URL = os.environ['JENKINS_URL'] + '/api/json?tree=jobs[displayName,url,lastBuild[fullDisplayName,building,builtOn,timestamp,result]' GET_SLAVE_URL = os.environ['JENKINS_URL'] + '/computer/' +# Notifier Settings +EMAIL_HOST = os.environ['EMAIL_HOST'] +EMAIL_PORT = os.environ['EMAIL_PORT'] +EMAIL_HOST_USER = os.environ['EMAIL_HOST_USER'] +EMAIL_HOST_PASSWORD = os.environ['EMAIL_HOST_PASSWORD'] +EMAIL_USE_TLS=True diff --git a/dashboard/src/templates/booking/booking_calendar.html b/dashboard/src/templates/booking/booking_calendar.html index 4644e85..16f0a4a 100644 --- a/dashboard/src/templates/booking/booking_calendar.html +++ b/dashboard/src/templates/booking/booking_calendar.html @@ -45,6 +45,7 @@ <div class='input-group' id='endtimepicker'> {% bootstrap_field form.end addon_after='<span class="glyphicon glyphicon-calendar"></span>' %} </div> + {% bootstrap_field form.opsys %} {% bootstrap_field form.purpose %} {% bootstrap_field form.installer %} {% bootstrap_field form.scenario %} @@ -100,4 +101,4 @@ <script src={% static "js/fullcalendar-options.js" %}></script> <script src={% static "js/datetimepicker-options.js" %}></script> <script src={% static "js/booking-calendar.js" %}></script> -{% endblock extrajs %}
\ No newline at end of file +{% endblock extrajs %} diff --git a/dashboard/src/templates/booking/booking_detail.html b/dashboard/src/templates/booking/booking_detail.html index 4b016b2..cb937d3 100644 --- a/dashboard/src/templates/booking/booking_detail.html +++ b/dashboard/src/templates/booking/booking_detail.html @@ -19,8 +19,11 @@ <b>Purpose: </b> {{ booking.purpose }} </p> <p> + <b>Operating System: </b> {{ booking.opsys }} +</p> +<p> <b>Installer: </b> {{ booking.installer }} </p> <p> <b>Scenario: </b> {{ booking.scenario }} -</p>
\ No newline at end of file +</p> diff --git a/dashboard/src/templates/booking/booking_table.html b/dashboard/src/templates/booking/booking_table.html index 655b013..af2248c 100644 --- a/dashboard/src/templates/booking/booking_table.html +++ b/dashboard/src/templates/booking/booking_table.html @@ -7,6 +7,7 @@ <th>Purpose</th> <th>Start</th> <th>End</th> + <th>Operating System</th> <th>Installer</th> <th>Scenario</th> </tr> @@ -27,6 +28,9 @@ {{ booking.end }} </td> <td> + {{ booking.opsys }} + </td> + <td> {{ booking.installer }} </td> <td> @@ -34,4 +38,4 @@ </td> </tr> {% endfor %} -</tbody>
\ No newline at end of file +</tbody> diff --git a/dashboard/web/requirements.txt b/dashboard/web/requirements.txt index f80f1c0..0d864d3 100644 --- a/dashboard/web/requirements.txt +++ b/dashboard/web/requirements.txt @@ -15,3 +15,4 @@ pika==0.10.0 psycopg2==2.6.2 PyJWT==1.4.2 requests==2.11.0 +django-fernet-fields==0.5 |