Merge remote-tracking branch 'origin/master'
authorMaciej Tronowski <mtro@man.poznan.pl>
Wed, 4 Mar 2015 09:22:22 +0000 (10:22 +0100)
committerMaciej Tronowski <mtro@man.poznan.pl>
Wed, 4 Mar 2015 09:22:22 +0000 (10:22 +0100)
# Conflicts:
# qcg/fields.py
# qcg/forms.py
# qcg/templates/qcg/job_new.html

qcg/fields.py
qcg/forms.py
qcg/models.py
qcg/static/qcg/main.css
qcg/templates/qcg/job_new.html
qcg/views.py

index 3e5ec44..e067f46 100644 (file)
@@ -4,16 +4,6 @@ from django import forms
 from django.core.exceptions import ValidationError
 
 
-class PredefinedChoiceField(forms.ChoiceField):
-    def valid_value(self, value):
-        # any value is valid
-        return True
-
-
-class MultiplePredefinedChoiceField(forms.MultipleChoiceField, PredefinedChoiceField):
-    pass
-
-
 class TimeRangeWidget(forms.MultiWidget):
     def decompress(self, value):
         if not value:
index 7b11d13..b2ad790 100644 (file)
@@ -4,7 +4,7 @@ from django.core.validators import RegexValidator
 from django.template.defaultfilters import capfirst
 from pyqcg.utils import TaskStatus
 
-from qcg.fields import PredefinedChoiceField, MultiplePredefinedChoiceField, TimeRangeField
+from qcg.fields import TimeRangeField
 from qcg.models import Task, Allocation
 
 
@@ -84,8 +84,9 @@ class JobDescriptionForm(forms.Form):
     APPLICATION_CHOICES = (
         CHOICES_PLACEHOLDER,
         ('bash', 'BASH'),
-        ('python', 'Python'),
+        ('gromacs/4.6.3', 'GROMACS 4.6.3'),
         ('matlab', 'MATLAB'),
+        ('python', 'Python'),
     )
     QUEUE_CHOICES = (
         CHOICES_PLACEHOLDER,
@@ -109,36 +110,28 @@ class JobDescriptionForm(forms.Form):
     )
 
     application = forms.ChoiceField(choices=APPLICATION_CHOICES, label=u"Aplikacja", required=False)  # TODO choices
-    executable = forms.CharField(label=u"Polecenie", max_length=500, required=False)  # TODO grid ftp
-    arguments = forms.CharField(label=u"Argumenty", max_length=1000, required=False)
-
-    name = forms.CharField(label=u"Nazwa", max_length=100, required=False)
-    note = forms.CharField(label=u"Notatka", widget=forms.Textarea(attrs={'rows': 2, 'cols': 40}), required=False)
+    executable = forms.CharField(label=u"Plik wykonywalny", max_length=500, required=False)  # TODO grid ftp
+    script = forms.CharField(label=u"Skrypt", widget=forms.Textarea(attrs={'rows': 2, 'cols': 40}), required=False)  # TODO saving to grid ftp
+    arguments = forms.MultipleChoiceField(label=u"Argumenty", required=False)
+    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.ChoiceField(label=u"Host", choices=Host.CHOICES, required=False)
-    queue = PredefinedChoiceField(choices=QUEUE_CHOICES, label=u"Kolejka", 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)
     nodes = forms.CharField(label=u"Topologia węzłów", max_length=10, validators=[nodes_validator], required=False)
-    wall_time = TimeRangeField(label=u"Wall time", required=False)
+    wall_time = forms.IntegerField(label=u"Wall time (s)", min_value=0, required=False)  # TODO duration field
     memory = forms.IntegerField(label=u"Pamięć (MB)", min_value=0, required=False)
     memory_per_slot = forms.IntegerField(label=u"Pamięci per proces (MB)", min_value=0, required=False)
+    modules = forms.MultipleChoiceField(label=u"Moduły", choices=MODULES_CHOICES, required=False)  # TODO choices
+    reservation = forms.CharField(label=u"Rezerwacja", max_length=100, required=False)
 
     # TODO grid ftp
     input = forms.CharField(label=u"Standardowe wejście", max_length=500, required=False)
