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