from pyqcg.utils import TaskStatus
from qcg.fields import TimeRangeField
-from qcg.models import Task, Allocation
+from qcg.models import Task, Allocation, JobTemplate
date_range_validator = RegexValidator(r'[0-9]{2}\.[0-9]{2}\.[0-9]{4} - [0-9]{2}\.[0-9]{2}\.[0-9]{4}')
columns = forms.MultipleChoiceField(choices=COLUMNS_CHOICES, initial=[k for k, v in COLUMNS_CHOICES[1:]],
label=u"Kolumny", required=False, widget=forms.CheckboxSelectMultiple)
+
+
+class JobTemplateForm(forms.ModelForm):
+ class Meta:
+ model = JobTemplate
+ fields = ('name',)
def __unicode__(self):
return u"{} ({})".format(self.name, self.owner)
+
+ def get_absolute_url(self):
+ return reverse('template_submit', kwargs={'template_id': self.id})
elapsed, job.job_id, elapsed_job, job.tasks.count(), elapsed_tasks, elapsed_py)
-def submit_job(params, proxy):
+def make_job_desc(params, proxy):
QCG.start()
desc = JobDescription(Credential(proxy))
desc.set_watch_output(params['watch_output'], params['watch_output_pattern'])
# TODO monitoring
- job = desc.submit()
-
- return job.job_id
+ return desc
def cancel(obj, proxy):
<li{% ifequal request.resolver_match.url_name 'jobs' %} class="active"{% endifequal %}>
<a href="{% url 'jobs' %}">Zadania</a>
</li>
- <li{% ifequal request.resolver_match.url_name 'job_submit' %} class="active"{% endifequal %}>
+ <li{% if request.resolver_match.url_name == 'job_submit' or request.resolver_match.url_name == 'template_submit' %} class="active"{% endif %}>
<a href="{% url 'job_submit' %}">Zleć zadanie</a>
</li>
<li{% ifequal request.resolver_match.url_name 'gridftp' %} class="active"{% endifequal %}>
</a>
<ul class="dropdown-menu">
<li><a href="{% url 'job_templates' %}">
- <span class="glyphicon glyphicon-floppy-disk"></span> Zapisane zadania</a>
+ <span class="glyphicon glyphicon-floppy-disk"></span> Szablony zadania</a>
</li>
{% if request.user.is_superuser %}
$('#gridftp').one('show.bs.modal', function() {
filex.initialLoad();
});
+
+ $('#template').on('show.bs.modal', function() {
+ $(this).find('.alert').remove();
+ this.reset();
+ }).on('shown.bs.modal', function() {
+ $(this).find('input[type="text"]')[0].focus();
+ }).on('submit', function(e) {
+ e.preventDefault();
+
+ var $this = $(this),
+ $btn = $this.find('[type="submit"]');
+
+ $this.find('.alert-danger').remove();
+ $btn.button('loading');
+
+ $.post(this.action, $('#description,#template').serializeArray(), function(response) {
+ // FIXME url after redirection
+ document.open();
+ document.write(response);
+ document.close();
+ }).fail(function(xhr) {
+ var error = (xhr.responseJSON || {}).error || 'Błąd serwera';
+
+ $('<div>', {
+ 'class': 'alert alert-danger',
+ html: '<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> ' + error
+ }).prependTo($this.find('.modal-body'));
+
+ $btn.button('reset');
+ });
+ });
});
</script>
{% endblock %}
{% block container %}
- <h1 class="page-header">{% block title %}Zleć zadanie{% endblock %}</h1>
+ <div class="pull-right">
+ {% if template %}
+ <button class="btn btn-default" data-toggle="modal" data-target="#template">Zapisz</button>
+ <button class="btn btn-default" data-toggle="modal" data-target="#delete-modal">Usuń</button>
+ {% else %}
+ <button class="btn btn-default" data-toggle="modal" data-target="#template">Zapisz jako szablon</button>
+ {% endif %}
+ </div>
+
+ <h1 class="page-header">{% block title %}Zleć zadanie{% endblock %} <small>{{ template.name }}</small></h1>
{% if errors %}
<div class="alert alert-danger">
<strong>Uwaga!</strong> Formularz zawiera błędy.
{{ form.non_field_errors }}
{{ env_formset.non_field_errors }}
+ {{ template_form.non_field_errors }}
</div>
{% endif %}
- <form action="." method="post" class="form-horizontal">
+ <form id="description" action="." method="post" class="form-horizontal">
{% csrf_token %}
<!-- Nav tabs -->
</div>
</form>
- <div id="gridftp" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="modal-label" aria-hidden="true">
+ <div id="gridftp" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="gridftp-modal-label" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
- <h4 class="modal-title" id="modal-label">Wybierz plik</h4>
+ <h4 class="modal-title" id="gridftp-modal-label">Wybierz plik</h4>
</div>
<div class="modal-body">
{% include 'filex/source.html' %}
</div>
</div>
+ <form id="template" action="." method="post" class="modal fade form-horizontal" tabindex="-1"
+ role="dialog" aria-labelledby="template-modal-label" aria-hidden="true">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
+ <h4 class="modal-title" id="template-modal-label">Podaj nazwę szablonu</h4>
+ </div>
+ <div class="modal-body">
+ {% csrf_token %}
+ {% bootstrap_field template_form.name layout="horizontal" %}
+ <input type="hidden" name="save-template" value="1">
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal">Anuluj</button>
+ <button type="submit" class="btn btn-primary" data-loading-text="Zapisywanie...">Zapisz</button>
+ </div>
+ </div>
+ </div>
+ </form>
+
+ {% if template %}
+ <form id="delete-modal" action="{% url 'template_delete' template.id %}" method="post" class="modal fade" tabindex="-1"
+ role="dialog" aria-labelledby="delete-modal-label" aria-hidden="true">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
+ <h4 class="modal-title" id="delete-modal-label">Usuwanie szablonu</h4>
+ </div>
+ <div class="modal-body">
+ {% csrf_token %}
+ <p>Czy na pewno usunąć szablon <em>{{ template.name }}</em>?</p>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal">Anuluj</button>
+ <button type="submit" class="btn btn-danger">Usuń</button>
+ </div>
+ </div>
+ </div>
+ </form>
+ {% endif %}
{% endblock %}
{% extends 'qcg/base.html' %}
+{% block extra_js %}
+ <script>
+ $(function() {
+ $('.delete-template').on('click', function() {
+ var $this = $(this),
+ $modal = $('#delete-modal');
+
+ $modal.attr('action', $this.data('url'));
+ $modal.find('em').text($this.data('name'));
+ });
+ });
+ </script>
+{% endblock %}
+
{% block container %}
<h1 class="page-header">{% block title %}Szablony zadania{% endblock %}</h1>
{% for template in templates %}
<tr>
<td class="text-right">{{ forloop.counter }}</td>
- <td><a href="#">{{ template.name }}</a></td>
+ <td><a href="{{ template.get_absolute_url }}">{{ template.name }}</a></td>
<td>{{ template.created }}</td>
<td>{{ template.updated }}</td>
<td class="text-right">
- <button class="btn btn-xs btn-danger" title="Usuń">
+ <button class="btn btn-xs btn-danger delete-template" title="Usuń"
+ data-url="{% url 'template_delete' template.id %}"
+ data-name="{{ template.name }}"
+ data-toggle="modal" data-target="#delete-modal">
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
</button>
</td>
</div>
{% endif %}
+ <form id="delete-modal" action="" method="post" class="modal fade" tabindex="-1"
+ role="dialog" aria-labelledby="delete-modal-label" aria-hidden="true">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
+ <h4 class="modal-title" id="delete-modal-label">Usuwanie szablonu</h4>
+ </div>
+ <div class="modal-body">
+ {% csrf_token %}
+ <p>Czy na pewno usunąć szablon <em></em>?</p>
+ </div>
+ <div class="modal-footer">
+ <button type="button" class="btn btn-default" data-dismiss="modal">Anuluj</button>
+ <button type="submit" class="btn btn-danger">Usuń</button>
+ </div>
+ </div>
+ </div>
+ </form>
+
{% endblock %}
url(r'^job/templates/$', views.job_templates, name='job_templates'),
url(r'^job/submit/$', views.job_submit, name='job_submit'),
+ url(r'^job/template/submit/(?P<template_id>\d+)/$', views.job_submit, name='template_submit'),
+ url(r'^job/template/delete/(?P<template_id>\d+)/$', views.template_delete, name='template_delete'),
+
url(r'^job/cancel/(?P<job_id>[\w]+)/$', views.job_cancel, name='job_cancel'),
url(r'^job/clean/(?P<job_id>[\w]+)/$', views.job_clean, name='job_clean'),
url(r'^task/cancel/(?P<job_id>[\w]+)/(?P<task_id>[\w]+)/$', views.task_cancel, name='task_cancel'),
from filex.forms import HostPathNameForm, RenameForm, ArchiveForm, HostPathForm
from filex.ftp import FTPOperation, FTPError
from filex.views import make_url
-from qcg.forms import FiltersForm, ColumnsForm, JobDescriptionForm, EnvFormSet
+from qcg.forms import FiltersForm, ColumnsForm, JobDescriptionForm, EnvFormSet, JobTemplateForm
from qcg.utils import paginator_context
-from qcg.service import update_user_data, submit_job, update_job, cancel, clean
+from qcg.service import update_user_data, make_job_desc, update_job, cancel, clean
def index(request):
@login_required
-def job_submit(request):
+def job_submit(request, template_id=None):
+ save_template = 'save-template' in request.POST
+ template = get_object_or_404(request.user.templates, id=template_id) if template_id is not None else None
+
if request.method == 'POST':
form = JobDescriptionForm(request.POST)
env_formset = EnvFormSet(request.POST)
+ template_form = JobTemplateForm(request.POST, prefix='template', instance=template)
- if form.is_valid() and env_formset.is_valid():
+ if form.is_valid() and env_formset.is_valid() and (not save_template or template_form.is_valid):
params = form.cleaned_data
params['env_variables'] = [(env['name'], env['value'])
for env in env_formset.cleaned_data if env and not env['DELETE']]
- job_id = submit_job(params, request.session['proxy'])
+ job_desc = make_job_desc(params, request.session['proxy'])
+
+ if save_template:
+ if template is not None:
+ # save already existing template...
+ instance = template_form.save()
+ else:
+ # or set attributes for new template
+ instance = template_form.save(commit=False)
+
+ instance.owner = request.user
+ instance.description = job_desc.xml_description
+ instance.save()
+
+ return redirect(instance)
+
+ else:
+ job = job_desc.submit()
- messages.success(request,
- format_html('<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> '
- 'Zlecono zadanie <em>{}</em>.', job_id))
+ messages.success(request,
+ format_html('<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> '
+ 'Zlecono zadanie <em>{}</em>.', job.job_id))
- return redirect('jobs')
+ return redirect('jobs')
- print repr(form.errors)
- print repr(env_formset.errors)
+ print 'form', repr(form.errors)
+ print 'env_formset', repr(env_formset.errors)
+ print 'template_form', repr(template_form.errors)
else:
form = JobDescriptionForm()
env_formset = EnvFormSet()
+ template_form = JobTemplateForm(prefix='template', instance=template)
- errors = form.errors or (env_formset.is_bound and not env_formset.is_valid)
+ errors = form.errors or (env_formset.is_bound and not env_formset.is_valid) or (
+ save_template and template_form.errors)
- return render(request, 'qcg/job_submit.html', {'form': form, 'env_formset': env_formset, 'errors': errors})
+ return render(request, 'qcg/job_submit.html', {'form': form, 'env_formset': env_formset, 'errors': errors,
+ 'template_form': template_form, 'template': template})
@require_POST
@login_required
def job_templates(request):
return render(request, 'qcg/job_templates.html', {'templates': request.user.templates.all()})
+
+
+@require_POST
+@login_required
+def template_delete(request, template_id):
+ get_object_or_404(request.user.templates, id=template_id).delete()
+
+ return redirect('job_templates')