diff --git a/printer/locale/fr/LC_MESSAGES/django.mo b/printer/locale/fr/LC_MESSAGES/django.mo index 907ebf2f..3028f4de 100644 Binary files a/printer/locale/fr/LC_MESSAGES/django.mo and b/printer/locale/fr/LC_MESSAGES/django.mo differ diff --git a/printer/locale/fr/LC_MESSAGES/django.po b/printer/locale/fr/LC_MESSAGES/django.po index bd5c2040..d608a85c 100644 --- a/printer/locale/fr/LC_MESSAGES/django.po +++ b/printer/locale/fr/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-10-26 01:01+0200\n" +"POT-Creation-Date: 2018-10-26 17:50+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,186 +18,215 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: printer/forms.py:34 +#: forms.py:33 msgid "Print As" msgstr "Imprimer en tant que" -#: printer/models.py:116 +#: models.py:87 +msgid "This is not your print operation task" +msgstr "Ceci n'est pas votre tache" + +#: models.py:124 msgid "Pending" msgstr "En attente" -#: printer/models.py:117 +#: models.py:125 msgid "Printable" msgstr "Imprimable" -#: printer/models.py:118 +#: models.py:126 msgid "Running" msgstr "En cours..." -#: printer/models.py:119 +#: models.py:127 msgid "Cancelled" msgstr "Annulée" -#: printer/models.py:120 +#: models.py:128 msgid "Finished" msgstr "Terminée" -#: printer/models.py:132 +#: models.py:140 msgid "File" msgstr "Fichier" -#: printer/models.py:137 +#: models.py:145 templates/printer/aff_jobs.html:31 msgid "File Name" msgstr "Nom du fichier" -#: printer/models.py:144 +#: models.py:152 templates/printer/aff_jobs.html:34 msgid "Status" msgstr "Status" -#: printer/models.py:153 +#: models.py:161 #, fuzzy #| msgid "Print As" msgid "Print as" msgstr "Imprimer en tant que" -#: printer/models.py:158 +#: models.py:166 templates/printer/aff_jobs.html:36 #, fuzzy #| msgid "price" msgid "Price" msgstr "Prix" -#: printer/models.py:166 +#: models.py:174 msgid "Format" msgstr "Format" -#: printer/models.py:172 printer/settings.py:25 +#: models.py:180 settings.py:25 msgid "Color" msgstr "Couleur" -#: printer/models.py:178 +#: models.py:186 msgid "Disposition" msgstr "Disposition" -#: printer/models.py:182 +#: models.py:190 msgid "Count" msgstr "Nombre" -#: printer/models.py:188 +#: models.py:196 msgid "Stapling" msgstr "Agrafage" -#: printer/models.py:194 +#: models.py:202 msgid "Perforation" msgstr "Perforation" -#: printer/settings.py:20 +#: models.py:243 +msgid "This is not your print job" +msgstr "Ceci n'est pas votre tache" + +#: settings.py:20 msgid "A4" msgstr "A4" -#: printer/settings.py:21 +#: settings.py:21 msgid "A3" msgstr "A3" -#: printer/settings.py:24 +#: settings.py:24 msgid "Greyscale" msgstr "Noir et blanc" -#: printer/settings.py:28 +#: settings.py:28 msgid "Two sided" msgstr "Recto-verso" -#: printer/settings.py:29 +#: settings.py:29 msgid "One sided" msgstr "Recto" -#: printer/settings.py:30 +#: settings.py:30 msgid "Booklet" msgstr "Livret" -#: printer/settings.py:33 printer/settings.py:40 +#: settings.py:33 settings.py:40 msgid "None" msgstr "Aucun" -#: printer/settings.py:34 +#: settings.py:34 msgid "One top left" msgstr "En haut à gauche" -#: printer/settings.py:35 +#: settings.py:35 msgid "One top right" msgstr "En haut à droite" -#: printer/settings.py:36 +#: settings.py:36 msgid "Two left sided" msgstr "Deux à gauche" -#: printer/settings.py:37 +#: settings.py:37 msgid "Two right sided" msgstr "Deux à droite" -#: printer/settings.py:41 +#: settings.py:41 msgid "Two left sided holes" msgstr "Deux trous à gauche" -#: printer/settings.py:42 +#: settings.py:42 msgid "Two right sided holes" msgstr "Deux trous à droite" -#: printer/settings.py:43 +#: settings.py:43 msgid "Two top holes" msgstr "Deux trous en haut" -#: printer/settings.py:44 +#: settings.py:44 msgid "Two bottom holes" msgstr "Deux trous en bas" -#: printer/settings.py:45 +#: settings.py:45 msgid "Four left sided holes" msgstr "Quatre trous à gauche" -#: printer/settings.py:46 +#: settings.py:46 msgid "Four right sided holes" msgstr "Quatre trous à droite" -#: printer/templates/printer/echec.html:11 -msgid "Failure" -msgstr "Echec" +#: templates/printer/aff_jobs.html:32 +msgid "By user" +msgstr "Par" -#: printer/templates/printer/newjob.html:13 +#: templates/printer/aff_jobs.html:33 +msgid "Start at" +msgstr "Début" + +#: templates/printer/aff_jobs.html:35 +msgid "Number" +msgstr "Exemplaires" + +#: templates/printer/aff_jobs.html:43 +msgid "for" +msgstr "pour" + +#: templates/printer/index_jobs.html:28 +#, fuzzy +#| msgid "Print" +msgid "Printer" +msgstr "Imprimer" + +#: templates/printer/index_jobs.html:31 templates/printer/sidebar.html:34 +msgid "List of jobs" +msgstr "Liste des taches" + +#: templates/printer/newjob.html:13 msgid "Printing Menu" msgstr "Menu d'impression" -#: printer/templates/printer/newjob.html:23 -#: printer/templates/printer/newjob.html:44 +#: templates/printer/newjob.html:23 templates/printer/newjob.html:43 msgid "Delete file" msgstr "Supprimer le fichier" -#: printer/templates/printer/newjob.html:34 +#: templates/printer/newjob.html:33 msgid "Add a file" msgstr "Fichier supplémentaire" -#: printer/templates/printer/print.html:13 +#: templates/printer/print.html:13 msgid "Confirm printing" msgstr "Confirmer le lancement de l'impression" -#: printer/templates/printer/print.html:20 +#: templates/printer/print.html:20 #, fuzzy #| msgid "File Name" msgid "Filename : " msgstr "Nom du fichier" -#: printer/templates/printer/print.html:21 +#: templates/printer/print.html:21 msgid "Price for one copy : " msgstr "Prix d'une copie : " -#: printer/templates/printer/print.html:22 +#: templates/printer/print.html:22 msgid "Number of pages : " msgstr "Nombre de pages : " -#: printer/templates/printer/success.html:11 -msgid "Success" -msgstr "Succès" +#: templates/printer/sidebar.html:30 +msgid "Print document" +msgstr "Imprimer un document" -#: printer/validators.py:48 +#: validators.py:48 #, python-format msgid "" "MIME type '%(type)s' is not valid. Please, use one of these types: " @@ -206,7 +235,7 @@ msgstr "" "Le type '%(type)s' n'est pas autorisé. Merci d'utiliser un type" "%(allowed_types)s." -#: printer/validators.py:51 +#: validators.py:51 #, python-format msgid "" "The current file size is %(size)s. The maximum file size is %(max_size)s." @@ -214,14 +243,20 @@ msgstr "" "Le fichier a une taille de %(size)s. La taille maximum autorisée est " "%(max_size)s." -#: printer/views.py:89 +#: views.py:84 msgid "Next" msgstr "Suivant" -#: printer/views.py:121 +#: views.py:115 msgid "Print" msgstr "Imprimer" -#: printer/views.py:150 +#: views.py:144 msgid "You are not allowed to print" msgstr "Vous n'êtes pas autorisé à imprimer" + +#~ msgid "Failure" +#~ msgstr "Echec" + +#~ msgid "Success" +#~ msgstr "Succès" diff --git a/printer/models.py b/printer/models.py index 1e36ac9b..88301e23 100644 --- a/printer/models.py +++ b/printer/models.py @@ -54,25 +54,25 @@ def user_printing_path(instance, filename): class Digicode(RevMixin, models.Model): - """ + """ This is a model to represent a digicode, maybe should be an external app. - """ - code = models.BigIntegerField(default=0, unique=True) - user = models.ForeignKey('users.User', on_delete=models.PROTECT) - created = models.DateTimeField(auto_now_add=True) - used_time = models.DateTimeField(null=True) + """ + code = models.BigIntegerField(default=0, unique=True) + user = models.ForeignKey('users.User', on_delete=models.PROTECT) + created = models.DateTimeField(auto_now_add=True) + used_time = models.DateTimeField(null=True) - def _gen_code(user): - try_again = True - while try_again: - try: - code = randint(695895, 6958942)*1437+38 - Digicode.objects.get(code=code) - except ObjectDoesNotExist: - try_again = False - digicode = Digicode.objects.create(code=code, user=user) - digicode.save() - return (str(code) + '#') + def _gen_code(user): + try_again = True + while try_again: + try: + code = randint(695895, 6958942)*1437+38 + Digicode.objects.get(code=code) + except ObjectDoesNotExist: + try_again = False + digicode = Digicode.objects.create(code=code, user=user) + digicode.save() + return (str(code) + '#') class PrintOperation(RevMixin, AclMixin, models.Model): """Abstract printing operation""" @@ -88,7 +88,7 @@ class PrintOperation(RevMixin, AclMixin, models.Model): class JobWithOptions(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): - """ + """ This is the main model of printer application : - ```user``` is a ForeignKey to the User Application @@ -120,130 +120,138 @@ class JobWithOptions(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model ```_compute_price``` compute the printing price ```_update_price``` update printing price """ - STATUS_AVAILABLE = ( - ('Pending', _('Pending')), - ('Printable', _('Printable')), - ('Running', _('Running')), - ('Cancelled', _('Cancelled')), - ('Finished', _('Finished')) - ) - user = models.ForeignKey('users.User', on_delete=models.PROTECT) - print_operation = models.ForeignKey('PrintOperation', on_delete=models.CASCADE) - paid = models.BooleanField(default='False') - file = models.FileField( - storage=FileSystemStorage(location='/var/impressions'), - upload_to=user_printing_path, - validators=[FileValidator( - allowed_types=ALLOWED_TYPES, - max_size=MAX_PRINTFILE_SIZE) - ], - verbose_name=_('File') - ) - filename = models.CharField( - max_length=255, - null=True, - verbose_name=_('File Name') - ) - starttime = models.DateTimeField(auto_now_add=True) - endtime = models.DateTimeField(null=True) - status = models.CharField( - max_length=255, - choices=STATUS_AVAILABLE, - verbose_name=_('Status'), - default='Pending' - ) - printAs = models.ForeignKey( - 'users.User', - on_delete=models.PROTECT, - related_name='print_as_user', - blank=True, - null=True, - verbose_name=_('Print as') - ) - price = models.DecimalField( - max_digits=5, - decimal_places=2, - verbose_name=_("Price"), - default=0.0 - ) - pages = models.IntegerField(default=0) - format = models.CharField( - max_length=255, - choices=FORMAT_AVAILABLE, - default='A4', - verbose_name=_("Format") - ) - color = models.CharField( - max_length=255, - choices=COLOR_CHOICES, - default='Greyscale', - verbose_name=_("Color") - ) - disposition = models.CharField( - max_length=255, - choices=DISPOSITIONS_AVAILABLE, - default='TwoSided', - verbose_name=_("Disposition") - ) - count = models.PositiveIntegerField( - default=1, - verbose_name=_("Count") - ) - stapling = models.CharField( - max_length=255, - choices=STAPLING_OPTIONS, - default='None', - verbose_name=_("Stapling") - ) - perforation = models.CharField( - max_length=255, - choices=PERFORATION_OPTIONS, - default='None', - verbose_name=_("Perforation") - ) + STATUS_AVAILABLE = ( + ('Pending', _('Pending')), + ('Printable', _('Printable')), + ('Running', _('Running')), + ('Cancelled', _('Cancelled')), + ('Finished', _('Finished')) + ) + user = models.ForeignKey('users.User', on_delete=models.PROTECT) + print_operation = models.ForeignKey('PrintOperation', on_delete=models.CASCADE) + paid = models.BooleanField(default='False') + file = models.FileField( + storage=FileSystemStorage(location='/var/impressions'), + upload_to=user_printing_path, + validators=[FileValidator( + allowed_types=ALLOWED_TYPES, + max_size=MAX_PRINTFILE_SIZE) + ], + verbose_name=_('File') + ) + filename = models.CharField( + max_length=255, + null=True, + verbose_name=_('File Name') + ) + starttime = models.DateTimeField(auto_now_add=True) + endtime = models.DateTimeField(null=True) + status = models.CharField( + max_length=255, + choices=STATUS_AVAILABLE, + verbose_name=_('Status'), + default='Pending' + ) + printAs = models.ForeignKey( + 'users.User', + on_delete=models.PROTECT, + related_name='print_as_user', + blank=True, + null=True, + verbose_name=_('Print as') + ) + price = models.DecimalField( + max_digits=5, + decimal_places=2, + verbose_name=_("Price"), + default=0.0 + ) + pages = models.IntegerField(default=0) + format = models.CharField( + max_length=255, + choices=FORMAT_AVAILABLE, + default='A4', + verbose_name=_("Format") + ) + color = models.CharField( + max_length=255, + choices=COLOR_CHOICES, + default='Greyscale', + verbose_name=_("Color") + ) + disposition = models.CharField( + max_length=255, + choices=DISPOSITIONS_AVAILABLE, + default='TwoSided', + verbose_name=_("Disposition") + ) + count = models.PositiveIntegerField( + default=1, + verbose_name=_("Count") + ) + stapling = models.CharField( + max_length=255, + choices=STAPLING_OPTIONS, + default='None', + verbose_name=_("Stapling") + ) + perforation = models.CharField( + max_length=255, + choices=PERFORATION_OPTIONS, + default='None', + verbose_name=_("Perforation") + ) - def _update_price(self): - self.price = self._compute_price() + def _update_price(self): + self.price = self._compute_price() - def _compute_price(self): - pages = int(self.pages) - price_paper = PRICES[self.format] - price_stapling = 0.0 - nb_staples = 0 + def _compute_price(self): + pages = int(self.pages) + price_paper = PRICES[self.format] + price_stapling = 0.0 + nb_staples = 0 - if self.disposition == 'Booklet': - sheets = int((pages+3)/4) - pages = 2 * sheets - elif self.disposition == 'TwoSided': - sheets = int(pages/2.+0.5) - else: - sheets = pages + if self.disposition == 'Booklet': + sheets = int((pages+3)/4) + pages = 2 * sheets + elif self.disposition == 'TwoSided': + sheets = int(pages/2.+0.5) + else: + sheets = pages - if self.format == 'A3': - pages*=2 + if self.format == 'A3': + pages*=2 - price_ink = price_paper*sheets + PRICES[self.color]*pages + price_ink = price_paper*sheets + PRICES[self.color]*pages - if self.stapling: - nb_staples = 2 - int('Top' in self.stapling) + if self.stapling: + nb_staples = 2 - int('Top' in self.stapling) - price_stapling = nb_staples * PRICES['Staples'] + price_stapling = nb_staples * PRICES['Staples'] - total_price = math.floor(self.count * (price_ink + price_stapling)) + total_price = math.floor(self.count * (price_ink + price_stapling)) - return total_price/100 + return total_price/100 - def __init__(self, *args, **kwargs): - super(JobWithOptions, self).__init__(*args, **kwargs) - self.field_permissions = { - 'printAs': self.can_change_printas, - } + def can_view(self, user_request, *args, **kwargs): + if user_request.has_perm('printer.view_jobwithoptions'): + return True, None + elif user_request == self.user or user_request == self.printAs: + return True, None + else: + return False, _("This is not your print job") - def can_change_printas(self, user_request, *_args, **_kwargs): - return user_request.adherent.club_members.all(), None + def __init__(self, *args, **kwargs): + super(JobWithOptions, self).__init__(*args, **kwargs) + self.field_permissions = { + 'printAs': self.can_change_printas, + } - def save(self, *args, **kwargs): - self._update_price() - super(JobWithOptions, self).save(*args, **kwargs) + def can_change_printas(self, user_request, *_args, **_kwargs): + return user_request.adherent.club_members.all(), None + + def save(self, *args, **kwargs): + self._update_price() + super(JobWithOptions, self).save(*args, **kwargs) diff --git a/printer/templates/printer/aff_jobs.html b/printer/templates/printer/aff_jobs.html new file mode 100644 index 00000000..e33662e1 --- /dev/null +++ b/printer/templates/printer/aff_jobs.html @@ -0,0 +1,52 @@ +{% comment %} +Re2o est un logiciel d'administration développé initiallement au rezometz. Il +se veut agnostique au réseau considéré, de manière à être installable en +quelques clics. + +Copyright © 2018 Gabriel Détraz + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +{% endcomment %} + +{% load acl %} +{% load logs_extra %} +{% load i18n %} + +
+ + + + + + + + + + + + {% for job in jobs_list %} + {% can_view job %} + + + + + + + + + {% acl_end %} + {% endfor %} +
{% trans "File Name" %}{% trans "By user" %}{% trans "Start at" %}{% trans "Status" %}{% trans "Number" %}{% trans "Price" %}
{{ job.filename }}{{ job.user }} {% if job.user != job.printAs %} {% trans "for" %} {{ job.printAs }}{% endif %}{{ job.starttime }}{{ job.status }}{{ job.count }}{{ job.price }}
+
diff --git a/printer/templates/printer/index_jobs.html b/printer/templates/printer/index_jobs.html new file mode 100644 index 00000000..7896cf07 --- /dev/null +++ b/printer/templates/printer/index_jobs.html @@ -0,0 +1,33 @@ +{% extends "printer/sidebar.html" %} +{% comment %} +Re2o est un logiciel d'administration développé initiallement au rezometz. Il +se veut agnostique au réseau considéré, de manière à être installable en +quelques clics. + +Copyright © 2018 Gabriel Détraz + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +{% endcomment %} + +{% load bootstrap3 %} +{% load acl %} +{% load i18n %} + +{% block title %}{% trans "Printer" %}{% endblock %} + +{% block content %} +

