creating template from submitted jobs
authorMaciej Tronowski <mtro@man.poznan.pl>
Tue, 5 May 2015 12:09:56 +0000 (14:09 +0200)
committerMaciej Tronowski <mtro@man.poznan.pl>
Tue, 5 May 2015 12:09:56 +0000 (14:09 +0200)
pkgs/pyqcg-0.6.1.tar.gz [new file with mode: 0644]
pkgs/pyqcg-0.6.tar.gz [deleted file]
qcg/migrations/0004_auto_20150505_1202.py [new file with mode: 0644]
qcg/models.py
qcg/templates/qcg/job.html
qcg/templatetags/qcg_utils.py
qcg/urls.py
qcg/views.py
requirements.txt

diff --git a/pkgs/pyqcg-0.6.1.tar.gz b/pkgs/pyqcg-0.6.1.tar.gz
new file mode 100644 (file)
index 0000000..ff98756
Binary files /dev/null and b/pkgs/pyqcg-0.6.1.tar.gz differ
diff --git a/pkgs/pyqcg-0.6.tar.gz b/pkgs/pyqcg-0.6.tar.gz
deleted file mode 100644 (file)
index ba13a7a..0000000
Binary files a/pkgs/pyqcg-0.6.tar.gz and /dev/null differ
diff --git a/qcg/migrations/0004_auto_20150505_1202.py b/qcg/migrations/0004_auto_20150505_1202.py
new file mode 100644 (file)
index 0000000..1909bdf
--- /dev/null
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('qcg', '0003_jobtemplate'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='job',
+            name='description_type',
+            field=models.IntegerField(blank=True, null=True, verbose_name='Typ opisu', choices=[(0, b'JSDL'), (1, b'QCG'), (2, b'QCG_SIMPLE')]),
+            preserve_default=True,
+        ),
+        migrations.AddField(
+            model_name='job',
+            name='qcg_description',
+            field=models.TextField(default=b'', verbose_name='Opis QCG', blank=True),
+            preserve_default=True,
+        ),
+        migrations.AddField(
+            model_name='task',
+            name='description_type',
+            field=models.IntegerField(blank=True, null=True, verbose_name='Typ opisu', choices=[(0, b'JSDL'), (1, b'QCG'), (2, b'QCG_SIMPLE')]),
+            preserve_default=True,
+        ),
+    ]
index 580944c..316f56e 100644 (file)
@@ -4,7 +4,7 @@ from django.core.urlresolvers import reverse
 from django.db import models
 from django.utils.timezone import now
 from pyqcg.service import Job as QcgJob, Task as QcgTask
-from pyqcg.utils import JobStatus, TaskStatus, TaskType, AllocationType, EPRUtils
+from pyqcg.utils import JobStatus, TaskStatus, TaskType, AllocationType, EPRUtils, DescriptionType
 
 from qcg.utils import username_from_dn, get_attributes
 
@@ -20,6 +20,10 @@ class User(AbstractUser):
         return Task.objects.filter(job__owner=self)
 
 
+DESC_TYPE_CHOICES = list(enumerate(field for field in dir(DescriptionType) if not field.startswith('__')))
+DESC_TYPE_CHOICES_REVERSED = {v: k for k, v in DESC_TYPE_CHOICES}
+
+
 class Job(models.Model):
     STATUS_CHOICES = list(enumerate(field for field in dir(JobStatus) if not field.startswith('__')))
     STATUS_CHOICES_REVERSED = {v: k for k, v in STATUS_CHOICES}
@@ -31,6 +35,8 @@ class Job(models.Model):
     status = models.IntegerField(u"Status", choices=STATUS_CHOICES)
     note = models.TextField(u"Notatka", blank=True, default='')
     description = models.TextField(u"Opis", blank=True, default='')
+    description_type = models.IntegerField(u"Typ opisu", choices=DESC_TYPE_CHOICES, blank=True, null=True)
+    qcg_description = models.TextField(u"Opis QCG", blank=True, default='')
     project = models.TextField(u"Projekt", blank=True, default='')
     submission_time = models.DateTimeField(u"Data wysłania")
     finish_time = models.DateTimeField(u"Data zakończenia", blank=True, null=True)
@@ -61,11 +67,12 @@ class Job(models.Model):
 
     @staticmethod
     def qcg_map(qcg_job, user=None):
-        attrs = get_attributes(qcg_job, ('job_id', 'note', 'description', 'submission_time', 'finish_time',
-                                         'project', 'purged'))
+        attrs = get_attributes(qcg_job, ('job_id', 'note', 'description', 'qcg_description', 'submission_time',
+                                         'finish_time', 'project', 'purged'))
 
         attrs['epr'] = EPRUtils.serialize_epr(qcg_job.epr)
         attrs['status'] = Job.STATUS_CHOICES_REVERSED[qcg_job.status]
+        attrs['description_type'] = DESC_TYPE_CHOICES_REVERSED[qcg_job.description_type]
         attrs['proxy_lifetime'] = now() + qcg_job.proxy_lifetime
 
         username = username_from_dn(qcg_job.user_dn)
@@ -100,6 +107,7 @@ class Task(models.Model):
     type = models.IntegerField(u"Typ", choices=TYPE_CHOICES)
     note = models.TextField(u"Notatka", blank=True, default='')
     description = models.TextField(u"Opis", blank=True, default='')
