date_range_validator = RegexValidator(r'[0-9]{2}\.[0-9]{2}\.[0-9]{4} - [0-9]{2}\.[0-9]{2}\.[0-9]{4}')
-nodes_validator = RegexValidator(r'^[0-9]{1,3}(:[0-9]{1,2}){0,2}$')
+nodes_validator = RegexValidator(r'^[0-9]{1,3}:[0-9]{1,2}(:[0-9]{1,2})?$')
env_name_validator = RegexValidator(r'^[a-zA-Z_][a-zA-Z0-9_]*$')
CHOICES_PLACEHOLDER = (None, '')
(REEF, u'Reef'),
(ZEUS, u'Zeus'),
)
+
+ class Process(object):
+ NONE = ''
+ CMD = 'c'
+ SCRIPT = 's'
+
+ CHOICES = (
+ (NONE, u'Brak'),
+ (CMD, u'Polecenie'),
+ (SCRIPT, u'Skrypt'),
+ )
+
APPLICATION_CHOICES = (
CHOICES_PLACEHOLDER,
('bash', 'BASH'),
('plgrid/apps/python', 'plgrid/apps/python'),
('plgrid/apps/matlab', 'plgrid/apps/matlab'),
)
- NOTIFY_CHOICES = (
- (0, u'Brak'),
- (1, u'E-mail'),
- (2, u'XMPP'),
- )
- PROCESS_CHOICES = (
- (0, u'Brak'),
- (1, u'Polecenie'),
- (2, u'Skrypt'),
+ PROTOCOL_CHOICES = (
+ ('', u'Brak'),
+ ('mailto', u'E-mail'),
+ ('xmpp', u'XMPP'),
)
application = forms.ChoiceField(choices=APPLICATION_CHOICES, label=u"Aplikacja", required=False) # TODO choices
note = forms.CharField(label=u"Opis", widget=forms.Textarea(attrs={'rows': 2, 'cols': 40}), required=False)
grant = forms.CharField(label=u"Grant", max_length=100, required=False)
- host = forms.MultipleChoiceField(label=u"Host", choices=Host.CHOICES, required=False)
+ hosts = forms.MultipleChoiceField(label=u"Host", choices=Host.CHOICES, required=False)
properties = forms.MultipleChoiceField(label=u"Właściwości węzłów", required=False)
queue = forms.ChoiceField(choices=QUEUE_CHOICES, label=u"Kolejka", required=False)
procs = forms.IntegerField(label=u"Liczba procesów", min_value=0, required=False)
stage_out = forms.CharField(label=u"Stage out", max_length=500, required=False)
monitoring = forms.BooleanField(label=u"Portal QCG-Monitoring", required=False)
- notify_type = forms.ChoiceField(label=u"Monitorowanie stanu", choices=NOTIFY_CHOICES, required=False, initial=0,
+ notify_type = forms.ChoiceField(label=u"Monitorowanie stanu", choices=PROTOCOL_CHOICES, required=False, initial='',
widget=forms.RadioSelect)
notify_address = forms.EmailField(label=u"Adres", required=False)
- watch_output_type = forms.ChoiceField(label=u"Monitorowanie wyjścia", choices=NOTIFY_CHOICES, required=False,
- initial=0, widget=forms.RadioSelect)
+ watch_output_type = forms.ChoiceField(label=u"Monitorowanie wyjścia", choices=PROTOCOL_CHOICES, required=False,
+ initial='', widget=forms.RadioSelect)
watch_output_address = forms.EmailField(label=u"Adres", required=False)
watch_output_pattern = forms.CharField(label=u"Wzorzec", max_length=500, required=False)
- preprocess_type = forms.ChoiceField(label=u"Preprocessing", choices=PROCESS_CHOICES, required=False, initial=0,
- widget=forms.RadioSelect)
+ preprocess_type = forms.ChoiceField(label=u"Preprocessing", choices=Process.CHOICES, required=False,
+ initial=Process.NONE, widget=forms.RadioSelect)
preprocess_cmd = forms.CharField(label=u"Polecenie", max_length=1000, required=False)
preprocess_script = forms.CharField(label=u"Skrypt", max_length=500, required=False) # TODO grid ftp
- postprocess_type = forms.ChoiceField(label=u"Postprocessing", choices=PROCESS_CHOICES, required=False, initial=0,
- widget=forms.RadioSelect)
+ postprocess_type = forms.ChoiceField(label=u"Postprocessing", choices=Process.CHOICES, required=False,
+ initial=Process.CHOICES, widget=forms.RadioSelect)
postprocess_cmd = forms.CharField(label=u"Polecenie", max_length=1000, required=False)
postprocess_script = forms.CharField(label=u"Skrypt", max_length=500, required=False) # TODO grid ftp
native = forms.MultipleChoiceField(label=u"Opcje systemu kolejkowego", required=False)
self.fields['properties'].choices += ((v, v) for v in data.getlist('properties'))
self.fields['native'].choices += ((v, v) for v in data.getlist('native'))
+ def clean(self):
+ data = super(JobDescriptionForm, self).clean()
+
+ notify_type = data.get('notify_type')
+ data['notify'] = u'{}:{}'.format(notify_type, data['notify_address']) if notify_type else ''
+
+ wo_type = data.get('watch_output_type')
+ data['watch_output'] = u'{}:{}'.format(wo_type, data['watch_output_address']) if wo_type else ''
+
+ preprocess_type = data.get('preprocess_type')
+ if preprocess_type == self.Process.CMD:
+ data['preprocess'] = data['preprocess_cmd']
+ elif preprocess_type == self.Process.SCRIPT:
+ data['preprocess'] = data['preprocess_script']
+ else:
+ data['preprocess'] = ''
+
+ postprocess_type = data.get('postprocess_type')
+ if postprocess_type == self.Process.CMD:
+ data['postprocess'] = data['postprocess_cmd']
+ elif postprocess_type == self.Process.SCRIPT:
+ data['postprocess'] = data['postprocess_script']
+ else:
+ data['postprocess'] = ''
+
+ def clean_application(self):
+ value = self.cleaned_data['application']
+
+ if '/' in value:
+ return value.split('/', 1)
+
+ return value, None
+
+ def clean_nodes(self):
+ value = map(int, self.cleaned_data['nodes'].split(':', 2))
+
+ if len(value) == 3:
+ return tuple(value)
+
+ nodes, slots = value
+ return nodes, slots, slots
+
class EnvForm(forms.Form):
name = forms.CharField(label=u"Nazwa", max_length=100, validators=[env_name_validator],
}
}
});
- $('#id_modules, #id_host').selectize({
+ $('#id_modules, #id_hosts').selectize({
plugins: ['remove_button']
});
{% block container %}
<h1 class="page-header">{% block title %}Zleć zadanie{% endblock %}</h1>
+ {% if errors %}
+ <div class="alert alert-danger">
+ <strong>Uwaga!</strong> Formularz zawiera błędy.
+ {{ form.non_field_errors }}
+ {{ env_formset.non_field_errors }}
+ </div>
+ {% endif %}
+
<form action="." method="post" class="form-horizontal">
{% csrf_token %}
</fieldset>
<fieldset id="resources" class="tab-pane" role="tabpanel">
- {% bootstrap_field form.host layout="horizontal" %}
+ {% bootstrap_field form.hosts layout="horizontal" %}
{% bootstrap_field form.queue layout="horizontal" %}
{% bootstrap_field form.properties layout="horizontal" form_group_class="form-group collapse" %}
{% bootstrap_field form.modules layout="horizontal" form_group_class="form-group collapse" %}
<label class="btn btn-default">
<input type="radio" autocomplete="off" name="{{ option.name }}" value="{{ option.choice_value }}"
{% if option.is_checked %}checked{% endif %}
- data-target=".notify-type-{{ option.choice_value }}"> {{ option.choice_label }}
+ data-target=".notify-type-{{ forloop.counter0 }}"> {{ option.choice_label }}
</label>
{% endfor %}
</div>
<label class="btn btn-default">
<input type="radio" autocomplete="off" name="{{ option.name }}" value="{{ option.choice_value }}"
{% if option.is_checked %}checked{% endif %}
- data-target=".watch-output-type-{{ option.choice_value }}"> {{ option.choice_label }}
+ data-target=".watch-output-type-{{ forloop.counter0 }}"> {{ option.choice_label }}
</label>
{% endfor %}
</div>
<label class="btn btn-default">
<input type="radio" autocomplete="off" name="{{ option.name }}" value="{{ option.choice_value }}"
{% if option.is_checked %}checked{% endif %}
- data-target=".preprocess-type-{{ option.choice_value }}"> {{ option.choice_label }}
+ data-target=".preprocess-type-{{ forloop.counter0 }}"> {{ option.choice_label }}
</label>
{% endfor %}
</div>
<label class="btn btn-default">
<input type="radio" autocomplete="off" name="{{ option.name }}" value="{{ option.choice_value }}"
{% if option.is_checked %}checked{% endif %}
- data-target=".postprocess-type-{{ option.choice_value }}"> {{ option.choice_label }}
+ data-target=".postprocess-type-{{ forloop.counter0 }}"> {{ option.choice_label }}
</label>
{% endfor %}
</div>
desc = JobDescription(Credential(request.session['proxy']))
- application = form.cleaned_data['application']
- if '/' in application:
- app_tuple = application.split('/', 1)
- else:
- app_tuple = (application, None)
- desc.application = app_tuple
+ direct_map = ('application', 'arguments', 'note', 'grant', 'hosts', 'properties', 'queue', 'procs', 'nodes',
+ 'wall_time', 'memory', 'memory_per_slot', 'modules', 'native', 'notify', 'preprocess',
+ 'postprocess', 'persistent')
+ for name in direct_map:
+ if form.cleaned_data[name]:
+ setattr(desc, name, form.cleaned_data[name])
+
+ desc.reservation = ('LOCAL', form.cleaned_data['reservation'])
+ desc.watch_output = (form.cleaned_data['watch_output'], form.cleaned_data['watch_output_pattern'])
+ desc.env_variables += [(env['name'], env['value']) for env in env_formset.cleaned_data if not env['DELETE']]
# TODO script
# TODO executable
- desc.arguments = form.cleaned_data['arguments']
- desc.note = form.cleaned_data['note']
- desc.grant = form.cleaned_data['grant']
- desc.hosts = form.cleaned_data['host']
- desc.properties = form.cleaned_data['properties']
- # TODO queue
- # TODO procs
- # TODO nodes
- if form.cleaned_data['wall_time'] is not None:
- desc.wall_time = form.cleaned_data['wall_time']
- desc.memory = form.cleaned_data['memory']
- desc.memory_per_slot = form.cleaned_data['memory_per_slot']
- desc.env_variables = [(env['name'], env['value']) for env in env_formset.cleaned_data if not env['DELETE']]
- desc.modules = form.cleaned_data['modules']
- desc.natives = form.cleaned_data['native']
- desc.reservation = ("LOCAL", form.cleaned_data['reservation'])
# TODO input
# TODO stage_in
# TODO stage_out
# TODO monitoring
- # TODO notify_type
- # TODO notify_address
- # TODO watch_output_type
- # TODO watch_output_address
- # TODO watch_output_pattern
- # TODO preprocess_type
- # TODO preprocess_cmd
- # TODO preprocess_script
- # TODO postprocess_type
- # TODO postprocess_cmd
- # TODO postprocess_script
- desc.persistent = form.cleaned_data['persistent']
-
- for prop in ('application', 'arguments', 'note', 'grant', 'hosts', 'wall_time', 'memory', 'memory_per_slot',
- 'properties', 'env_variables', 'modules', 'natives', 'persistent', 'reservation'):
+
+ for prop in direct_map + ('env_variables', 'reservation', 'watch_output'):
print prop, type(getattr(desc, prop)), repr(getattr(desc, prop))
print desc.xml_description
+ print repr(form.errors)
+ print repr(env_formset.errors)
else:
form = JobDescriptionForm()
env_formset = EnvFormSet()
- return render(request, 'qcg/job_new.html', {'form': form, 'env_formset': env_formset})
+ errors = form.errors or (env_formset.is_bound and not env_formset.is_valid)
+
+ return render(request, 'qcg/job_new.html', {'form': form, 'env_formset': env_formset, 'errors': errors})