Rewrite Notification subsystem
[pharos-tools.git] / dashboard / src / workflow / booking_workflow.py
1 ##############################################################################
2 # Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
3 #
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
9
10 from django.contrib import messages
11 from django.shortcuts import render
12 from django.contrib.auth.models import User
13 from django.utils import timezone
14
15 import json
16 from datetime import timedelta
17
18 from account.models import UserProfile
19 from booking.models import Booking
20 from workflow.models import WorkflowStep
21 from workflow.forms import ResourceSelectorForm, SWConfigSelectorForm, BookingMetaForm, ConfirmationForm
22 from resource_inventory.models import GenericResourceBundle, ResourceBundle, ConfigBundle
23
24 class Resource_Select(WorkflowStep):
25     template = 'booking/steps/resource_select.html'
26     title = "Select Resource"
27     description = "Select a resource template to use for your deployment"
28     short_title = "pod select"
29
30     def __init__(self, *args, **kwargs):
31         super(Resource_Select, self).__init__(*args, **kwargs)
32         self.repo_key = self.repo.SELECTED_GRESOURCE_BUNDLE
33         self.repo_check_key = False
34         self.confirm_key = "booking"
35
36     def get_default_entry(self):
37         return None
38
39     def get_context(self):
40         context = super(Resource_Select, self).get_context()
41         default = []
42         chosen_bundle = None
43         default_bundle = self.get_default_entry()
44         if default_bundle:
45             context['disabled'] = True
46             chosen_bundle = default_bundle
47             if chosen_bundle.id:
48                 default.append(chosen_bundle.id)
49             else:
50                 default.append("repo bundle")
51         else:
52             chosen_bundle = self.repo_get(self.repo_key, False)
53             if chosen_bundle:
54                 if chosen_bundle.id:
55                     default.append(chosen_bundle.id)
56                 else:
57                     default.append("repo bundle")
58
59         bundle = default_bundle
60         if not bundle:
61             bundle = chosen_bundle
62         edit = self.repo_get(self.repo.EDIT, False)
63         user = self.repo_get(self.repo.SESSION_USER)
64         context['form'] = ResourceSelectorForm(
65                 data={"user": user},
66                 chosen_resource=default,
67                 bundle=bundle,
68                 edit=edit
69                 )
70         return context
71
72     def  post_render(self, request):
73         form = ResourceSelectorForm(request.POST)
74         context = self.get_context()
75         if form.is_valid():
76             data = form.cleaned_data['generic_resource_bundle']
77             data = data[2:-2]
78             if not data:
79                 self.metastep.set_invalid("Please select a valid bundle")
80                 return render(request, self.template, context)
81             selected_bundle = json.loads(data)
82             selected_id = selected_bundle[0]['id']
83             gresource_bundle = None
84             try:
85                 selected_id = int(selected_id)
86                 gresource_bundle = GenericResourceBundle.objects.get(id=selected_id)
87             except ValueError:
88                 # we want the bundle in the repo
89                 gresource_bundle = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS,{}).get("bundle", GenericResourceBundle())
90             self.repo_put(
91                     self.repo_key,
92                     gresource_bundle
93                     )
94             confirm = self.repo_get(self.repo.CONFIRMATION)
95             if self.confirm_key not in confirm:
96                 confirm[self.confirm_key] = {}
97             confirm[self.confirm_key]["resource name"] = gresource_bundle.name
98             self.repo_put(self.repo.CONFIRMATION, confirm)
99             messages.add_message(request, messages.SUCCESS, 'Form Validated Successfully', fail_silently=True)
100             self.metastep.set_valid("Step Completed")
101             return render(request, self.template, context)
102         else:
103             messages.add_message(request, messages.ERROR, "Form Didn't Validate", fail_silently=True)
104             self.metastep.set_invalid("Please complete the fields highlighted in red to continue")
105             return render(request, self.template, context)
106
107 class Booking_Resource_Select(Resource_Select):
108
109     def __init__(self, *args, **kwargs):
110         super(Booking_Resource_Select, self).__init__(*args, **kwargs)
111         self.repo_key = self.repo.BOOKING_SELECTED_GRB
112         self.confirm_key = "booking"
113
114     def get_default_entry(self):
115         default = self.repo_get(self.repo.GRESOURCE_BUNDLE_MODELS, {}).get("bundle")
116         mine = self.repo_get(self.repo_key)
117         if mine:
118             return None
119         try:
120             config_bundle = self.repo_get(self.repo.BOOKING_MODELS)['booking'].config_bundle
121             if default:
122                 return default # select created grb, even if preselected config bundle
123             return config_bundle.bundle
124         except:
125             pass
126         return default
127
128     def get_context(self):
129         context = super(Booking_Resource_Select, self).get_context()
130         return context
131
132     def post_render(self, request):
133         response = super(Booking_Resource_Select, self).post_render(request)
134         models = self.repo_get(self.repo.BOOKING_MODELS, {})
135         if "booking" not in models:
136             models['booking'] = Booking()
137         booking = models['booking']
138         resource = self.repo_get(self.repo_key, False)
139         if resource:
140             try:
141                 booking.resource.template = resource
142             except:
143                 booking.resource = ResourceBundle(template=resource)
144         models['booking'] = booking
145         self.repo_put(self.repo.BOOKING_MODELS, models)
146         return response
147
148 class SWConfig_Select(WorkflowStep):
149     template = 'booking/steps/swconfig_select.html'
150     title = "Select Software Configuration"
151     description = "Choose the software and related configurations you want to have used for your deployment"
152     short_title = "pod config"
153
154     def post_render(self, request):
155         form = SWConfigSelectorForm(request.POST)
156         if form.is_valid():
157
158             bundle_json = form.cleaned_data['software_bundle']
159             bundle_json = bundle_json[2:-2]  # Stupid django string bug
160             if not bundle_json:
161                 self.metastep.set_invalid("Please select a valid config")
162                 return self.render(request)
163             bundle_json = json.loads(bundle_json)
164             bundle = None
165             try:
166                 id = int(bundle_json[0]['id'])
167                 bundle = ConfigBundle.objects.get(id=id)
168             except ValueError:
169                 bundle = self.repo_get(self.repo.CONFIG_MODELS).get("bundle")
170
171             models = self.repo_get(self.repo.BOOKING_MODELS, {})
172             if "booking" not in models:
173                 models['booking'] = Booking()
174             models['booking'].config_bundle = bundle
175             self.repo_put(self.repo.BOOKING_MODELS, models)
176             confirm = self.repo_get(self.repo.CONFIRMATION)
177             if "booking" not in confirm:
178                 confirm['booking'] = {}
179             confirm['booking']["configuration name"] = bundle.name
180             self.repo_put(self.repo.CONFIRMATION, confirm)
181             self.metastep.set_valid("Step Completed")
182             messages.add_message(request, messages.SUCCESS, 'Form Validated Successfully', fail_silently=True)
183         else:
184             self.metastep.set_invalid("Please select or create a valid config")
185             messages.add_message(request, messages.ERROR, "Form Didn't Validate", fail_silently=True)
186
187         return self.render(request)
188
189
190     def get_context(self):
191         context = super(SWConfig_Select, self).get_context()
192         default = []
193         bundle = None
194         chosen_bundle = None
195         created_bundle = self.repo_get(self.repo.CONFIG_MODELS, {}).get("bundle", False)
196         booking = self.repo_get(self.repo.BOOKING_MODELS, {}).get("booking", False)
197         try:
198             chosen_bundle = booking.config_bundle
199             default.append(chosen_bundle.id)
200             bundle=chosen_bundle
201         except:
202             if created_bundle:
203                 default.append("repo bundle")
204                 bundle = created_bundle
205                 context['disabled'] = True
206         edit = self.repo_get(self.repo.EDIT, False)
207         grb = self.repo_get(self.repo.BOOKING_SELECTED_GRB)
208         context['form'] = SWConfigSelectorForm(chosen_software=default, bundle=bundle, edit=edit, resource=grb)
209         return context
210
211 class Booking_Meta(WorkflowStep):
212     template = 'booking/steps/booking_meta.html'
213     title = "Extra Info"
214     description = "Tell us how long you want your booking, what it is for, and who else should have access to it"
215     short_title = "booking info"
216
217     def get_context(self):
218         context = super(Booking_Meta, self).get_context()
219         initial = {}
220         default = []
221         try:
222             models = self.repo_get(self.repo.BOOKING_MODELS, {})
223             booking = models.get("booking")
224             if booking:
225                 initial['purpose'] = booking.purpose
226                 initial['project'] = booking.project
227                 initial['length'] = (booking.end - booking.start).days
228             info = self.repo_get(self.repo.BOOKING_INFO_FILE, False)
229             if info:
230                 initial['info_file'] = info
231             users = models.get("collaborators", [])
232             for user in users:
233                 default.append(user.id)
234         except Exception as e:
235             pass
236
237         default_user = self.repo_get(self.repo.SESSION_USER)
238         if default_user is None:
239             # TODO: error
240             default_user = "you"
241         else:
242             default_user = default_user.username
243
244         context['form'] = BookingMetaForm(initial=initial, chosen_users=default, default_user=default_user)
245         return context
246
247     def post_render(self, request):
248         form = BookingMetaForm(data=request.POST)
249         context = self.get_context()
250
251         forms = self.repo_get(self.repo.BOOKING_FORMS, {})
252
253         forms["meta_form"] = form
254         self.repo_put(self.repo.BOOKING_FORMS, forms)
255
256         if form.is_valid():
257             models = self.repo_get(self.repo.BOOKING_MODELS, {})
258             if "booking" not in models:
259                 models['booking'] = Booking()
260             models['collaborators'] = []
261             confirm = self.repo_get(self.repo.CONFIRMATION)
262             if "booking" not in confirm:
263                 confirm['booking'] = {}
264
265             models['booking'].start = timezone.now()
266             models['booking'].end = timezone.now() + timedelta(days=int(form.cleaned_data['length']))
267             models['booking'].purpose = form.cleaned_data['purpose']
268             models['booking'].project = form.cleaned_data['project']
269             for key in ['length', 'project', 'purpose']:
270                 confirm['booking'][key] = form.cleaned_data[key]
271
272             user_data = form.cleaned_data['users']
273             confirm['booking']['collaborators'] = []
274             user_data = user_data[2:-2] #fixes malformed string from querydict
275             if user_data:
276                 form_users = json.loads(user_data)
277                 for user_json in form_users:
278                     user = User.objects.get(pk=user_json['id'])
279                     models['collaborators'].append(user)
280                     confirm['booking']['collaborators'].append(user.username)
281
282             info_file = form.cleaned_data.get("info_file", False)
283             if info_file:
284                 self.repo_put(self.repo.BOOKING_INFO_FILE, info_file)
285
286             self.repo_put(self.repo.BOOKING_MODELS, models)
287             self.repo_put(self.repo.CONFIRMATION, confirm)
288             messages.add_message(request, messages.SUCCESS, 'Form Validated', fail_silently=True)
289             self.metastep.set_valid("Step Completed")
290         else:
291             messages.add_message(request, messages.ERROR, "Form didn't validate", fail_silently=True)
292             self.metastep.set_invalid("Please complete the fields highlighted in red to continue")
293             context['form'] = form  # TODO: store this form
294         return render(request, self.template, context)