-    output = forms.CharField(label=u"Standardowe wyjście", max_length=500, required=False)
-    error = forms.CharField(label=u"Standardowe wyjście błędów", max_length=500, required=False)
     stage_in = forms.CharField(label=u"Stage in", max_length=500, required=False)
     stage_out = forms.CharField(label=u"Stage out", max_length=500, required=False)
 
-    properties = MultiplePredefinedChoiceField(label=u"Właściwości węzłów", required=False)
-    modules = forms.MultipleChoiceField(label=u"Moduły", choices=MODULES_CHOICES, required=False)  # TODO choices
-
-    not_after = forms.DateTimeField(label=u"Nie później niż", required=False)
-    not_before = forms.DateTimeField(label=u"Nie wcześniej niż", required=False)
-    deadline = TimeRangeField(label=u"Deadline", required=False)
-    reservation = forms.CharField(label=u"Rezerwacja", max_length=100, 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,
                                     widget=forms.RadioSelect)
@@ -156,9 +149,18 @@ class JobDescriptionForm(forms.Form):
                                          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 = MultiplePredefinedChoiceField(label=u"Parametry natywne", required=False)
+    native = forms.MultipleChoiceField(label=u"Opcje systemu kolejkowego", required=False)
     persistent = forms.BooleanField(label=u"Trwałe", required=False)
-    use_scratch = forms.BooleanField(label=u"Scratch", required=False)
+
+    def __init__(self, data=None, *args, **kwargs):
+        super(JobDescriptionForm, self).__init__(data, *args, **kwargs)
+
+        if data is not None:
+            # accept user defined choices
+            self.fields['queue'].choices += ((data.get('queue'), data.get('queue')), )
+            self.fields['arguments'].choices += ((v, v) for v in data.getlist('arguments'))
+            self.fields['properties'].choices += ((v, v) for v in data.getlist('properties'))
+            self.fields['native'].choices += ((v, v) for v in data.getlist('native'))
 
 
 class EnvForm(forms.Form):
@@ -183,5 +185,5 @@ class ColumnsForm(forms.Form):
         (HOST, u"Host"),
     )
 
-    columns = forms.MultipleChoiceField(choices=COLUMNS_CHOICES, label=u"Kolumny", required=False,
-                                        widget=forms.CheckboxSelectMultiple)
+    columns = forms.MultipleChoiceField(choices=COLUMNS_CHOICES, initial=[k for k, v in COLUMNS_CHOICES[1:]],
+                                        label=u"Kolumny", required=False, widget=forms.CheckboxSelectMultiple)
index 451d7fb..b41fe85 100644 (file)
@@ -235,6 +235,4 @@ class NodeInfo(models.Model):
 
     @staticmethod
     def qcg_map(qcg_node):
