Lab as a Service 2.0
[laas.git] / src / booking / views.py
1 ##############################################################################
2 # Copyright (c) 2016 Max Breitenfeldt and others.
3 # Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
4 #
5 # All rights reserved. This program and the accompanying materials
6 # are made available under the terms of the Apache License, Version 2.0
7 # which accompanies this distribution, and is available at
8 # http://www.apache.org/licenses/LICENSE-2.0
9 ##############################################################################
10
11 from django.contrib import messages
12 from django.shortcuts import get_object_or_404
13 from django.http import JsonResponse
14 from django.urls import reverse
15 from django.utils import timezone
16 from django.views import View
17 from django.views.generic import FormView
18 from django.views.generic import TemplateView
19 from django.shortcuts import redirect, render
20 import json
21
22 from booking.forms import BookingForm, BookingEditForm
23 from resource_inventory.models import ResourceBundle
24 from resource_inventory.resource_manager import ResourceManager
25 from booking.models import Booking, Installer, Opsys
26 from booking.stats import StatisticsManager
27
28
29 def drop_filter(context):
30     installer_filter = {}
31     for os in Opsys.objects.all():
32         installer_filter[os.id] = []
33         for installer in os.sup_installers.all():
34             installer_filter[os.id].append(installer.id)
35
36     scenario_filter = {}
37     for installer in Installer.objects.all():
38         scenario_filter[installer.id] = []
39         for scenario in installer.sup_scenarios.all():
40             scenario_filter[installer.id].append(scenario.id)
41
42     context.update({'installer_filter': json.dumps(installer_filter), 'scenario_filter': json.dumps(scenario_filter)})
43
44
45 class BookingFormView(FormView):
46     template_name = "booking/booking_calendar.html"
47     form_class = BookingForm
48
49     def dispatch(self, request, *args, **kwargs):
50         self.resource = get_object_or_404(ResourceBundle, id=self.kwargs['resource_id'])
51         return super(BookingFormView, self).dispatch(request, *args, **kwargs)
52
53     def get_context_data(self, **kwargs):
54         title = 'Booking: ' + str(self.resource.id)
55         context = super(BookingFormView, self).get_context_data(**kwargs)
56         context.update({'title': title, 'resource': self.resource})
57
58         drop_filter(context)
59
60         return context
61
62     def get_success_url(self):
63         return reverse('booking:create', kwargs=self.kwargs)
64
65     def form_valid(self, form):
66         if not self.request.user.is_authenticated:
67             messages.add_message(self.request, messages.ERROR,
68                                  'You need to be logged in to book a Pod.')
69             return super(BookingFormView, self).form_invalid(form)
70
71         if form.cleaned_data['end'] - form.cleaned_data['start'] > timezone.timedelta(days=21):
72             messages.add_message(self.request, messages.ERROR,
73                                  'Bookings can be no more than 3 weeks long.')
74             return super(BookingFormView, self).form_invalid(form)
75
76         user = self.request.user
77         booking = Booking(start=form.cleaned_data['start'],
78                           end=form.cleaned_data['end'],
79                           purpose=form.cleaned_data['purpose'],
80                           installer=form.cleaned_data['installer'],
81                           scenario=form.cleaned_data['scenario'],
82                           resource=self.resource,
83                           owner=user
84                           )
85         try:
86             booking.save()
87         except ValueError as err:
88             messages.add_message(self.request, messages.ERROR, err)
89             return super(BookingFormView, self).form_invalid(form)
90         messages.add_message(self.request, messages.SUCCESS, 'Booking saved')
91         return super(BookingFormView, self).form_valid(form)
92
93
94 class BookingEditFormView(FormView):
95     template_name = "booking/booking_calendar.html"
96     form_class = BookingEditForm
97
98     def is_valid(self):
99         return True
100
101     def dispatch(self, request, *args, **kwargs):
102         self.resource = get_object_or_404(ResourceBundle, id=self.kwargs['resource_id'])
103         self.original_booking = get_object_or_404(Booking, id=self.kwargs['booking_id'])
104         return super(BookingEditFormView, self).dispatch(request, *args, **kwargs)
105
106     def get_context_data(self, **kwargs):
107         title = 'Editing Booking on: ' + self.resource.name
108         context = super(BookingEditFormView, self).get_context_data(**kwargs)
109         context.update({'title': title, 'resource': self.resource, 'booking': self.original_booking})
110
111         drop_filter(context)
112
113         return context
114
115     def get_form_kwargs(self):
116         kwargs = super(BookingEditFormView, self).get_form_kwargs()
117         kwargs['purpose'] = self.original_booking.purpose
118         kwargs['start'] = self.original_booking.start
119         kwargs['end'] = self.original_booking.end
120         return kwargs
121
122     def get_success_url(self):
123         return reverse('booking:create', args=(self.resource.id,))
124
125     def form_valid(self, form):
126
127         if not self.request.user.is_authenticated:
128             messages.add_message(self.request, messages.ERROR,
129                                  'You need to be logged in to book a Pod.')
130             return super(BookingEditFormView, self).form_invalid(form)
131
132         if not self.request.user == self.original_booking.user:
133             messages.add_message(self.request, messages.ERROR,
134                                  'You are not the owner of this booking.')
135             return super(BookingEditFormView, self).form_invalid(form)
136
137         # Do Conflict Checks
138         if self.original_booking.end != form.cleaned_data['end']:
139             if form.cleaned_data['end'] - self.original_booking.end > timezone.timedelta(days=7):
140                 messages.add_message(self.request, messages.ERROR,
141                                      'Extensions can not be longer than one week.')
142                 return super(BookingEditFormView, self).form_invalid(form)
143             elif self.original_booking.ext_count <= 0:
144                 messages.add_message(self.request, messages.ERROR,
145                                      'Cannot change end date after maximum number of extensions reached.')
146                 return super(BookingEditFormView, self).form_invalid(form)
147
148             else:
149                 self.original_booking.ext_count -= 1
150
151         if self.original_booking.start != form.cleaned_data['start']:
152             if timezone.now() > form.cleaned_data['start']:
153                 messages.add_message(self.request, messages.ERROR,
154                                      'Cannot change start date after it has occurred.')
155                 return super(BookingEditFormView, self).form_invalid(form)
156         self.original_booking.start = form.cleaned_data['start']
157         self.original_booking.end = form.cleaned_data['end']
158         self.original_booking.purpose = form.cleaned_data['purpose']
159         self.original_booking.installer = form.cleaned_data['installer']
160         self.original_booking.scenario = form.cleaned_data['scenario']
161         self.original_booking.reset = form.cleaned_data['reset']
162         try:
163             self.original_booking.save()
164         except ValueError as err:
165             messages.add_message(self.request, messages.ERROR, err)
166             return super(BookingEditFormView, self).form_invalid(form)
167
168         return super(BookingEditFormView, self).form_valid(form)
169
170
171 class BookingView(TemplateView):
172     template_name = "booking/booking_detail.html"
173
174     def get_context_data(self, **kwargs):
175         booking = get_object_or_404(Booking, id=self.kwargs['booking_id'])
176         title = 'Booking Details'
177         context = super(BookingView, self).get_context_data(**kwargs)
178         context.update({'title': title, 'booking': booking})
179         return context
180
181
182 class BookingDeleteView(TemplateView):
183     template_name = "booking/booking_delete.html"
184
185     def get_context_data(self, **kwargs):
186         booking = get_object_or_404(Booking, id=self.kwargs['booking_id'])
187         title = 'Delete Booking'
188         context = super(BookingDeleteView, self).get_context_data(**kwargs)
189         context.update({'title': title, 'booking': booking})
190         return context
191
192
193 def bookingDelete(request, booking_id):
194     booking = get_object_or_404(Booking, id=booking_id)
195     booking.delete()
196     messages.add_message(request, messages.SUCCESS, 'Booking deleted')
197     return redirect('../../../../')
198
199
200 class BookingListView(TemplateView):
201     template_name = "booking/booking_list.html"
202
203     def get_context_data(self, **kwargs):
204         bookings = Booking.objects.filter(end__gte=timezone.now())
205         title = 'Search Booking'
206         context = super(BookingListView, self).get_context_data(**kwargs)
207         context.update({'title': title, 'bookings': bookings})
208         return context
209
210
211 class ResourceBookingsJSON(View):
212     def get(self, request, *args, **kwargs):
213         resource = get_object_or_404(ResourceBundle, id=self.kwargs['resource_id'])
214         bookings = resource.booking_set.get_queryset().values(
215             'id',
216             'start',
217             'end',
218             'purpose',
219             'jira_issue_status',
220             'config_bundle__name'
221         )
222         return JsonResponse({'bookings': list(bookings)})
223
224
225 def booking_detail_view(request, booking_id):
226     user = None
227     if request.user.is_authenticated:
228         user = request.user
229     else:
230         return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
231
232     booking = get_object_or_404(Booking, id=booking_id)
233     return render(request, "booking/booking_detail.html", {
234         'title': 'Booking Details',
235         'booking': booking,
236         'pdf': ResourceManager().makePDF(booking.resource),
237         'user_id': user.id})
238
239
240 def booking_stats_view(request):
241     return render(
242             request,
243             "booking/stats.html",
244             context={
245                 "data": StatisticsManager.getContinuousBookingTimeSeries(),
246                 "title": "Booking Statistics"
247                 }
248             )
249
250
251 def booking_stats_json(request):
252     try:
253         span = int(request.GET.get("days", 14))
254     except:
255         span = 14
256     return JsonResponse(StatisticsManager.getContinuousBookingTimeSeries(span), safe=False)