feb32f22de148f179bd9583e2f55cb72383485c6
[pharos-tools.git] / dashboard / src / workflow / forms.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
11 import django.forms as forms
12 from django.forms import widgets
13 from django.utils.safestring import mark_safe
14 from django.template.loader import render_to_string
15 from django.forms.widgets import NumberInput
16
17 from account.models import Lab
18 from account.models import UserProfile
19 from resource_inventory.models import (
20     GenericResourceBundle,
21     ConfigBundle,
22     OPNFVRole,
23     Image,
24     Installer,
25     Scenario
26 )
27
28
29 class SearchableSelectMultipleWidget(widgets.SelectMultiple):
30     template_name = 'dashboard/searchable_select_multiple.html'
31
32     def __init__(self, attrs=None):
33         self.items = attrs['set']
34         self.show_from_noentry = attrs['show_from_noentry']
35         self.show_x_results = attrs['show_x_results']
36         self.results_scrollable = attrs['scrollable']
37         self.selectable_limit = attrs['selectable_limit']
38         self.placeholder = attrs['placeholder']
39         self.name = attrs['name']
40         self.initial = attrs.get("initial", "")
41         self.default_entry = attrs.get("default_entry", "")
42         self.edit = attrs.get("edit", False)
43         self.wf_type = attrs.get("wf_type")
44
45         super(SearchableSelectMultipleWidget, self).__init__(attrs)
46
47     def render(self, name, value, attrs=None, renderer=None):
48
49         context = self.get_context(attrs)
50         return mark_safe(render_to_string(self.template_name, context))
51
52     def get_context(self, attrs):
53         return {
54             'items': self.items,
55             'name': self.name,
56             'show_from_noentry': self.show_from_noentry,
57             'show_x_results': self.show_x_results,
58             'results_scrollable': self.results_scrollable,
59             'selectable_limit': self.selectable_limit,
60             'placeholder': self.placeholder,
61             'initial': self.initial,
62             'default_entry': self.default_entry,
63             'edit': self.edit,
64             'wf_type': self.wf_type
65         }
66
67
68 class ResourceSelectorForm(forms.Form):
69
70     def __init__(self, data=None, **kwargs):
71         chosen_resource = ""
72         bundle = None
73         edit = False
74         if "chosen_resource" in kwargs:
75             chosen_resource = kwargs.pop("chosen_resource")
76         if "bundle" in kwargs:
77             bundle = kwargs.pop("bundle")
78         if "edit" in kwargs:
79             edit = kwargs.pop("edit")
80         super(ResourceSelectorForm, self).__init__(data=data, **kwargs)
81         queryset = GenericResourceBundle.objects.select_related("owner").all()
82         if data and 'user' in data:
83             queryset = queryset.filter(owner=data['user'])
84
85         attrs = self.build_search_widget_attrs(chosen_resource, bundle, edit, queryset)
86
87         self.fields['generic_resource_bundle'] = forms.CharField(
88             widget=SearchableSelectMultipleWidget(attrs=attrs)
89         )
90
91     def build_search_widget_attrs(self, chosen_resource, bundle, edit, queryset):
92         resources = {}
93         for res in queryset:
94             displayable = {}
95             displayable['small_name'] = res.name
96             if res.owner:
97                 displayable['expanded_name'] = res.owner.username
98             else:
99                 displayable['expanded_name'] = ""
100             displayable['string'] = res.description
101             displayable['id'] = res.id
102             resources[res.id] = displayable
103
104             if bundle:
105                 displayable = {}
106                 displayable['small_name'] = bundle.name
107                 displayable['expanded_name'] = "Current bundle"
108                 displayable['string'] = bundle.description
109                 displayable['id'] = "repo bundle"
110                 resources["repo bundle"] = displayable
111         attrs = {
112             'set': resources,
113             'show_from_noentry': "true",
114             'show_x_results': -1,
115             'scrollable': "true",
116             'selectable_limit': 1,
117             'name': "generic_resource_bundle",
118             'placeholder': "resource",
119             'initial': chosen_resource,
120             'edit': edit,
121             'wf_type': 1
122         }
123         return attrs
124
125
126 class SWConfigSelectorForm(forms.Form):
127
128     def __init__(self, *args, **kwargs):
129         chosen_software = ""
130         bundle = None
131         edit = False
132         resource = None
133         if "chosen_software" in kwargs:
134             chosen_software = kwargs.pop("chosen_software")
135
136         if "bundle" in kwargs:
137             bundle = kwargs.pop("bundle")
138         if "edit" in kwargs:
139             edit = kwargs.pop("edit")
140         if "resource" in kwargs:
141             resource = kwargs.pop("resource")
142         super(SWConfigSelectorForm, self).__init__(*args, **kwargs)
143         attrs = self.build_search_widget_attrs(chosen_software, bundle, edit, resource)
144         self.fields['software_bundle'] = forms.CharField(
145             widget=SearchableSelectMultipleWidget(attrs=attrs)
146         )
147
148     def build_search_widget_attrs(self, chosen, bundle, edit, resource):
149         configs = {}
150         queryset = ConfigBundle.objects.select_related('owner').all()
151         if resource:
152             queryset = queryset.filter(bundle=resource)
153
154         for config in queryset:
155             displayable = {}
156             displayable['small_name'] = config.name
157             displayable['expanded_name'] = config.owner.username
158             displayable['string'] = config.description
159             displayable['id'] = config.id
160             configs[config.id] = displayable
161
162         if bundle:
163             displayable = {}
164             displayable['small_name'] = bundle.name
165             displayable['expanded_name'] = "Current configuration"
166             displayable['string'] = bundle.description
167             displayable['id'] = "repo bundle"
168             configs['repo bundle'] = displayable
169
170         attrs = {
171             'set': configs,
172             'show_from_noentry': "true",
173             'show_x_results': -1,
174             'scrollable': "true",
175             'selectable_limit': 1,
176             'name': "software_bundle",
177             'placeholder': "config",
178             'initial': chosen,
179             'edit': edit,
180             'wf_type': 2
181         }
182         return attrs
183
184
185 class BookingMetaForm(forms.Form):
186
187     length = forms.IntegerField(
188         widget=NumberInput(
189             attrs={
190                 "type": "range",
191                 'min': "0",
192                 "max": "21",
193                 "value": "0"
194             }
195         )
196     )
197     purpose = forms.CharField(max_length=1000)
198     project = forms.CharField(max_length=400)
199     info_file = forms.CharField(max_length=1000, required=False)
200
201     def __init__(self, data=None, *args, **kwargs):
202         chosen_users = []
203         if "default_user" in kwargs:
204             default_user = kwargs.pop("default_user")
205         else:
206             default_user = "you"
207         self.default_user = default_user
208         if "chosen_users" in kwargs:
209             chosen_users = kwargs.pop("chosen_users")
210         elif data and "users" in data:
211             chosen_users = data.getlist("users")
212         else:
213             pass
214
215         super(BookingMetaForm, self).__init__(data=data, **kwargs)
216
217         self.fields['users'] = forms.CharField(
218             widget=SearchableSelectMultipleWidget(
219                 attrs=self.build_search_widget_attrs(chosen_users, default_user=default_user)
220             ),
221             required=False
222         )
223
224     def build_user_list(self):
225         """
226         returns a mapping of UserProfile ids to displayable objects expected by
227         searchable multiple select widget
228         """
229         try:
230             users = {}
231             d_qset = UserProfile.objects.select_related('user').all().exclude(user__username=self.default_user)
232             for userprofile in d_qset:
233                 user = {
234                     'id': userprofile.user.id,
235                     'expanded_name': userprofile.full_name,
236                     'small_name': userprofile.user.username,
237                     'string': userprofile.email_addr
238                 }
239
240                 users[userprofile.user.id] = user
241
242             return users
243         except Exception:
244             pass
245
246     def build_search_widget_attrs(self, chosen_users, default_user="you"):
247
248         attrs = {
249             'set': self.build_user_list(),
250             'show_from_noentry': "false",
251             'show_x_results': 10,
252             'scrollable': "false",
253             'selectable_limit': -1,
254             'name': "users",
255             'placeholder': "username",
256             'initial': chosen_users,
257             'edit': False
258         }
259         return attrs
260
261
262 class MultipleSelectFilterWidget(forms.Widget):
263     def __init__(self, attrs=None):
264         super(MultipleSelectFilterWidget, self).__init__(attrs)
265         self.attrs = attrs
266         self.template_name = "dashboard/multiple_select_filter_widget.html"
267
268     def render(self, name, value, attrs=None, renderer=None):
269         attrs = self.attrs
270         self.context = self.get_context(name, value, attrs)
271         html = render_to_string(self.template_name, context=self.context)
272         return mark_safe(html)
273
274     def get_context(self, name, value, attrs):
275         return attrs
276
277
278 class MultipleSelectFilterField(forms.Field):
279
280     def __init__(self, required=True, widget=None, label=None, initial=None,
281                  help_text='', error_messages=None, show_hidden_initial=False,
282                  validators=(), localize=False, disabled=False, label_suffix=None):
283         """from the documentation:
284         # required -- Boolean that specifies whether the field is required.
285         #             True by default.
286         # widget -- A Widget class, or instance of a Widget class, that should
287         #           be used for this Field when displaying it. Each Field has a
288         #           default Widget that it'll use if you don't specify this. In
289         #           most cases, the default widget is TextInput.
290         # label -- A verbose name for this field, for use in displaying this
291         #          field in a form. By default, Django will use a "pretty"
292         #          version of the form field name, if the Field is part of a
293         #          Form.
294         # initial -- A value to use in this Field's initial display. This value
295         #            is *not* used as a fallback if data isn't given.
296         # help_text -- An optional string to use as "help text" for this Field.
297         # error_messages -- An optional dictionary to override the default
298         #                   messages that the field will raise.
299         # show_hidden_initial -- Boolean that specifies if it is needed to render a
300         #                        hidden widget with initial value after widget.
301         # validators -- List of additional validators to use
302         # localize -- Boolean that specifies if the field should be localized.
303         # disabled -- Boolean that specifies whether the field is disabled, that
304         #             is its widget is shown in the form but not editable.
305         # label_suffix -- Suffix to be added to the label. Overrides
306         #                 form's label_suffix.
307         """
308         # this is bad, but django forms are annoying
309         self.widget = widget
310         if self.widget is None:
311             self.widget = MultipleSelectFilterWidget()
312         super(MultipleSelectFilterField, self).__init__(
313             required=required,
314             widget=self.widget,
315             label=label,
316             initial=None,
317             help_text=help_text,
318             error_messages=error_messages,
319             show_hidden_initial=show_hidden_initial,
320             validators=validators,
321             localize=localize,
322             disabled=disabled,
323             label_suffix=label_suffix
324         )
325
326         def clean(data):
327             """
328             This method will raise a django.forms.ValidationError or return clean data
329             """
330             return data
331
332
333 class FormUtils:
334     @staticmethod
335     def getLabData():
336         """
337         Gets all labs and thier host profiles and returns a serialized version the form can understand.
338         Should be rewritten with a related query to make it faster
339         Should be moved outside of global scope
340         """
341         labs = {}
342         hosts = {}
343         items = {}
344         mapping = {}
345         for lab in Lab.objects.all():
346             slab = {}
347             slab['id'] = "lab_" + str(lab.lab_user.id)
348             slab['name'] = lab.name
349             slab['description'] = lab.description
350             slab['selected'] = 0
351             slab['selectable'] = 1
352             slab['follow'] = 1
353             slab['multiple'] = 0
354             items[slab['id']] = slab
355             mapping[slab['id']] = []
356             labs[slab['id']] = slab
357             for host in lab.hostprofiles.all():
358                 shost = {}
359                 shost['forms'] = [{"name": "host_name", "type": "text", "placeholder": "hostname"}]
360                 shost['id'] = "host_" + str(host.id)
361                 shost['name'] = host.name
362                 shost['description'] = host.description
363                 shost['selected'] = 0
364                 shost['selectable'] = 1
365                 shost['follow'] = 0
366                 shost['multiple'] = 1
367                 items[shost['id']] = shost
368                 mapping[slab['id']].append(shost['id'])
369                 if shost['id'] not in mapping:
370                     mapping[shost['id']] = []
371                 mapping[shost['id']].append(slab['id'])
372                 hosts[shost['id']] = shost
373
374         filter_objects = [("labs", labs.values()), ("hosts", hosts.values())]
375
376         context = {
377             'filter_objects': filter_objects,
378             'mapping': mapping,
379             'items': items
380         }
381         return context
382
383
384 class HardwareDefinitionForm(forms.Form):
385
386     def __init__(self, *args, **kwargs):
387         selection_data = kwargs.pop("selection_data", False)
388         super(HardwareDefinitionForm, self).__init__(*args, **kwargs)
389         attrs = FormUtils.getLabData()
390         attrs['selection_data'] = selection_data
391         self.fields['filter_field'] = MultipleSelectFilterField(
392             widget=MultipleSelectFilterWidget(
393                 attrs=attrs
394             )
395         )
396
397
398 class PodDefinitionForm(forms.Form):
399
400     fields = ["xml"]
401     xml = forms.CharField()
402
403
404 class ResourceMetaForm(forms.Form):
405
406     bundle_name = forms.CharField(label="POD Name")
407     bundle_description = forms.CharField(label="POD Description", widget=forms.Textarea)
408
409
410 class GenericHostMetaForm(forms.Form):
411
412     host_profile = forms.CharField(label="Host Type", disabled=True, required=False)
413     host_name = forms.CharField(label="Host Name")
414
415
416 class NetworkDefinitionForm(forms.Form):
417     def __init__(self, *args, **kwargs):
418         super(NetworkDefinitionForm, self).__init__(**kwargs)
419
420
421 class NetworkConfigurationForm(forms.Form):
422     def __init__(self, *args, **kwargs):
423         super(NetworkConfigurationForm).__init__(**kwargs)
424
425
426 class HostSoftwareDefinitionForm(forms.Form):
427     fields = ["host_name", "role", "image"]
428
429     host_name = forms.CharField(max_length=200, disabled=True, required=False)
430     role = forms.ModelChoiceField(queryset=OPNFVRole.objects.all())
431     image = forms.ModelChoiceField(queryset=Image.objects.all())
432
433
434 class SoftwareConfigurationForm(forms.Form):
435
436     name = forms.CharField(max_length=200)
437     description = forms.CharField(widget=forms.Textarea)
438     opnfv = forms.BooleanField(disabled=True, required=False)
439     installer = forms.ModelChoiceField(queryset=Installer.objects.all(), disabled=True, required=False)
440     scenario = forms.ModelChoiceField(queryset=Scenario.objects.all(), disabled=True, required=False)
441
442
443 class WorkflowSelectionForm(forms.Form):
444     fields = ['workflow']
445
446     empty_permitted = False
447
448     workflow = forms.ChoiceField(
449         choices=(
450             (0, 'Booking'),
451             (1, 'Resource Bundle'),
452             (2, 'Software Configuration')
453         ),
454         label="Choose Workflow",
455         initial='booking',
456         required=True
457     )
458
459
460 class SnapshotHostSelectForm(forms.Form):
461     host = forms.CharField()
462
463
464 class SnapshotMetaForm(forms.Form):
465     name = forms.CharField()
466     description = forms.CharField()
467
468
469 class ConfirmationForm(forms.Form):
470     fields = ['confirm']
471
472     confirm = forms.ChoiceField(
473         choices=(
474             (True, "Confirm"),
475             (False, "Cancel")
476         )
477     )