From: Maciej Tronowski Date: Wed, 4 Mar 2015 09:22:22 +0000 (+0100) Subject: Merge remote-tracking branch 'origin/master' X-Git-Tag: v1.0~140 X-Git-Url: http://mmka.chem.univ.gda.pl/gitweb/?a=commitdiff_plain;h=7c9392626226b7d859c5bc321ab15326fa1a17c6;hp=acdd4e574dd1ec6f3b901cbdaefb2c155f09bbf5;p=qcg-portal.git Merge remote-tracking branch 'origin/master' # Conflicts: # qcg/fields.py # qcg/forms.py # qcg/templates/qcg/job_new.html --- diff --git a/qcg/fields.py b/qcg/fields.py index 3e5ec44..e067f46 100644 --- a/qcg/fields.py +++ b/qcg/fields.py @@ -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: diff --git a/qcg/forms.py b/qcg/forms.py index 7b11d13..b2ad790 100644 --- a/qcg/forms.py +++ b/qcg/forms.py @@ -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) diff --git a/qcg/models.py b/qcg/models.py index 451d7fb..b41fe85 100644 --- a/qcg/models.py +++ b/qcg/models.py @@ -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} diff --git a/qcg/static/qcg/main.css b/qcg/static/qcg/main.css index 4edc075..b3bdc3b 100644 --- a/qcg/static/qcg/main.css +++ b/qcg/static/qcg/main.css @@ -19,6 +19,7 @@ footer.navbar-fixed-bottom { textarea { resize: vertical; + min-height: 34px; } diff --git a/qcg/templates/qcg/job_new.html b/qcg/templates/qcg/job_new.html index 37a4fc5..d5b7344 100644 --- a/qcg/templates/qcg/job_new.html +++ b/qcg/templates/qcg/job_new.html @@ -96,7 +96,6 @@
  • Zasoby
  • Pliki
  • Środowisko
  • -
  • Rezerwacja
  • Powiadomienia
  • Inne
  • @@ -111,9 +110,9 @@ - {% 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" %} @@ -121,23 +120,23 @@
    {% 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" %}
    {% 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" %}
    - {% bootstrap_field form.properties layout="horizontal" %} {{ env_formset.management_form }}
    @@ -154,15 +153,6 @@
    - - {% bootstrap_field form.modules layout="horizontal" %} -
    - -
    - {% 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" %}
    @@ -191,27 +181,29 @@ -
    - -
    -
    - {% for option in form.watch_output_type %} - - {% endfor %} +
    +
    + +
    +
    + {% for option in form.watch_output_type %} + + {% endfor %} +
    -
    -
    -
    +
    +
    -
    - {% bootstrap_field form.watch_output_address layout="horizontal" %} - {% bootstrap_field form.watch_output_pattern layout="horizontal" %} +
    + {% bootstrap_field form.watch_output_address layout="horizontal" %} + {% bootstrap_field form.watch_output_pattern layout="horizontal" %} +
    @@ -273,7 +265,6 @@ {% bootstrap_field form.native layout="horizontal" %} {% bootstrap_checkbox form.persistent %} - {% bootstrap_checkbox form.use_scratch %} diff --git a/qcg/views.py b/qcg/views.py index 7a8f351..c34af9b 100644 --- a/qcg/views.py +++ b/qcg/views.py @@ -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})