aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/account/migrations/0002_auto_20180109_2002.py (renamed from src/account/migrations/0003_lab.py)18
-rw-r--r--src/account/migrations/0002_userprofile_email_addr.py20
-rw-r--r--src/api/serializers.py2
-rw-r--r--src/booking/forms.py44
-rw-r--r--src/booking/migrations/0002_booking_changeid.py (renamed from src/booking/migrations/0002_auto_20171213_1506.py)10
-rw-r--r--src/booking/migrations/0003_auto_20180108_2024.py25
-rw-r--r--src/booking/models.py8
-rw-r--r--src/booking/urls.py1
-rw-r--r--src/booking/views.py74
-rw-r--r--src/notifier/migrations/0001_initial.py1
-rw-r--r--src/static/js/booking-calendar.js15
-rw-r--r--src/static/js/fullcalendar-options.js2
-rw-r--r--src/templates/booking/booking_calendar.html22
-rw-r--r--src/templates/booking/booking_detail.html9
14 files changed, 212 insertions, 39 deletions
diff --git a/src/account/migrations/0003_lab.py b/src/account/migrations/0002_auto_20180109_2002.py
index c4643c5..5cf8a70 100644
--- a/src/account/migrations/0003_lab.py
+++ b/src/account/migrations/0002_auto_20180109_2002.py
@@ -1,14 +1,5 @@
-##############################################################################
-# Copyright (c) 2016 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
-##############################################################################
-
# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2018-01-09 15:34
+# Generated by Django 1.10 on 2018-01-09 20:02
from __future__ import unicode_literals
from django.conf import settings
@@ -20,7 +11,7 @@ class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('account', '0002_userprofile_email_addr'),
+ ('account', '0001_initial'),
]
operations = [
@@ -34,4 +25,9 @@ class Migration(migrations.Migration):
('lab_user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
+ migrations.AddField(
+ model_name='userprofile',
+ name='email_addr',
+ field=models.CharField(default='email@mail.com', max_length=300),
+ ),
]
diff --git a/src/account/migrations/0002_userprofile_email_addr.py b/src/account/migrations/0002_userprofile_email_addr.py
deleted file mode 100644
index bfbed17..0000000
--- a/src/account/migrations/0002_userprofile_email_addr.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- 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/src/api/serializers.py b/src/api/serializers.py
index c371a92..794e139 100644
--- a/src/api/serializers.py
+++ b/src/api/serializers.py
@@ -21,7 +21,7 @@ class BookingSerializer(serializers.ModelSerializer):
class Meta:
model = Booking
- fields = ('id', 'resource_id', 'start', 'end', 'opsys_name', 'installer_name', 'scenario_name', 'purpose')
+ fields = ('id', 'changeid', 'reset', 'resource_id', 'opsys_name', 'start', 'end', 'installer_name', 'scenario_name', 'purpose')
class ServerSerializer(serializers.ModelSerializer):
diff --git a/src/booking/forms.py b/src/booking/forms.py
index 1f09c05..b4acd8f 100644
--- a/src/booking/forms.py
+++ b/src/booking/forms.py
@@ -11,10 +11,21 @@
import django.forms as forms
from booking.models import Installer, Scenario, Opsys
-
+from datetime import datetime
class BookingForm(forms.Form):
- fields = ['start', 'end', 'purpose', 'opsys', 'installer', 'scenario']
+ fields = ['start', 'end', 'purpose', 'opsys', 'reset', 'installer', 'scenario']
+
+ start = forms.DateTimeField()
+ end = forms.DateTimeField()
+ reset = forms.ChoiceField(choices = ((True, 'Yes'),(False, 'No')), label="Reset System", initial='False', required=False)
+ 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)
+
+class BookingEditForm(forms.Form):
+ fields = ['start', 'end', 'purpose', 'opsys', 'reset', 'installer', 'scenario']
start = forms.DateTimeField()
end = forms.DateTimeField()
@@ -22,3 +33,32 @@ class BookingForm(forms.Form):
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)
+ reset = forms.ChoiceField(choices = ((True, 'Yes'),(False, 'No')), label="Reset System", initial='False', required=True)
+
+
+ def __init__(self, *args, **kwargs ):
+ cloned_kwargs = {}
+ cloned_kwargs['purpose'] = kwargs.pop('purpose')
+ cloned_kwargs['start'] = kwargs.pop('start')
+ cloned_kwargs['end'] = kwargs.pop('end')
+ if 'installer' in kwargs:
+ cloned_kwargs['installer'] = kwargs.pop('installer')
+ if 'scenario' in kwargs:
+ cloned_kwargs['scenario'] = kwargs.pop('scenario')
+ super(BookingEditForm, self).__init__( *args, **kwargs)
+
+ self.fields['purpose'].initial = cloned_kwargs['purpose']
+ self.fields['start'].initial = cloned_kwargs['start'].strftime('%m/%d/%Y %H:%M')
+ self.fields['end'].initial = cloned_kwargs['end'].strftime('%m/%d/%Y %H:%M')
+ try:
+ self.fields['installer'].initial = cloned_kwargs['installer'].id
+ except KeyError:
+ pass
+ except AttributeError:
+ pass
+ try:
+ self.fields['scenario'].initial = cloned_kwargs['scenario'].id
+ except KeyError:
+ pass
+ except AttributeError:
+ pass
diff --git a/src/booking/migrations/0002_auto_20171213_1506.py b/src/booking/migrations/0002_booking_changeid.py
index 3e0a5fa..33af8fd 100644
--- a/src/booking/migrations/0002_auto_20171213_1506.py
+++ b/src/booking/migrations/0002_booking_changeid.py
@@ -25,4 +25,14 @@ class Migration(migrations.Migration):
name='opsys',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='booking.Opsys'),
),
+ migrations.AddField(
+ model_name='booking',
+ name='changeid',
+ field=models.TextField(default='no change ID'),
+ ),
+ migrations.AlterField(
+ model_name='booking',
+ name='changeid',
+ field=models.TextField(blank=True, default='no change ID', null=True),
+ ),
]
diff --git a/src/booking/migrations/0003_auto_20180108_2024.py b/src/booking/migrations/0003_auto_20180108_2024.py
new file mode 100644
index 0000000..93cecc2
--- /dev/null
+++ b/src/booking/migrations/0003_auto_20180108_2024.py
@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10 on 2018-01-08 20:24
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('booking', '0002_booking_changeid'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='booking',
+ name='reset',
+ field=models.BooleanField(default=False),
+ ),
+ migrations.AlterField(
+ model_name='booking',
+ name='changeid',
+ field=models.TextField(blank=True, default='initial', null=True),
+ ),
+ ] \ No newline at end of file
diff --git a/src/booking/models.py b/src/booking/models.py
index 0bf5961..9156484 100644
--- a/src/booking/models.py
+++ b/src/booking/models.py
@@ -13,6 +13,8 @@ from django.contrib.auth.models import User
from django.db import models
from jira import JIRA
from jira import JIRAError
+from django.utils.crypto import get_random_string
+import hashlib
from dashboard.models import Resource
@@ -40,10 +42,12 @@ class Opsys(models.Model):
class Booking(models.Model):
id = models.AutoField(primary_key=True)
+ changeid = models.TextField(default='initial', blank=True, null=True)
user = models.ForeignKey(User, models.CASCADE) # delete if user is deleted
resource = models.ForeignKey(Resource, models.PROTECT)
start = models.DateTimeField()
end = models.DateTimeField()
+ reset = models.BooleanField(default=False)
jira_issue_id = models.IntegerField(null=True)
jira_issue_status = models.CharField(max_length=50)
@@ -78,6 +82,10 @@ class Booking(models.Model):
conflicting_dates = conflicting_dates.filter(start__lt=self.end)
if conflicting_dates.count() > 0:
raise ValueError('This booking overlaps with another booking')
+ if not self.changeid:
+ self.changeid = self.id
+ else:
+ self.changeid = hashlib.md5(self.changeid.encode() + get_random_string(length=32).encode()).hexdigest()
return super(Booking, self).save(*args, **kwargs)
def __str__(self):
diff --git a/src/booking/urls.py b/src/booking/urls.py
index 9e01316..403e32f 100644
--- a/src/booking/urls.py
+++ b/src/booking/urls.py
@@ -29,6 +29,7 @@ from booking.views import *
urlpatterns = [
url(r'^(?P<resource_id>[0-9]+)/$', BookingFormView.as_view(), name='create'),
+ url(r'^(?P<resource_id>[0-9]+)/edit/(?P<booking_id>[0-9]+)/$', BookingEditFormView.as_view(), name='edit'),
url(r'^(?P<resource_id>[0-9]+)/bookings_json/$', ResourceBookingsJSON.as_view(),
name='bookings_json'),
diff --git a/src/booking/views.py b/src/booking/views.py
index da2fe11..ab3a04e 100644
--- a/src/booking/views.py
+++ b/src/booking/views.py
@@ -7,7 +7,6 @@
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
@@ -21,11 +20,10 @@ from django.views.generic import TemplateView
from jira import JIRAError
from account.jira_util import get_jira
-from booking.forms import BookingForm
+from booking.forms import BookingForm, BookingEditForm
from booking.models import Booking
from dashboard.models import Resource
-
def create_jira_ticket(user, booking):
jira = get_jira(user)
issue_dict = {
@@ -55,6 +53,7 @@ class BookingFormView(FormView):
title = 'Booking: ' + self.resource.name
context = super(BookingFormView, self).get_context_data(**kwargs)
context.update({'title': title, 'resource': self.resource})
+ #raise PermissionDenied('check')
return context
def get_success_url(self):
@@ -92,6 +91,75 @@ class BookingFormView(FormView):
return super(BookingFormView, self).form_valid(form)
+class BookingEditFormView(FormView):
+ template_name = "booking/booking_calendar.html"
+ form_class = BookingEditForm
+
+ def is_valid(self):
+ return True
+
+ def dispatch(self, request, *args, **kwargs):
+ self.resource = get_object_or_404(Resource, id=self.kwargs['resource_id'])
+ self.original_booking = get_object_or_404(Booking, id=self.kwargs['booking_id'])
+ return super(BookingEditFormView, self).dispatch(request, *args, **kwargs)
+
+ def get_context_data(self, **kwargs):
+ title = 'Editing Booking on: ' + self.resource.name
+ context = super(BookingEditFormView, self).get_context_data(**kwargs)
+ context.update({'title': title, 'resource': self.resource})
+ return context
+
+ def get_form_kwargs(self):
+ kwargs = super(BookingEditFormView, self).get_form_kwargs()
+ kwargs['purpose'] = self.original_booking.purpose
+ kwargs['start'] = self.original_booking.start
+ kwargs['end'] = self.original_booking.end
+ try:
+ kwargs['installer'] = self.original_booking.installer
+ except AttributeError:
+ pass
+ try:
+ kwargs['scenario'] = self.original_booking.scenario
+ except AttributeError:
+ pass
+ return kwargs
+
+ def get_success_url(self):
+ return reverse('booking:create', args=(self.resource.id,))
+
+ def form_valid(self, form):
+
+ if not self.request.user.is_authenticated:
+ messages.add_message(self.request, messages.ERROR,
+ 'You need to be logged in to book a Pod.')
+ return super(BookingEditFormView, self).form_invalid(form)
+
+ if not self.request.user == self.original_booking.user:
+ messages.add_message(self.request, messages.ERROR,
+ 'You are not the owner of this booking.')
+ return super(BookingEditFormView, self).form_invalid(form)
+
+ #Do Conflict Checks
+ if self.original_booking.start != form.cleaned_data['start']:
+ if timezone.now() > form.cleaned_data['start']:
+ messages.add_message(self.request, messages.ERROR,
+ 'Cannot change start date after it has occurred.')
+ return super(BookingEditFormView, self).form_invalid(form)
+ self.original_booking.start = form.cleaned_data['start']
+ self.original_booking.end = form.cleaned_data['end']
+ self.original_booking.purpose = form.cleaned_data['purpose']
+ self.original_booking.installer = form.cleaned_data['installer']
+ self.original_booking.scenario = form.cleaned_data['scenario']
+ self.original_booking.reset = form.cleaned_data['reset']
+ try:
+ self.original_booking.save()
+ except ValueError as err:
+ messages.add_message(self.request, messages.ERROR, err)
+ return super(BookingEditFormView, self).form_invalid(form)
+
+ user = self.request.user
+ return super(BookingEditFormView, self).form_valid(form)
+
class BookingView(TemplateView):
template_name = "booking/booking_detail.html"
diff --git a/src/notifier/migrations/0001_initial.py b/src/notifier/migrations/0001_initial.py
index 78d2bad..cac4d04 100644
--- a/src/notifier/migrations/0001_initial.py
+++ b/src/notifier/migrations/0001_initial.py
@@ -12,7 +12,6 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
- ('account', '0002_userprofile_email_addr'),
('dashboard', '0002_auto_20170505_0815'),
]
diff --git a/src/static/js/booking-calendar.js b/src/static/js/booking-calendar.js
index f634293..303a6b2 100644
--- a/src/static/js/booking-calendar.js
+++ b/src/static/js/booking-calendar.js
@@ -51,8 +51,23 @@ function loadEvents(url) {
}
$(document).ready(function () {
+ createEditViewSwitch();
$('#calendar').fullCalendar(calendarOptions);
loadEvents(bookings_url);
$('#starttimepicker').datetimepicker(timepickerOptions);
$('#endtimepicker').datetimepicker(timepickerOptions);
+ $('#starttimeeditpicker').datetimepicker(timepickerOptions);
+ $('#endtimeeditpicker').datetimepicker(timepickerOptions);
});
+
+function createEditViewSwitch() {
+ var url = window.location.href;
+ var isEdit = url.substr(url.lastIndexOf('/'));
+
+ if ( url.indexOf('edit') !== -1 ) {
+ document.getElementById('booking_form_div').style.display = 'none';
+ calendarOptions.selectable = false;
+ } else {
+ document.getElementById('booking_edit_form_div').style.display = 'none';
+ }
+}
diff --git a/src/static/js/fullcalendar-options.js b/src/static/js/fullcalendar-options.js
index 22a1b95..a29103a 100644
--- a/src/static/js/fullcalendar-options.js
+++ b/src/static/js/fullcalendar-options.js
@@ -98,4 +98,4 @@ var calendarOptions = {
eventResize: function (event) {
sendEventToForm(event);
}
-}; \ No newline at end of file
+};
diff --git a/src/templates/booking/booking_calendar.html b/src/templates/booking/booking_calendar.html
index 16f0a4a..52193d5 100644
--- a/src/templates/booking/booking_calendar.html
+++ b/src/templates/booking/booking_calendar.html
@@ -56,6 +56,28 @@
{% endbuttons %}
</form>
</div>
+ <div id="booking_edit_form_div">
+ {% bootstrap_form_errors form type='non_fields' %}
+ <form method="post" action="" class="form" id="bookingeditform">
+ {% csrf_token %}
+
+ <div class='input-group' id='starttimeeditpicker'>
+ {% bootstrap_field form.start addon_after='<span class="glyphicon glyphicon-calendar"></span>' %}
+ </div>
+ <div class='input-group' id='endtimeeditpicker'>
+ {% bootstrap_field form.end addon_after='<span class="glyphicon glyphicon-calendar"></span>' %}
+ </div>
+ {% bootstrap_field form.purpose %}
+ {% bootstrap_field form.installer %}
+ {% bootstrap_field form.scenario %}
+ {% bootstrap_field form.reset %}
+ {% buttons %}
+ <button type="submit" class="btn btn btn-success">
+ Confirm Edit
+ </button>
+ {% endbuttons %}
+ </form>
+ </div>
{% else %}
<p>Please
<a href="{% url 'account:login' %}">
diff --git a/src/templates/booking/booking_detail.html b/src/templates/booking/booking_detail.html
index cb937d3..dd0bf03 100644
--- a/src/templates/booking/booking_detail.html
+++ b/src/templates/booking/booking_detail.html
@@ -27,3 +27,12 @@
<p>
<b>Scenario: </b> {{ booking.scenario }}
</p>
+{% if user.is_authenticated %}
+{% if user.get_username == booking.user.username %}
+<p>
+ <a href="{% url 'booking:edit' booking_id=booking.id resource_id=booking.resource.id %}" class="btn btn btn-success">
+ Edit Booking
+ </a>
+</p>
+{% endif %}
+{% endif %}