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
13 from django.utils.safestring import mark_safe
14 from django.template.loader import render_to_string
15 from django.forms.widgets import NumberInput
17 from account.models import Lab
18 from account.models import UserProfile
19 from resource_inventory.models import (
20 GenericResourceBundle,
28 class SearchableSelectMultipleWidget(widgets.SelectMultiple):
29 template_name = 'dashboard/searchable_select_multiple.html'
31 def __init__(self, attrs=None):
32 self.items = attrs['set']
33 self.show_from_noentry = attrs['show_from_noentry']
34 self.show_x_results = attrs['show_x_results']
35 self.results_scrollable = attrs['scrollable']
36 self.selectable_limit = attrs['selectable_limit']
37 self.placeholder = attrs['placeholder']
38 self.name = attrs['name']
39 self.initial = attrs.get("initial", "")
40 self.default_entry = attrs.get("default_entry", "")
41 self.edit = attrs.get("edit", False)
42 self.wf_type = attrs.get("wf_type")
43 self.incompatible = attrs.get("incompatible", "false")
45 super(SearchableSelectMultipleWidget, self).__init__(attrs)
47 def render(self, name, value, attrs=None, renderer=None):
49 context = self.get_context(attrs)
50 return mark_safe(render_to_string(self.template_name, context))
52 def get_context(self, attrs):
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,
64 'wf_type': self.wf_type,
65 'incompatible': self.incompatible
69 class ResourceSelectorForm(forms.Form):
71 def __init__(self, data=None, **kwargs):
75 if "chosen_resource" in kwargs:
76 chosen_resource = kwargs.pop("chosen_resource")
77 if "bundle" in kwargs:
78 bundle = kwargs.pop("bundle")
80 edit = kwargs.pop("edit")
81 super(ResourceSelectorForm, self).__init__(data=data, **kwargs)
82 queryset = GenericResourceBundle.objects.select_related("owner").all()
83 if data and 'user' in data:
84 queryset = queryset.filter(owner=data['user'])
86 attrs = self.build_search_widget_attrs(chosen_resource, bundle, edit, queryset)
88 self.fields['generic_resource_bundle'] = forms.CharField(
89 widget=SearchableSelectMultipleWidget(attrs=attrs)
92 def build_search_widget_attrs(self, chosen_resource, bundle, edit, queryset):
96 displayable['small_name'] = res.name
98 displayable['expanded_name'] = res.owner.username
100 displayable['expanded_name'] = ""
101 displayable['string'] = res.description
102 displayable['id'] = res.id
103 resources[res.id] = displayable
107 'show_from_noentry': "true",
108 'show_x_results': -1,
109 'scrollable': "true",
110 'selectable_limit': 1,
111 'name': "generic_resource_bundle",
112 'placeholder': "resource",
113 'initial': chosen_resource,
120 class SWConfigSelectorForm(forms.Form):
122 def __init__(self, *args, **kwargs):
128 if "chosen_software" in kwargs:
129 chosen_software = kwargs.pop("chosen_software")
131 if "bundle" in kwargs:
132 bundle = kwargs.pop("bundle")
134 edit = kwargs.pop("edit")
135 if "resource" in kwargs:
136 resource = kwargs.pop("resource")
138 user = kwargs.pop("user")
139 super(SWConfigSelectorForm, self).__init__(*args, **kwargs)
140 attrs = self.build_search_widget_attrs(chosen_software, bundle, edit, resource, user)
141 self.fields['software_bundle'] = forms.CharField(
142 widget=SearchableSelectMultipleWidget(attrs=attrs)
145 def build_search_widget_attrs(self, chosen, bundle, edit, resource, user):
147 queryset = ConfigBundle.objects.select_related('owner').all()
150 user = resource.owner
151 queryset = queryset.filter(bundle=resource)
154 queryset = queryset.filter(owner=user)
156 for config in queryset:
158 displayable['small_name'] = config.name
159 displayable['expanded_name'] = config.owner.username
160 displayable['string'] = config.description
161 displayable['id'] = config.id
162 configs[config.id] = displayable
164 incompatible_choice = "false"
165 if bundle and bundle.id not in configs:
167 displayable['small_name'] = bundle.name
168 displayable['expanded_name'] = bundle.owner.username
169 displayable['string'] = bundle.description
170 displayable['id'] = bundle.id
171 configs[bundle.id] = displayable
172 incompatible_choice = "true"
176 'show_from_noentry': "true",
177 'show_x_results': -1,
178 'scrollable': "true",
179 'selectable_limit': 1,
180 'name': "software_bundle",
181 'placeholder': "config",
185 'incompatible': incompatible_choice
190 class BookingMetaForm(forms.Form):
192 length = forms.IntegerField(
202 purpose = forms.CharField(max_length=1000)
203 project = forms.CharField(max_length=400)
204 info_file = forms.CharField(max_length=1000, required=False)
206 def __init__(self, data=None, *args, **kwargs):
208 if "default_user" in kwargs:
209 default_user = kwargs.pop("default_user")
212 self.default_user = default_user
213 if "chosen_users" in kwargs:
214 chosen_users = kwargs.pop("chosen_users")
215 elif data and "users" in data:
216 chosen_users = data.getlist("users")
220 super(BookingMetaForm, self).__init__(data=data, **kwargs)
222 self.fields['users'] = forms.CharField(
223 widget=SearchableSelectMultipleWidget(
224 attrs=self.build_search_widget_attrs(chosen_users, default_user=default_user)
229 def build_user_list(self):
231 returns a mapping of UserProfile ids to displayable objects expected by
232 searchable multiple select widget
236 d_qset = UserProfile.objects.select_related('user').all().exclude(user__username=self.default_user)
237 for userprofile in d_qset:
239 'id': userprofile.user.id,
240 'expanded_name': userprofile.full_name,
241 'small_name': userprofile.user.username,
242 'string': userprofile.email_addr
245 users[userprofile.user.id] = user
251 def build_search_widget_attrs(self, chosen_users, default_user="you"):
254 'set': self.build_user_list(),
255 'show_from_noentry': "false",
256 'show_x_results': 10,
257 'scrollable': "false",
258 'selectable_limit': -1,
260 'placeholder': "username",
261 'initial': chosen_users,
267 class MultipleSelectFilterWidget(forms.Widget):
268 def __init__(self, attrs=None):
269 super(MultipleSelectFilterWidget, self).__init__(attrs)
271 self.template_name = "dashboard/multiple_select_filter_widget.html"
273 def render(self, name, value, attrs=None, renderer=None):
275 self.context = self.get_context(name, value, attrs)
276 html = render_to_string(self.template_name, context=self.context)
277 return mark_safe(html)
279 def get_context(self, name, value, attrs):
283 class MultipleSelectFilterField(forms.Field):
285 def __init__(self, required=True, widget=None, label=None, initial=None,
286 help_text='', error_messages=None, show_hidden_initial=False,
287 validators=(), localize=False, disabled=False, label_suffix=None):
288 """from the documentation:
289 # required -- Boolean that specifies whether the field is required.
291 # widget -- A Widget class, or instance of a Widget class, that should
292 # be used for this Field when displaying it. Each Field has a
293 # default Widget that it'll use if you don't specify this. In
294 # most cases, the default widget is TextInput.
295 # label -- A verbose name for this field, for use in displaying this
296 # field in a form. By default, Django will use a "pretty"
297 # version of the form field name, if the Field is part of a
299 # initial -- A value to use in this Field's initial display. This value
300 # is *not* used as a fallback if data isn't given.
301 # help_text -- An optional string to use as "help text" for this Field.
302 # error_messages -- An optional dictionary to override the default
303 # messages that the field will raise.
304 # show_hidden_initial -- Boolean that specifies if it is needed to render a
305 # hidden widget with initial value after widget.
306 # validators -- List of additional validators to use
307 # localize -- Boolean that specifies if the field should be localized.
308 # disabled -- Boolean that specifies whether the field is disabled, that
309 # is its widget is shown in the form but not editable.
310 # label_suffix -- Suffix to be added to the label. Overrides
311 # form's label_suffix.
313 # this is bad, but django forms are annoying
315 if self.widget is None:
316 self.widget = MultipleSelectFilterWidget()
317 super(MultipleSelectFilterField, self).__init__(
323 error_messages=error_messages,
324 show_hidden_initial=show_hidden_initial,
325 validators=validators,
328 label_suffix=label_suffix
333 This method will raise a django.forms.ValidationError or return clean data
340 def getLabData(multiple_selectable_hosts):
342 Gets all labs and thier host profiles and returns a serialized version the form can understand.
343 Should be rewritten with a related query to make it faster
344 Should be moved outside of global scope
350 for lab in Lab.objects.all():
352 slab['id'] = "lab_" + str(lab.lab_user.id)
353 slab['name'] = lab.name
354 slab['description'] = lab.description
356 slab['selectable'] = 1
358 if not multiple_selectable_hosts:
361 items[slab['id']] = slab
362 mapping[slab['id']] = []
363 labs[slab['id']] = slab
364 for host in lab.hostprofiles.all():
366 shost['forms'] = [{"name": "host_name", "type": "text", "placeholder": "hostname"}]
367 shost['id'] = "host_" + str(host.id)
368 shost['name'] = host.name
369 shost['description'] = host.description
370 shost['selected'] = 0
371 shost['selectable'] = 1
373 shost['multiple'] = multiple_selectable_hosts
374 items[shost['id']] = shost
375 mapping[slab['id']].append(shost['id'])
376 if shost['id'] not in mapping:
377 mapping[shost['id']] = []
378 mapping[shost['id']].append(slab['id'])
379 hosts[shost['id']] = shost
381 filter_objects = [("labs", labs.values()), ("hosts", hosts.values())]
384 'filter_objects': filter_objects,
386 'filter_items': items
391 class HardwareDefinitionForm(forms.Form):
393 def __init__(self, *args, **kwargs):
394 selection_data = kwargs.pop("selection_data", False)
395 super(HardwareDefinitionForm, self).__init__(*args, **kwargs)
396 attrs = FormUtils.getLabData(1)
397 attrs['selection_data'] = selection_data
398 self.fields['filter_field'] = MultipleSelectFilterField(
399 widget=MultipleSelectFilterWidget(
405 class PodDefinitionForm(forms.Form):
408 xml = forms.CharField()
411 class ResourceMetaForm(forms.Form):
413 bundle_name = forms.CharField(label="POD Name")
414 bundle_description = forms.CharField(label="POD Description", widget=forms.Textarea)
417 class GenericHostMetaForm(forms.Form):
419 host_profile = forms.CharField(label="Host Type", disabled=True, required=False)
420 host_name = forms.CharField(label="Host Name")
423 class NetworkDefinitionForm(forms.Form):
424 def __init__(self, *args, **kwargs):
425 super(NetworkDefinitionForm, self).__init__(**kwargs)
428 class NetworkConfigurationForm(forms.Form):
429 def __init__(self, *args, **kwargs):
430 super(NetworkConfigurationForm).__init__(**kwargs)
433 class HostSoftwareDefinitionForm(forms.Form):
435 host_name = forms.CharField(max_length=200, disabled=True, required=False)
436 headnode = forms.BooleanField(required=False, widget=forms.HiddenInput)
438 def __init__(self, *args, **kwargs):
439 imageQS = kwargs.pop("imageQS")
440 super(HostSoftwareDefinitionForm, self).__init__(*args, **kwargs)
441 self.fields['image'] = forms.ModelChoiceField(queryset=imageQS)
444 class WorkflowSelectionForm(forms.Form):
445 fields = ['workflow']
447 empty_permitted = False
449 workflow = forms.ChoiceField(
452 (1, 'Resource Bundle'),
453 (2, 'Software Configuration')
455 label="Choose Workflow",
461 class SnapshotHostSelectForm(forms.Form):
462 host = forms.CharField()
465 class BasicMetaForm(forms.Form):
466 name = forms.CharField()
467 description = forms.CharField(widget=forms.Textarea)
470 class ConfirmationForm(forms.Form):
473 confirm = forms.ChoiceField(
481 class OPNFVSelectionForm(forms.Form):
482 installer = forms.ModelChoiceField(queryset=Installer.objects.all(), required=True)
483 scenario = forms.ModelChoiceField(queryset=Scenario.objects.all(), required=True)
486 class OPNFVNetworkRoleForm(forms.Form):
487 role = forms.CharField(max_length=200, disabled=True, required=False)
489 def __init__(self, *args, config_bundle, **kwargs):
490 super(OPNFVNetworkRoleForm, self).__init__(*args, **kwargs)
491 self.fields['network'] = forms.ModelChoiceField(
492 queryset=config_bundle.bundle.networks.all()
496 class OPNFVHostRoleForm(forms.Form):
497 host_name = forms.CharField(max_length=200, disabled=True, required=False)
498 role = forms.ModelChoiceField(queryset=OPNFVRole.objects.all().order_by("name").distinct("name"))