-        return {'name': qcg_node.name,
-                # FIXME
-                'count': qcg_node.slots_count.intValue() if qcg_node.slots_count is not None else None}
+        return {'name': qcg_node.name, 'count': qcg_node.slots_count}
index 4edc075..b3bdc3b 100644 (file)
@@ -19,6 +19,7 @@ footer.navbar-fixed-bottom {
 
 textarea {
     resize: vertical;
+    min-height: 34px;
 }
 
 
index 37a4fc5..d5b7344 100644 (file)
@@ -96,7 +96,6 @@
             <li role="presentation"><a href="#resources" data-toggle="tab">Zasoby</a></li>
             <li role="presentation"><a href="#files" data-toggle="tab">Pliki</a></li>
             <li role="presentation"><a href="#environment" data-toggle="tab">Środowisko</a></li>
-            <li role="presentation"><a href="#reservation" data-toggle="tab">Rezerwacja</a></li>
             <li role="presentation"><a href="#monitoring" data-toggle="tab">Powiadomienia</a></li>
             <li role="presentation"><a href="#other" data-toggle="tab">Inne</a></li>
         </ul>
                     </div>
                 </div>
 
-                {% bootstrap_field form.executable layout="horizontal" form_group_class="form-group collapse"  %}
+                {% bootstrap_field form.executable layout="horizontal" form_group_class="form-group collapse" %}
+                {% bootstrap_field form.script layout="horizontal" form_group_class="form-group collapse" %}
                 {% bootstrap_field form.arguments layout="horizontal" %}
-                {% bootstrap_field form.name layout="horizontal" %}
                 {% bootstrap_field form.note layout="horizontal" %}
                 {% bootstrap_field form.grant layout="horizontal" %}
             </fieldset>
             <fieldset id="resources" class="tab-pane" role="tabpanel">
                 {% bootstrap_field form.host 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" %}
                 {% bootstrap_field form.procs layout="horizontal" %}
                 {% bootstrap_field form.nodes layout="horizontal" form_group_class="form-group collapse" %}
                 {% include 'qcg/time_range_field.html' with field_name='wall_time' field=form.wall_time %}
-                {% bootstrap_field form.memory layout="horizontal" %}
-                {% bootstrap_field form.memory_per_slot layout="horizontal" %}
+                {% bootstrap_field form.memory layout="horizontal" form_group_class="form-group collapse" %}
+                {% bootstrap_field form.memory_per_slot layout="horizontal" form_group_class="form-group collapse" %}
+                {% bootstrap_field form.reservation layout="horizontal" form_group_class="form-group collapse" %}
             </fieldset>
 
             <fieldset id="files" class="tab-pane" role="tabpanel">
                 {% bootstrap_field form.input layout="horizontal" %}
-                {% bootstrap_field form.output layout="horizontal" %}
-                {% bootstrap_field form.error layout="horizontal" %}
                 {% bootstrap_field form.stage_in layout="horizontal" %}
-                {% bootstrap_field form.stage_out layout="horizontal" %}
+                {% bootstrap_field form.stage_out layout="horizontal" form_group_class="form-group collapse" %}
             </fieldset>
 
             <fieldset id="environment" class="tab-pane" role="tabpanel">
-                {% bootstrap_field form.properties layout="horizontal" %}
                 {{ env_formset.management_form }}
 
                 <div class="form-group">
                         </div>
                     </div>
                 </div>
-
-                {% bootstrap_field form.modules layout="horizontal" %}
-            </fieldset>
-
-            <fieldset id="reservation" class="tab-pane" role="tabpanel">
-                {% bootstrap_field form.not_before layout="horizontal" %}
-                {% bootstrap_field form.not_after layout="horizontal" %}
-                {% include 'qcg/time_range_field.html' with field_name='deadline' field=form.deadline %}
-                {% bootstrap_field form.reservation layout="horizontal" form_group_class="form-group collapse" %}
             </fieldset>
 
             <fieldset id="monitoring" class="tab-pane" role="tabpanel">
                     </div>
                 </div>
 
-                <div class="form-group">
-                    <label class="col-sm-3 col-md-4 control-label">Monitorowanie wyjścia</label>
-                    <div class="col-sm-9 col-md-6">
-                        <div class="btn-group" data-toggle="buttons">
-                            {% for option in form.watch_output_type %}
-                                <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 }}
-                                </label>
-                            {% endfor %}
+                <div class="collapse">
+                    <div class="form-group">
+                        <label class="col-sm-3 col-md-4 control-label">Monitorowanie wyjścia</label>
+                        <div class="col-sm-9 col-md-6">
+                            <div class="btn-group" data-toggle="buttons">
+                                {% for option in form.watch_output_type %}
+                                    <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 }}
+                                    </label>
+                                {% endfor %}
+                            </div>
                         </div>
                     </div>
-                </div>
 
-                <div class="tab-content">
-                    <div class="tab-pane watch-output-type-0"></div>
+                    <div class="tab-content">
+                        <div class="tab-pane watch-output-type-0"></div>
 
-                    <div class="tab-pane watch-output-type-1 watch-output-type-2" style="margin-top: 15px">
-                        {% bootstrap_field form.watch_output_address layout="horizontal" %}
-                        {% bootstrap_field form.watch_output_pattern layout="horizontal" %}
+                        <div class="tab-pane watch-output-type-1 watch-output-type-2" style="margin-top: 15px">
+                            {% bootstrap_field form.watch_output_address layout="horizontal" %}
+                            {% bootstrap_field form.watch_output_pattern layout="horizontal" %}
+                        </div>
                     </div>
                 </div>
             </fieldset>
 
                 {% bootstrap_field form.native layout="horizontal" %}
                 {% bootstrap_checkbox form.persistent %}
-                {% bootstrap_checkbox form.use_scratch %}
             </fieldset>
         </div>
 