{% trans "List of jobs" %}

+ {% include "printer/aff_jobs.html" with jobs_list=jobs_list %} +{% endblock %} diff --git a/printer/templates/printer/newjob.html b/printer/templates/printer/newjob.html index 73bdf3ac..24ae2b48 100644 --- a/printer/templates/printer/newjob.html +++ b/printer/templates/printer/newjob.html @@ -1,10 +1,10 @@ -{% extends "base.html" %} +{% extends "printer/sidebar.html" %} {% load staticfiles %} -{% load i18n %} +{% load i18n %} {% load bootstrap3 %} {% load massive_bootstrap_form %} -{% load static %} + {% block title %}Printing interface{% endblock %} {% block content %} diff --git a/printer/templates/printer/print.html b/printer/templates/printer/print.html index ad0b5016..5de50680 100644 --- a/printer/templates/printer/print.html +++ b/printer/templates/printer/print.html @@ -1,4 +1,4 @@ -{% extends "base.html" %} +{% extends "printer/sidebar.html" %} {% load staticfiles %} {% load i18n %} diff --git a/printer/templates/printer/sidebar.html b/printer/templates/printer/sidebar.html new file mode 100644 index 00000000..16a62ac8 --- /dev/null +++ b/printer/templates/printer/sidebar.html @@ -0,0 +1,36 @@ +{% extends "base.html" %} +{% comment %} +Re2o est un logiciel d'administration développé initiallement au rezometz. Il +se veut agnostique au réseau considéré, de manière à être installable en +quelques clics. + +Copyright © 2017 Gabriel Détraz + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +{% endcomment %} + +{% load acl %} +{% load i18n %} + +{% block sidebar %} + + + {% trans "Print document" %} + + + + {% trans "List of jobs" %} + +{% endblock %} diff --git a/printer/urls.py b/printer/urls.py index 92dbb4ce..529062cd 100644 --- a/printer/urls.py +++ b/printer/urls.py @@ -13,5 +13,6 @@ from . import views urlpatterns = [ url(r'^new_job/$', views.new_job, name="new-job"), - url(r'^print_job/(?P[0-9]+)$', views.print_job, name='print-job') + url(r'^print_job/(?P[0-9]+)$', views.print_job, name='print-job'), + url(r'^index_jobs/$', views.index_jobs, name="index-jobs") ] diff --git a/printer/views.py b/printer/views.py index 8efce2f8..6928acd2 100644 --- a/printer/views.py +++ b/printer/views.py @@ -15,7 +15,7 @@ from django.utils.translation import ugettext as _ from re2o.views import form from users.models import User - +from re2o.utils import re2o_paginator from . import settings from .utils import pdfinfo, send_mail_printer @@ -24,20 +24,17 @@ from .models import ( JobWithOptions, PrintOperation ) - from .forms import ( JobWithOptionsForm, ) - +from preferences.models import GeneralOption from cotisations.models import( Paiement, Facture, Vente, ) - from cotisations.utils import find_payment_method - from cotisations.payment_methods.balance.models import BalancePayment from django.core.exceptions import ValidationError @@ -178,3 +175,11 @@ def payment(request, jobs): 'users:profil', kwargs={'userid': str(request.user.id)} )) + +@login_required +def index_jobs(request): + """ Display jobs""" + pagination_number = GeneralOption.get_cached_value('pagination_number') + jobs = JobWithOptions.objects.select_related('user').select_related('print_operation') + jobs_list = re2o_paginator(request, jobs, pagination_number) + return render(request, 'printer/index_jobs.html', {'jobs_list': jobs_list})