+    description_type = models.IntegerField(u"Typ opisu", choices=DESC_TYPE_CHOICES, blank=True, null=True)
     submission_time = models.DateTimeField(u"Data wysłania")
     start_time = models.DateTimeField(u"Data rozpoczęcia", blank=True, null=True)
     finish_time = models.DateTimeField(u"Data zakończenia", blank=True, null=True)
@@ -138,6 +146,7 @@ class Task(models.Model):
         attrs['epr'] = EPRUtils.serialize_epr(qcg_task.epr)
         attrs['status'] = Task.STATUS_CHOICES_REVERSED[qcg_task.status]
         attrs['type'] = Task.TYPE_CHOICES_REVERSED[qcg_task.type]
+        attrs['description_type'] = DESC_TYPE_CHOICES_REVERSED[qcg_task.description_type]
         attrs['proxy_lifetime'] = now() + qcg_task.proxy_lifetime
 
         if job is not None:
index d20049d..d3c3922 100644 (file)
@@ -1,5 +1,5 @@
 {% extends 'qcg/base.html' %}
-{% load qcg_utils %}
+{% load qcg_utils bootstrap3 %}
 
 {% block container %}
     <ol class="breadcrumb">
 
             </div>
             <div role="tabpanel" class="tab-pane" id="desc">
-                <pre>{{ job.description }}</pre>
+                <button class="btn btn-default btn-sm pull-right" data-toggle="modal" data-target="#template">Zapisz jako szablon</button>
+
+                {% if job.get_description_type_display != 'QCG' %}
+                    <h3>Opis użytkownika</h3>
+                    <pre>{{ job.description }}</pre>
+                {% endif %}
+
+                <h3>Opis QCG</h3>
+                <pre>{{ job.qcg_description|format_xml }}</pre>
             </div>
         </div>
     </div>
 
+    <form id="template" action="{% url 'job_save_template' job.job_id %}" 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">&times;</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" %}
+                </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>
+
 {% endblock container %}
index e176eb3..1fb673a 100644 (file)
@@ -1,4 +1,5 @@
 from datetime import datetime, timedelta
+from pyexpat import ExpatError
 from xml.dom import minidom
 
 from django import template
@@ -46,4 +47,7 @@ def bootstrap_checkbox(field, label_class=settings.BOOTSTRAP3['horizontal_label_
 @register.filter(needs_autoescape=True)
 def format_xml(string, autoescape=None):
     esc = conditional_escape if autoescape else lambda x: x
-    return esc(minidom.parseString(string).toprettyxml(indent='    '))
+    try:
+        return esc(minidom.parseString(string).toprettyxml(indent='    '))
+    except ExpatError:
+        return string
index 57aea13..f78447d 100644 (file)
@@ -21,6 +21,7 @@ urlpatterns = patterns('',
 
     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'^job/save/template/(?P<job_id>[\w]+)/$', views.job_save_template, name='job_save_template'),
     url(r'^task/cancel/(?P<job_id>[\w]+)/(?P<task_id>[\w]+)/$', views.task_cancel, name='task_cancel'),
     url(r'^task/clean/(?P<job_id>[\w]+)/(?P<task_id>[\w]+)/$', views.task_clean, name='task_clean'),
 
index 2dc5e8f..6462613 100644 (file)
@@ -22,6 +22,7 @@ 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, JobTemplateForm
+from qcg.models import JobTemplate
 from qcg.utils import paginator_context, to_job_desc, to_form_data
 from qcg.service import update_user_data, update_job, cancel, clean
 
@@ -156,7 +157,9 @@ def job_details(request, job_id):
 
     update_job(job, request.session['proxy'])
 
-    return render(request, 'qcg/job.html', {'job': job})
+    template_form = JobTemplateForm(initial={'name': job.note or u'Szablon z {}'.format(job.job_id)})
+
+    return render(request, 'qcg/job.html', {'job': job, 'template_form': template_form})
 
 
 @login_required
@@ -310,3 +313,22 @@ def template_delete(request, template_id):
                                           'Usunięto szablon <em>{}</em>.', template.name))
 
     return redirect('job_templates')
+
+
+@require_POST
+@login_required
+def job_save_template(request, job_id):
+    job = get_object_or_404(request.user.jobs, job_id=job_id)
+
+    template_form = JobTemplateForm(request.POST,
+                                    instance=JobTemplate(owner=request.user, description=job.qcg_description))
+
+    if template_form.is_valid():
+        template = template_form.save()
+
+        return redirect(template)
+
+    messages.error(request, format_html('<span class="glyphicon glyphicon-info-sign" aria-hidden="true"></span> {}'
+                                        'Nie udało się zapisać szablonu!', template_form.errors))
+
+    return redirect(job)
index 41e2068..4671907 100644 (file)
@@ -2,7 +2,7 @@ Django<1.8
 django-grappelli
 django-bootstrap3
 pkgs/django-openid-auth-0.5.1.tar.gz
-pkgs/pyqcg-0.6.tar.gz
+pkgs/pyqcg-0.6.1.tar.gz
 pkgs/python-gridftp-1.3.4.tar.gz
 python-openid
 pytz