1 ##############################################################################
2 # Copyright (c) 2018 Sawyer Bergeron, Parker Berberian, and others.
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 ##############################################################################
11 import django.forms as forms
12 from django.forms import widgets, ValidationError
13 from django.utils.safestring import mark_safe
14 from django.template.loader import render_to_string
15 from django.forms.widgets import NumberInput
19 from account.models import Lab
20 from account.models import UserProfile
21 from resource_inventory.models import (
26 from booking.lib import get_user_items, get_user_field_opts
29 class SearchableSelectMultipleWidget(widgets.SelectMultiple):
30 template_name = 'dashboard/searchable_select_multiple.html'
32 def __init__(self, attrs=None):
33 self.items = attrs['items']
34 self.show_from_noentry = attrs['show_from_noentry']
35 self.show_x_results = attrs['show_x_results']
36 self.results_scrollable = attrs['results_scrollable']
37 self.selectable_limit = attrs['selectable_limit']
38 self.placeholder = attrs['placeholder']
39 self.name = attrs['name']
40 self.initial = attrs.get("initial", [])
42 super(SearchableSelectMultipleWidget, self).__init__()
44 def render(self, name, value, attrs=None, renderer=None):
46 context = self.get_context(attrs)
47 return mark_safe(render_to_string(self.template_name, context))
49 def get_context(self, attrs):
53 'show_from_noentry': self.show_from_noentry,
54 'show_x_results': self.show_x_results,
55 'results_scrollable': self.results_scrollable,
56 'selectable_limit': self.selectable_limit,
57 'placeholder': self.placeholder,
58 'initial': self.initial,
62 class SearchableSelectMultipleField(forms.Field):
63 def __init__(self, *args, required=True, widget=None, label=None, disabled=False,
64 items=None, queryset=None, show_from_noentry=True, show_x_results=-1,
65 results_scrollable=False, selectable_limit=-1, placeholder="search here",
66 name="searchable_select", initial=[], **kwargs):
67 """from the documentation:
68 # required -- Boolean that specifies whether the field is required.
70 # widget -- A Widget class, or instance of a Widget class, that should
71 # be used for this Field when displaying it. Each Field has a
72 # default Widget that it'll use if you don't specify this. In
73 # most cases, the default widget is TextInput.
74 # label -- A verbose name for this field, for use in displaying this
75 # field in a form. By default, Django will use a "pretty"
76 # version of the form field name, if the Field is part of a
78 # initial -- A value to use in this Field's initial display. This value
79 # is *not* used as a fallback if data isn't given.
80 # help_text -- An optional string to use as "help text" for this Field.
81 # error_messages -- An optional dictionary to override the default
82 # messages that the field will raise.
83 # show_hidden_initial -- Boolean that specifies if it is needed to render a
84 # hidden widget with initial value after widget.
85 # validators -- List of additional validators to use
86 # localize -- Boolean that specifies if the field should be localized.
87 # disabled -- Boolean that specifies whether the field is disabled, that
88 # is its widget is shown in the form but not editable.
89 # label_suffix -- Suffix to be added to the label. Overrides
90 # form's label_suffix.
94 if self.widget is None:
95 self.widget = SearchableSelectMultipleWidget(
98 'initial': [obj.id for obj in initial],
99 'show_from_noentry': show_from_noentry,
100 'show_x_results': show_x_results,
101 'results_scrollable': results_scrollable,
102 'selectable_limit': selectable_limit,
103 'placeholder': placeholder,
108 self.disabled = disabled
109 self.queryset = queryset
110 self.selectable_limit = selectable_limit
112 super().__init__(disabled=disabled, **kwargs)
114 self.required = required
116 def clean(self, data):
120 raise ValidationError("Nothing was selected")
123 data_as_list = json.loads(data)
124 if self.selectable_limit != -1:
125 if len(data_as_list) > self.selectable_limit:
126 raise ValidationError("Too many items were selected")
129 for elem in data_as_list:
130 items.append(self.queryset.get(id=elem))
135 class SearchableSelectAbstractForm(forms.Form):
136 def __init__(self, *args, queryset=None, initial=[], **kwargs):
137 self.queryset = queryset
138 items = self.generate_items(self.queryset)
139 options = self.generate_options()
141 super(SearchableSelectAbstractForm, self).__init__(*args, **kwargs)
142 self.fields['searchable_select'] = SearchableSelectMultipleField(
145 queryset=self.queryset,
149 def get_validated_bundle(self):
150 bundles = self.cleaned_data['searchable_select']
151 if len(bundles) < 1: # don't need to check for >1, as field does that for us
152 raise ValidationError("No bundle was selected")
155 def generate_items(self, queryset):
156 raise Exception("SearchableSelectAbstractForm does not implement concrete generate_items()")
158 def generate_options(self, disabled=False):
160 'show_from_noentry': True,
161 'show_x_results': -1,
162 'results_scrollable': True,
163 'selectable_limit': 1,
164 'placeholder': 'Search for a Bundle',
165 'name': 'searchable_select',
170 class SWConfigSelectorForm(SearchableSelectAbstractForm):
171 def generate_items(self, queryset):
174 for bundle in queryset:
176 'expanded_name': bundle.name,
177 'small_name': bundle.owner.username,
178 'string': bundle.description,
185 class OPNFVSelectForm(SearchableSelectAbstractForm):
186 def generate_items(self, queryset):
189 for config in queryset:
191 'expanded_name': config.name,
192 'small_name': config.bundle.owner.username,
193 'string': config.description,
200 class ResourceSelectorForm(SearchableSelectAbstractForm):
201 def generate_items(self, queryset):
204 for bundle in queryset:
206 'expanded_name': bundle.name,
207 'small_name': bundle.owner.username,
208 'string': bundle.description,
215 class BookingMetaForm(forms.Form):
217 length = forms.IntegerField(
227 purpose = forms.CharField(max_length=1000)
228 project = forms.CharField(max_length=400)
229 info_file = forms.CharField(max_length=1000, required=False)
230 deploy_opnfv = forms.BooleanField(required=False)
232 def __init__(self, *args, user_initial=[], owner=None, **kwargs):
233 super(BookingMetaForm, self).__init__(**kwargs)
235 self.fields['users'] = SearchableSelectMultipleField(
236 queryset=UserProfile.objects.select_related('user').exclude(user=owner),
237 initial=user_initial,
238 items=get_user_items(exclude=owner),
240 **get_user_field_opts()
244 class MultipleSelectFilterWidget(forms.Widget):
245 def __init__(self, attrs=None):
246 super(MultipleSelectFilterWidget, self).__init__(attrs)
248 self.template_name = "dashboard/multiple_select_filter_widget.html"
250 def render(self, name, value, attrs=None, renderer=None):
252 self.context = self.get_context(name, value, attrs)
253 html = render_to_string(self.template_name, context=self.context)
254 return mark_safe(html)
256 def get_context(self, name, value, attrs):
260 class MultipleSelectFilterField(forms.Field):
262 def __init__(self, required=True, widget=None, label=None, initial=None,
263 help_text='', error_messages=None, show_hidden_initial=False,
264 validators=(), localize=False, disabled=False, label_suffix=None):
265 """from the documentation:
266 # required -- Boolean that specifies whether the field is required.
268 # widget -- A Widget class, or instance of a Widget class, that should
269 # be used for this Field when displaying it. Each Field has a
270 # default Widget that it'll use if you don't specify this. In
271 # most cases, the default widget is TextInput.
272 # label -- A verbose name for this field, for use in displaying this
273 # field in a form. By default, Django will use a "pretty"
274 # version of the form field name, if the Field is part of a
276 # initial -- A value to use in this Field's initial display. This value
277 # is *not* used as a fallback if data isn't given.
278 # help_text -- An optional string to use as "help; text" for this Field.
279 # error_messages -- An optional dictionary to override the default
280 # messages that the field will raise.
281 # show_hidden_initial -- Boolean that specifies if it is needed to render a
282 # hidden widget with initial value after widget.
283 # validators -- List of additional validators to use
284 # localize -- Boolean that specifies if the field should be localized.
285 # disabled -- Boolean that specifies whether the field is disabled, that
286 # is its widget is shown in the form but not editable.
287 # label_suffix -- Suffix to be added to the label. Overrides
288 # form's label_suffix.
290 # this is bad, but django forms are annoying
292 if self.widget is None:
293 self.widget = MultipleSelectFilterWidget()
294 super(MultipleSelectFilterField, self).__init__(
300 error_messages=error_messages,
301 show_hidden_initial=show_hidden_initial,
302 validators=validators,
305 label_suffix=label_suffix
310 This method will raise a django.forms.ValidationError or return clean data
317 def getLabData(multiple_selectable_hosts):
319 Gets all labs and thier host profiles and returns a serialized version the form can understand.
320 Should be rewritten with a related query to make it faster
321 Should be moved outside of global scope
327 for lab in Lab.objects.all():
329 slab['id'] = "lab_" + str(lab.lab_user.id)
330 slab['name'] = lab.name
331 slab['description'] = lab.description
333 slab['selectable'] = 1
335 if not multiple_selectable_hosts:
338 items[slab['id']] = slab
339 mapping[slab['id']] = []
340 labs[slab['id']] = slab
341 for host in lab.hostprofiles.all():
343 shost['forms'] = [{"name": "host_name", "type": "text", "placeholder": "hostname"}]
344 shost['id'] = "host_" + str(host.id)
345 shost['name'] = host.name
346 shost['description'] = host.description
347 shost['selected'] = 0
348 shost['selectable'] = 1
350 shost['multiple'] = multiple_selectable_hosts
351 items[shost['id']] = shost
352 mapping[slab['id']].append(shost['id'])
353 if shost['id'] not in mapping:
354 mapping[shost['id']] = []
355 mapping[shost['id']].append(slab['id'])
356 hosts[shost['id']] = shost
358 filter_objects = [("labs", labs.values()), ("hosts", hosts.values())]
361 'filter_objects': filter_objects,
363 'filter_items': items
368 class HardwareDefinitionForm(forms.Form):
370 def __init__(self, *args, **kwargs):
371 selection_data = kwargs.pop("selection_data", False)
372 super(HardwareDefinitionForm, self).__init__(*args, **kwargs)
373 attrs = FormUtils.getLabData(1)
374 attrs['selection_data'] = selection_data
375 self.fields['filter_field'] = MultipleSelectFilterField(
376 widget=MultipleSelectFilterWidget(
382 class PodDefinitionForm(forms.Form):
385 xml = forms.CharField()
388 class ResourceMetaForm(forms.Form):
390 bundle_name = forms.CharField(label="POD Name")
391 bundle_description = forms.CharField(label="POD Description", widget=forms.Textarea)
394 class GenericHostMetaForm(forms.Form):
396 host_profile = forms.CharField(label="Host Type", disabled=True, required=False)
397 host_name = forms.CharField(label="Host Name")
400 class NetworkDefinitionForm(forms.Form):
401 def __init__(self, *args, **kwargs):
402 super(NetworkDefinitionForm, self).__init__(**kwargs)
405 class NetworkConfigurationForm(forms.Form):
406 def __init__(self, *args, **kwargs):
407 super(NetworkConfigurationForm).__init__(**kwargs)
410 class HostSoftwareDefinitionForm(forms.Form):
412 host_name = forms.CharField(max_length=200, disabled=True, required=False)
413 headnode = forms.BooleanField(required=False, widget=forms.HiddenInput)
415 def __init__(self, *args, **kwargs):
416 imageQS = kwargs.pop("imageQS")
417 super(HostSoftwareDefinitionForm, self).__init__(*args, **kwargs)
418 self.fields['image'] = forms.ModelChoiceField(queryset=imageQS)
421 class WorkflowSelectionForm(forms.Form):
422 fields = ['workflow']
424 empty_permitted = False
426 workflow = forms.ChoiceField(
429 (1, 'Resource Bundle'),
430 (2, 'Software Configuration')
432 label="Choose Workflow",
438 class SnapshotHostSelectForm(forms.Form):
439 host = forms.CharField()
442 class BasicMetaForm(forms.Form):
443 name = forms.CharField()
444 description = forms.CharField(widget=forms.Textarea)
447 class ConfirmationForm(forms.Form):
450 confirm = forms.ChoiceField(
458 class OPNFVSelectionForm(forms.Form):
459 installer = forms.ModelChoiceField(queryset=Installer.objects.all(), required=True)
460 scenario = forms.ModelChoiceField(queryset=Scenario.objects.all(), required=True)
463 class OPNFVNetworkRoleForm(forms.Form):
464 role = forms.CharField(max_length=200, disabled=True, required=False)
466 def __init__(self, *args, config_bundle, **kwargs):
467 super(OPNFVNetworkRoleForm, self).__init__(*args, **kwargs)
468 self.fields['network'] = forms.ModelChoiceField(
469 queryset=config_bundle.bundle.networks.all()
473 class OPNFVHostRoleForm(forms.Form):
474 host_name = forms.CharField(max_length=200, disabled=True, required=False)
475 role = forms.ModelChoiceField(queryset=OPNFVRole.objects.all().order_by("name").distinct("name"))