From d44db64da1a19d79f9e80f941363cbd35831e6c3 Mon Sep 17 00:00:00 2001 From: Maxime Bombar Date: Sat, 8 Sep 2018 12:46:52 +0200 Subject: [PATCH] [Printer] Upload file to print and manages options --- printer/__init__.py | 0 printer/forms.py | 32 +++++- printer/migrations/0004_auto_20180907_1607.py | 32 ++++++ .../0005_jobwithoptions_filename.py | 20 ++++ printer/migrations/__init__.py | 0 printer/models.py | 13 ++- printer/templates/printer/newjob.html | 4 +- printer/templates/printer/print.html | 84 +++++++++++++++ printer/utils.py | 28 +++++ printer/validators.py | 1 - printer/views.py | 102 +++++++++++++++--- 11 files changed, 294 insertions(+), 22 deletions(-) create mode 100644 printer/__init__.py create mode 100644 printer/migrations/0004_auto_20180907_1607.py create mode 100644 printer/migrations/0005_jobwithoptions_filename.py create mode 100644 printer/migrations/__init__.py create mode 100644 printer/templates/printer/print.html create mode 100644 printer/utils.py diff --git a/printer/__init__.py b/printer/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/printer/forms.py b/printer/forms.py index 3d60fd38..1b721c95 100644 --- a/printer/forms.py +++ b/printer/forms.py @@ -25,13 +25,43 @@ class JobWithOptionsForm(FormRevMixin, ModelForm): def __init__(self, *args, **kwargs): prefix = kwargs.pop('prefix', self.Meta.model.__name__) super(JobWithOptionsForm, self).__init__(*args, prefix=prefix, **kwargs) + self.fields['printAs'].label = 'Print As' + self.fields['printAs'].empty_label = 'Print As' + self.fields['disposition'].label = 'disposition' + self.fields['color'].label = 'color' + self.fields['count'].label = 'count' class Meta: model = JobWithOptions fields = [ 'file', + 'printAs', 'color', 'disposition', 'count', ] - + + +class PrintForm(FormRevMixin, ModelForm): + def __init__(self, *args, **kwargs): + prefix = kwargs.pop('prefix', self.Meta.model.__name__) + super(PrintForm, self).__init__(*args, prefix=prefix, **kwargs) + self.fields['printAs'].label = 'Print As' + self.fields['disposition'].label = 'disposition' + self.fields['color'].label = 'color' + self.fields['count'].label = 'count' + + self.fields['printAs'].widget.attrs['readonly'] = True + self.fields['filename'].widget.attrs['readonly'] = True + self.fields['price'].widget.attrs['readonly'] = True + self.fields['pages'].widget.attrs['readonly'] = True + + class Meta: + model = JobWithOptions + exclude = [ + 'user', + 'starttime', + 'endtime', + 'status', + 'file', + ] diff --git a/printer/migrations/0004_auto_20180907_1607.py b/printer/migrations/0004_auto_20180907_1607.py new file mode 100644 index 00000000..8da73927 --- /dev/null +++ b/printer/migrations/0004_auto_20180907_1607.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-09-07 14:07 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('printer', '0003_auto_20180803_0854'), + ] + + operations = [ + migrations.AddField( + model_name='jobwithoptions', + name='pages', + field=models.IntegerField(default=0), + ), + migrations.AlterField( + model_name='jobwithoptions', + name='format', + field=models.CharField(choices=[('A4', 'A4'), ('A3', 'A3')], default='A4', max_length=255), + ), + migrations.AlterField( + model_name='jobwithoptions', + name='printAs', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='print_as_user', to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/printer/migrations/0005_jobwithoptions_filename.py b/printer/migrations/0005_jobwithoptions_filename.py new file mode 100644 index 00000000..03392146 --- /dev/null +++ b/printer/migrations/0005_jobwithoptions_filename.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-09-08 08:52 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('printer', '0004_auto_20180907_1607'), + ] + + operations = [ + migrations.AddField( + model_name='jobwithoptions', + name='filename', + field=models.CharField(max_length=255, null=True), + ), + ] diff --git a/printer/migrations/__init__.py b/printer/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/printer/models.py b/printer/models.py index f16e620d..55ccda40 100644 --- a/printer/models.py +++ b/printer/models.py @@ -3,7 +3,6 @@ """printer.models Models of the printer application Author : Maxime Bombar . -Date : 29/06/2018 """ from __future__ import unicode_literals @@ -44,6 +43,7 @@ class JobWithOptions(RevMixin, models.Model): - ```user``` is a ForeignKey to the User Application - ```file``` is the file to print + - ```filename``` is the name of the file to print - ```starttime``` is the time when the job was launched - ```endtime``` is the time when the job was stopped. A job is stopped when it is either finished or cancelled. @@ -72,15 +72,16 @@ class JobWithOptions(RevMixin, models.Model): ) user = models.ForeignKey('users.User', on_delete=models.PROTECT) file = models.FileField(upload_to=user_printing_path, validators=[FileValidator(allowed_types=ALLOWED_TYPES, max_size=MAX_PRINTFILE_SIZE)]) + filename = models.CharField(max_length=255,null=True) starttime = models.DateTimeField(auto_now_add=True) endtime = models.DateTimeField(null=True) status = models.CharField(max_length=255, choices=STATUS_AVAILABLE) - printAs = models.ForeignKey('users.User', on_delete=models.PROTECT, related_name='print_as_user', null=True) + printAs = models.ForeignKey('users.User', on_delete=models.PROTECT, related_name='print_as_user', blank=True, null=True) price = models.IntegerField(default=0) - + pages = models.IntegerField(default=0) FORMAT_AVAILABLE = ( ('A4', 'A4'), - ('A3', 'A4'), + ('A3', 'A3'), ) COLOR_CHOICES = ( ('Greyscale', 'Greyscale'), @@ -114,3 +115,7 @@ class JobWithOptions(RevMixin, models.Model): count = models.PositiveIntegerField(default=1) stapling = models.CharField(max_length=255, choices=STAPLING_OPTIONS, default='None') perforation = models.CharField(max_length=255, choices=PERFORATION_OPTIONS, default='None') + + + def _update_price(self): + self.price = 0 diff --git a/printer/templates/printer/newjob.html b/printer/templates/printer/newjob.html index 1167e18e..e9644c03 100644 --- a/printer/templates/printer/newjob.html +++ b/printer/templates/printer/newjob.html @@ -16,7 +16,7 @@
{% for job in jobform.forms %}
- {% bootstrap_form job label_class='sr-only' %} + {% massive_bootstrap_form job "" label_class='sr-only' %} @@ -24,7 +24,7 @@ {% endfor %}
- {% bootstrap_button action_name button_type="submit" icon="star" %} + {% bootstrap_button action_name name="options" button_type="submit" icon="star" %} --> +{% endblock %} + diff --git a/printer/utils.py b/printer/utils.py new file mode 100644 index 00000000..89a6b522 --- /dev/null +++ b/printer/utils.py @@ -0,0 +1,28 @@ +import subprocess + + +def pdfinfo(file_path): + """ + Uses pdfinfo to extract the PDF meta information. + Returns metainfo in a dictionary. + requires poppler-utils + """ + def _extract(row): + """Extracts the right hand value from a : delimited row""" + row=row.decode() + return row.split(':', 1)[1].strip() + + output = {} + + labels = ['Title', 'Author', 'Creator', 'Producer', 'CreationDate', 'ModDate', + 'Tagged', 'Pages', 'Encrypted', 'Page size', + 'File size', 'Optimized', 'PDF version'] + + cmd_output = subprocess.check_output(['/usr/bin/pdfinfo', file_path]) + for line in cmd_output.splitlines(): + for label in labels: + if label in line.decode(): + output[label] = _extract(line) + + return output + diff --git a/printer/validators.py b/printer/validators.py index 069383cb..863cc6c6 100644 --- a/printer/validators.py +++ b/printer/validators.py @@ -4,7 +4,6 @@ """printer.validators Custom validators useful for printer application. Author : Maxime Bombar . -Date : 29/06/2018 """ diff --git a/printer/views.py b/printer/views.py index 6767ab9c..e7c932f7 100644 --- a/printer/views.py +++ b/printer/views.py @@ -2,7 +2,6 @@ """printer.views The views for the printer app Author : Maxime Bombar . -Date : 29/06/2018 """ from __future__ import unicode_literals @@ -10,6 +9,7 @@ from __future__ import unicode_literals from django.urls import reverse from django.shortcuts import render, redirect from django.forms import modelformset_factory, formset_factory +from django.forms.models import model_to_dict from django.contrib.auth.decorators import login_required from re2o.views import form @@ -17,31 +17,105 @@ from users.models import User from . import settings +from .utils import pdfinfo + +from .models import ( + JobWithOptions, + ) + from .forms import ( JobWithOptionsForm, + PrintForm, ) +from django.core.exceptions import ValidationError + @login_required def new_job(request): """ View to create a new printing job """ - job_formset = formset_factory(JobWithOptionsForm)( - request.POST or None, request.FILES or None, - ) - if job_formset.is_valid(): - for job in job_formset: - job = job.save(commit=False) - job.user=request.user - job.status='Printable' - job.save() - return redirect(reverse( - 'printer:success', - )) + if request.method == 'POST': + if request.FILES: + job_formset = formset_factory(JobWithOptionsForm)( + request.POST, + request.FILES, + ) + + if job_formset.is_valid(): + files = request.FILES + data = [] + i=0 + for job in job_formset: + # raise ValidationError("'%(path)s'", code='path', params = {'path': job.cleaned_data["file"].name}) + filename = job.cleaned_data['file'].name + job = job.save(commit=False) + job.filename = filename + job.user=request.user + job.status='Printable' + # raise + # raise ValidationError("'%(path)s'", code='path', params = {'path': request.FILES['form-%s-file' % i].temporary_file_path()}) + metadata = pdfinfo(request.FILES['form-%s-file' % i].temporary_file_path()) + job.pages = metadata["Pages"] + # raise ValidationError("'%(path)s'", code='path', params = {'path': type(job)}) + # job.save() + # job_data = model_to_dict(job) + # job_data['file'] = request.FILES['form-%s-file' % i] + # raise ValidationError("'%(plop)s'", code='plop', params = {'plop': job_data }) + # raise ValidationError("'%(path)s'", code='path', params = {'path': request.session }) + job._update_price() + job.save() + job_data = model_to_dict(job) + request.session['id-form-%s-file' % i] = job.id + request.session['form-%s-file' % i] = request.FILES['form-%s-file' % i].temporary_file_path() + # raise ValidationError("'%(plop)s'", code='plop', params = {'plop': job_data }) + data.append(job_data) + i+=1 + job_formset_filled_in = formset_factory(PrintForm, extra=0)(initial=data) + return form( + { + 'jobform': job_formset_filled_in, + 'action_name' : 'Print', + }, + 'printer/print.html', + request + ) + + # elif 'Print' in request.POST: + # raise ValidationError("'%(path)s'", code='path', params = {'path': request.POST }) + + # raise Exception('On a déjà upload !') + n = int(request.POST['form-TOTAL_FORMS']) + job_formset = formset_factory(PrintForm)( + request.POST, + ) + id_list = [request.session['id-form-%s-file' % i] for i in range(n)] + # raise ValidationError("'%(path)s'", code='path', params = {'path': id_list }) + if job_formset.is_valid(): + for job_obj in job_formset: + i=0 + old_job = JobWithOptions.objects.get(id=id_list[i]) + job = job_obj.save(commit=False) + job.user = request.user + job.status = 'Running' + job.file = old_job.file + job.save() + i+=1 + # raise ValidationError("'%(plop)s'", code='plop', params = {'plop': request.method}) + # raise ValidationError("'%(path)s'", code='path', params = {'path': str(n) }) + return redirect(reverse( + 'printer:success', + )) + raise Exception("Invalid Job_formset") + + else: + job_formset = formset_factory(JobWithOptionsForm)( + None, + ) return form( { 'jobform': job_formset, - 'action_name': "Print", + 'action_name': "Advanced Options", }, 'printer/newjob.html', request