mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-25 04:43:10 +00:00
[Printer] Upload file to print and manages options
This commit is contained in:
parent
32b668ac85
commit
d44db64da1
11 changed files with 294 additions and 22 deletions
0
printer/__init__.py
Normal file
0
printer/__init__.py
Normal file
|
@ -25,13 +25,43 @@ class JobWithOptionsForm(FormRevMixin, ModelForm):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||||
super(JobWithOptionsForm, self).__init__(*args, prefix=prefix, **kwargs)
|
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:
|
class Meta:
|
||||||
model = JobWithOptions
|
model = JobWithOptions
|
||||||
fields = [
|
fields = [
|
||||||
'file',
|
'file',
|
||||||
|
'printAs',
|
||||||
'color',
|
'color',
|
||||||
'disposition',
|
'disposition',
|
||||||
'count',
|
'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',
|
||||||
|
]
|
||||||
|
|
32
printer/migrations/0004_auto_20180907_1607.py
Normal file
32
printer/migrations/0004_auto_20180907_1607.py
Normal file
|
@ -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),
|
||||||
|
),
|
||||||
|
]
|
20
printer/migrations/0005_jobwithoptions_filename.py
Normal file
20
printer/migrations/0005_jobwithoptions_filename.py
Normal file
|
@ -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),
|
||||||
|
),
|
||||||
|
]
|
0
printer/migrations/__init__.py
Normal file
0
printer/migrations/__init__.py
Normal file
|
@ -3,7 +3,6 @@
|
||||||
"""printer.models
|
"""printer.models
|
||||||
Models of the printer application
|
Models of the printer application
|
||||||
Author : Maxime Bombar <bombar@crans.org>.
|
Author : Maxime Bombar <bombar@crans.org>.
|
||||||
Date : 29/06/2018
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
@ -44,6 +43,7 @@ class JobWithOptions(RevMixin, models.Model):
|
||||||
|
|
||||||
- ```user``` is a ForeignKey to the User Application
|
- ```user``` is a ForeignKey to the User Application
|
||||||
- ```file``` is the file to print
|
- ```file``` is the file to print
|
||||||
|
- ```filename``` is the name of the file to print
|
||||||
- ```starttime``` is the time when the job was launched
|
- ```starttime``` is the time when the job was launched
|
||||||
- ```endtime``` is the time when the job was stopped.
|
- ```endtime``` is the time when the job was stopped.
|
||||||
A job is stopped when it is either finished or cancelled.
|
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)
|
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)])
|
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)
|
starttime = models.DateTimeField(auto_now_add=True)
|
||||||
endtime = models.DateTimeField(null=True)
|
endtime = models.DateTimeField(null=True)
|
||||||
status = models.CharField(max_length=255, choices=STATUS_AVAILABLE)
|
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)
|
price = models.IntegerField(default=0)
|
||||||
|
pages = models.IntegerField(default=0)
|
||||||
FORMAT_AVAILABLE = (
|
FORMAT_AVAILABLE = (
|
||||||
('A4', 'A4'),
|
('A4', 'A4'),
|
||||||
('A3', 'A4'),
|
('A3', 'A3'),
|
||||||
)
|
)
|
||||||
COLOR_CHOICES = (
|
COLOR_CHOICES = (
|
||||||
('Greyscale', 'Greyscale'),
|
('Greyscale', 'Greyscale'),
|
||||||
|
@ -114,3 +115,7 @@ class JobWithOptions(RevMixin, models.Model):
|
||||||
count = models.PositiveIntegerField(default=1)
|
count = models.PositiveIntegerField(default=1)
|
||||||
stapling = models.CharField(max_length=255, choices=STAPLING_OPTIONS, default='None')
|
stapling = models.CharField(max_length=255, choices=STAPLING_OPTIONS, default='None')
|
||||||
perforation = models.CharField(max_length=255, choices=PERFORATION_OPTIONS, default='None')
|
perforation = models.CharField(max_length=255, choices=PERFORATION_OPTIONS, default='None')
|
||||||
|
|
||||||
|
|
||||||
|
def _update_price(self):
|
||||||
|
self.price = 0
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
<div id="form_set" class="form-group">
|
<div id="form_set" class="form-group">
|
||||||
{% for job in jobform.forms %}
|
{% for job in jobform.forms %}
|
||||||
<div class='file_to_print form-inline'>
|
<div class='file_to_print form-inline'>
|
||||||
{% bootstrap_form job label_class='sr-only' %}
|
{% massive_bootstrap_form job "" label_class='sr-only' %}
|
||||||
<button class="btn btn-danger btn-sm" id="id_form-0-job-remove" type="button">
|
<button class="btn btn-danger btn-sm" id="id_form-0-job-remove" type="button">
|
||||||
<span class="fa fa-times"></span>
|
<span class="fa fa-times"></span>
|
||||||
</button>
|
</button>
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
<input class="btn btn-primary btn-sm" role="button" value="{% trans "Add a file"%}" id="add_one">
|
<input class="btn btn-primary btn-sm" role="button" value="{% trans "Add a file"%}" id="add_one">
|
||||||
{% bootstrap_button action_name button_type="submit" icon="star" %}
|
{% bootstrap_button action_name name="options" button_type="submit" icon="star" %}
|
||||||
</form>
|
</form>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
|
84
printer/templates/printer/print.html
Normal file
84
printer/templates/printer/print.html
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
{% load massive_bootstrap_form %}
|
||||||
|
{% load static %}
|
||||||
|
{% block title %}Printing interface{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<form class="form" method="post" enctype="multipart/form-data">
|
||||||
|
{% csrf_token %}
|
||||||
|
<h3>{% trans "Confirm printing" %}</h3>
|
||||||
|
{{ jobform.management_form }}
|
||||||
|
{% bootstrap_formset_errors jobform %}
|
||||||
|
<div id="form_set" class="form-group">
|
||||||
|
{% for job in jobform.forms %}
|
||||||
|
<div class='file_to_print form-inline'>
|
||||||
|
{% massive_bootstrap_form job "" label_class='sr-only' %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<!-- <input class="btn btn-primary btn-sm" role="button" value="{% trans "Add a file"%}" id="add_one"> -->
|
||||||
|
{% bootstrap_button action_name name="options" button_type="submit" icon="star" %}
|
||||||
|
</form>
|
||||||
|
<!-- <script type="text/javascript"> -->
|
||||||
|
|
||||||
|
<!-- var template = `{% bootstrap_form jobform.empty_form label_class='sr-only' %} -->
|
||||||
|
<!-- <button class="btn btn-danger btn-sm" -->
|
||||||
|
<!-- id="id_form-__prefix__-job-remove" type="button"> -->
|
||||||
|
<!-- <span class="fa fa-times"></span> -->
|
||||||
|
<!-- </button>` -->
|
||||||
|
|
||||||
|
<!-- function add_job() { -->
|
||||||
|
<!-- var new_index = -->
|
||||||
|
<!-- document.getElementsByClassName('file_to_print').length; -->
|
||||||
|
<!-- document.getElementById('id_form-TOTAL_FORMS').value ++; -->
|
||||||
|
<!-- var new_job = document.createElement('div'); -->
|
||||||
|
<!-- new_job.className = 'file_to_print form-inline'; -->
|
||||||
|
<!-- new_job.innerHTML = template.replace(/__prefix__/g, new_index); -->
|
||||||
|
<!-- document.getElementById('form_set').appendChild(new_job); -->
|
||||||
|
<!-- add_listener_for_id(new_index); -->
|
||||||
|
<!-- } -->
|
||||||
|
|
||||||
|
|
||||||
|
<!-- function del_job(event){ -->
|
||||||
|
<!-- var job = event.target.parentNode; -->
|
||||||
|
<!-- job.parentNode.removeChild(job); -->
|
||||||
|
<!-- document.getElementById('id_form-TOTAL_FORMS').value --; -->
|
||||||
|
<!-- } -->
|
||||||
|
|
||||||
|
|
||||||
|
<!-- function add_listener_for_id(i){ -->
|
||||||
|
<!-- document.getElementById('id_form-' + i.toString() + '-job-remove') -->
|
||||||
|
<!-- .addEventListener("click", function(event){ -->
|
||||||
|
<!-- var job = event.target.parentNode; -->
|
||||||
|
<!-- job.parentNode.removeChild(job); -->
|
||||||
|
<!-- document.getElementById('id_form-TOTAL_FORMS').value --; -->
|
||||||
|
<!-- } -->
|
||||||
|
<!-- ) -->
|
||||||
|
<!-- } -->
|
||||||
|
|
||||||
|
|
||||||
|
<!-- // Add events manager when DOM is fully loaded -->
|
||||||
|
<!-- document.addEventListener( -->
|
||||||
|
<!-- "DOMContentLoaded", -->
|
||||||
|
<!-- function() { -->
|
||||||
|
<!-- document.getElementById("add_one") -->
|
||||||
|
<!-- .addEventListener("click", add_job, true); -->
|
||||||
|
<!-- document.getElementById('id_form-0-job-remove') -->
|
||||||
|
<!-- .addEventListener("click", function(event){ -->
|
||||||
|
<!-- var job = event.target.parentNode; -->
|
||||||
|
<!-- job.parentNode.removeChild(job); -->
|
||||||
|
<!-- document.getElementById('id_form-TOTAL_FORMS').value --; -->
|
||||||
|
<!-- } -->
|
||||||
|
<!-- ) -->
|
||||||
|
|
||||||
|
<!-- } -->
|
||||||
|
|
||||||
|
<!-- ); -->
|
||||||
|
|
||||||
|
<!-- </script> -->
|
||||||
|
{% endblock %}
|
||||||
|
|
28
printer/utils.py
Normal file
28
printer/utils.py
Normal file
|
@ -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
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
"""printer.validators
|
"""printer.validators
|
||||||
Custom validators useful for printer application.
|
Custom validators useful for printer application.
|
||||||
Author : Maxime Bombar <bombar@crans.org>.
|
Author : Maxime Bombar <bombar@crans.org>.
|
||||||
Date : 29/06/2018
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
"""printer.views
|
"""printer.views
|
||||||
The views for the printer app
|
The views for the printer app
|
||||||
Author : Maxime Bombar <bombar@crans.org>.
|
Author : Maxime Bombar <bombar@crans.org>.
|
||||||
Date : 29/06/2018
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
@ -10,6 +9,7 @@ from __future__ import unicode_literals
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
from django.forms import modelformset_factory, formset_factory
|
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 django.contrib.auth.decorators import login_required
|
||||||
|
|
||||||
from re2o.views import form
|
from re2o.views import form
|
||||||
|
@ -17,31 +17,105 @@ from users.models import User
|
||||||
|
|
||||||
from . import settings
|
from . import settings
|
||||||
|
|
||||||
|
from .utils import pdfinfo
|
||||||
|
|
||||||
|
from .models import (
|
||||||
|
JobWithOptions,
|
||||||
|
)
|
||||||
|
|
||||||
from .forms import (
|
from .forms import (
|
||||||
JobWithOptionsForm,
|
JobWithOptionsForm,
|
||||||
|
PrintForm,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def new_job(request):
|
def new_job(request):
|
||||||
"""
|
"""
|
||||||
View to create a new printing job
|
View to create a new printing job
|
||||||
"""
|
"""
|
||||||
|
if request.method == 'POST':
|
||||||
|
if request.FILES:
|
||||||
job_formset = formset_factory(JobWithOptionsForm)(
|
job_formset = formset_factory(JobWithOptionsForm)(
|
||||||
request.POST or None, request.FILES or None,
|
request.POST,
|
||||||
|
request.FILES,
|
||||||
)
|
)
|
||||||
|
|
||||||
if job_formset.is_valid():
|
if job_formset.is_valid():
|
||||||
|
files = request.FILES
|
||||||
|
data = []
|
||||||
|
i=0
|
||||||
for job in job_formset:
|
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 = job.save(commit=False)
|
||||||
|
job.filename = filename
|
||||||
job.user=request.user
|
job.user=request.user
|
||||||
job.status='Printable'
|
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.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(
|
return redirect(reverse(
|
||||||
'printer:success',
|
'printer:success',
|
||||||
))
|
))
|
||||||
|
raise Exception("Invalid Job_formset")
|
||||||
|
|
||||||
|
else:
|
||||||
|
job_formset = formset_factory(JobWithOptionsForm)(
|
||||||
|
None,
|
||||||
|
)
|
||||||
return form(
|
return form(
|
||||||
{
|
{
|
||||||
'jobform': job_formset,
|
'jobform': job_formset,
|
||||||
'action_name': "Print",
|
'action_name': "Advanced Options",
|
||||||
},
|
},
|
||||||
'printer/newjob.html',
|
'printer/newjob.html',
|
||||||
request
|
request
|
||||||
|
|
Loading…
Reference in a new issue