index 7a8f351..c34af9b 100644 (file)
@@ -12,6 +12,8 @@ from django.utils.timezone import UTC
 from django_openid_auth.views import make_consumer
 from openid.extensions import ax
 from pyqcg import QCG
+from pyqcg.description import JobDescription
+from pyqcg.utils import Credential
 
 from qcg.forms import FiltersForm, ColumnsForm, JobDescriptionForm, EnvFormSet
 from qcg.utils import update_user_data, paginator_context
@@ -115,16 +117,18 @@ def jobs_list(request):
 
     checked_status = {i: widget.is_checked() for i, widget in enumerate(filters['status'])}
 
-    if 'columns' in request.COOKIES:
-        columns = ColumnsForm(QueryDict(request.COOKIES.get('columns')))
+    columns = ColumnsForm(QueryDict(request.COOKIES.get('columns', '')))
 
-    if 'columns' not in request.COOKIES or not columns.is_valid():
-        columns = ColumnsForm(QueryDict('columns=1&columns=2&columns=3&columns=4&columns=5&columns=6'))
-        columns.is_valid()
+    displayed = None
+    if columns.is_valid():
+        displayed = {int(c) for c in columns.cleaned_data['columns']}
+
+    # if invalid or empty
+    if not displayed:
+        displayed = set(columns.fields['columns'].initial)
 
     context = {'filters': filters, 'checked_status': checked_status, 'selected_filters': selected_filters,
-               'columns': ColumnsForm(initial={'columns': columns.cleaned_data['columns']}),
-               'displayed': {int(c) for c in columns.cleaned_data['columns']}}
+               'columns': ColumnsForm(initial={'columns': displayed}), 'displayed': displayed}
 
     context.update(paginator_context(request, tasks))
 
@@ -138,6 +142,7 @@ def job_details(request, job_id):
     return render(request, 'qcg/job.html', {'job': job})
 
 
+@login_required
 def task_details(request, job_id, task_id):
     task = get_object_or_404(request.user.tasks.select_related('job').prefetch_related('allocations'),
                              job__job_id=job_id, task_id=task_id)
@@ -145,5 +150,69 @@ def task_details(request, job_id, task_id):
     return render(request, 'qcg/task.html', {'task': task})
 
 
+@login_required
 def job_new(request):
-    return render(request, 'qcg/job_new.html', {'form': JobDescriptionForm(), 'env_formset': EnvFormSet()})
+    if request.method == 'POST':
+        QCG.start()
+
+        form = JobDescriptionForm(request.POST)
+        env_formset = EnvFormSet(request.POST)
+
+        if form.is_valid() and env_formset.is_valid():
+            print form.cleaned_data
+
+            desc = JobDescription(Credential(request.session['proxy']))
+
+            application = form.cleaned_data['application']
+            if '/' in application:
+                app, ver = application.split('1', 1)
+                desc.application.set_name(app)
+                desc.application.set_version(ver)
+            else:
+                desc.application.set_name(application)
+
+            # TODO script
+            # TODO executable
+            desc.arguments = form.cleaned_data['arguments']
+            desc.note = form.cleaned_data['note']
+            desc.grant = form.cleaned_data['grant']
+            desc.host_candidates = [(form.cleaned_data['host'], 'type?')]  # FIXME WTF ?!?
+            desc.properties = form.cleaned_data['properties']
+            # TODO queue
+            # TODO procs
+            # TODO nodes
+            desc.wall_time = timedelta(seconds=form.cleaned_data['wall_time'])  # FIXME odpowiedni typ pola
+            desc.memory = form.cleaned_data['memory']
+            desc.memory_per_slot = form.cleaned_data['memory_per_slot']
+            desc.variable = [(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 ('arguments', 'note', 'grant', 'host_candidates', 'wall_time', 'memory',
+                         'memory_per_slot', 'properties', 'variable', 'modules', 'natives', 'persistent', 'reservation'):
+                print prop, type(getattr(desc, prop)), repr(getattr(desc, prop))
+
+            print desc.xml_description
+
+    else:
+        form = JobDescriptionForm()
+        env_formset = EnvFormSet()
+
+    return render(request, 'qcg/job_new.html', {'form': form, 'env_formset': env_formset})