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 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
 
 
 from qcg.utils import username_from_dn, get_attributes
 
@@ -20,6 +20,10 @@ class User(AbstractUser):
         return Task.objects.filter(job__owner=self)
 
 
         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}
 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='')
     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)
     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):
 
     @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['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)
         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='')
     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)
     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['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:
         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' %}
 {% extends 'qcg/base.html' %}
-{% load qcg_utils %}
+{% load qcg_utils bootstrap3 %}
 
 {% block container %}
     <ol class="breadcrumb">
 
 {% block container %}
     <ol class="breadcrumb">
 
             </div>
             <div role="tabpanel" class="tab-pane" id="desc">
 
             </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>
 
             </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 %}
 {% endblock container %}
index e176eb3..1fb673a 100644 (file)
@@ -1,4 +1,5 @@
 from datetime import datetime, timedelta
 from datetime import datetime, timedelta
+from pyexpat import ExpatError
 from xml.dom import minidom
 
 from django import template
 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
 @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/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'),
 
     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 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
 
 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'])
 
 
     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
 
 
 @login_required
@@ -310,3 +313,22 @@ def template_delete(request, template_id):
                                           'Usunięto szablon <em>{}</em>.', template.name))
 
     return redirect('job_templates')
                                           '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
 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
 pkgs/python-gridftp-1.3.4.tar.gz
 python-openid
 pytz