From 484f155e436aca4cd25c987451c3eecb24a61dee Mon Sep 17 00:00:00 2001 From: FreneticScribbler Date: Fri, 31 Dec 2021 12:17:46 +0000 Subject: [PATCH] FEAT(Asset): Add ability to generate whole page of labels --- RIGS/static/imgs/square_logo.png | Bin 13246 -> 0 bytes RIGS/templatetags/filters.py | 6 +- assets/converters.py | 16 ++++ assets/static/imgs/square_logo.png | Bin 0 -> 24475 bytes assets/templates/asset_list.html | 4 + assets/templates/labels_print.xml | 35 +++++++ assets/templates/partials/asset_buttons.html | 2 - assets/templatetags/asset_tags.py | 17 ++++ assets/urls.py | 2 + assets/views.py | 95 +++++++++++++++---- 10 files changed, 150 insertions(+), 27 deletions(-) delete mode 100644 RIGS/static/imgs/square_logo.png create mode 100644 assets/static/imgs/square_logo.png create mode 100644 assets/templates/labels_print.xml create mode 100644 assets/templatetags/asset_tags.py diff --git a/RIGS/static/imgs/square_logo.png b/RIGS/static/imgs/square_logo.png deleted file mode 100644 index 3c2143ba1c1e0b32ce33d668cb8ec0c9e3be4688..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13246 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
{% button 'new' 'asset_create' style="width: 6em" %} + {% if object_list %} + Generate Labels + {% endif %}
diff --git a/assets/templates/labels_print.xml b/assets/templates/labels_print.xml new file mode 100644 index 00000000..b8adfd79 --- /dev/null +++ b/assets/templates/labels_print.xml @@ -0,0 +1,35 @@ + + +{% load multiply from filters %} +{% load index from asset_tags %} + + + + + + + + + + + {% for i in images0 %} + + {% with images0|index:forloop.counter0 as image %}{% if image %}{% endif %}{% endwith %} + {% with images1|index:forloop.counter0 as image %}{% if image %}{% endif %}{% endwith %} + {% with images2|index:forloop.counter0 as image %}{% if image %}{% endif %}{% endwith %} + {% with images3|index:forloop.counter0 as image %}{% if image %}{% endif %}{% endwith %} + + {% endfor %} + + + diff --git a/assets/templates/partials/asset_buttons.html b/assets/templates/partials/asset_buttons.html index 2551365e..71f2a11b 100644 --- a/assets/templates/partials/asset_buttons.html +++ b/assets/templates/partials/asset_buttons.html @@ -12,9 +12,7 @@ {% button 'edit' url='asset_update' pk=object.asset_id %} {% button 'duplicate' url='asset_duplicate' pk=object.asset_id %} Audit - {% if object.is_cable %} Generate Label - {% endif %}
{% endif %} {% if create or edit or duplicate %} diff --git a/assets/templatetags/asset_tags.py b/assets/templatetags/asset_tags.py new file mode 100644 index 00000000..553f646f --- /dev/null +++ b/assets/templatetags/asset_tags.py @@ -0,0 +1,17 @@ +from django import template +from assets import models + +register = template.Library() + + +@register.filter +def ids_from_objects(object_list): + id_list = [] + for obj in object_list: + id_list.append(obj.asset_id) + return id_list + + +@register.filter +def index(indexable, i): + return indexable[i] if i < len(indexable) else None diff --git a/assets/urls.py b/assets/urls.py index 4ec111c1..5f45d2e8 100644 --- a/assets/urls.py +++ b/assets/urls.py @@ -7,6 +7,7 @@ from PyRIGS.views import OEmbedView from . import views, converters register_converter(converters.AssetIDConverter, 'asset') +register_converter(converters.ListConverter, 'list') urlpatterns = [ path('', login_required(views.AssetList.as_view()), name='asset_index'), @@ -19,6 +20,7 @@ urlpatterns = [ path('asset/id//duplicate/', permission_required_with_403('assets.add_asset') (views.AssetDuplicate.as_view()), name='asset_duplicate'), path('asset/id//label', login_required(views.GenerateLabel.as_view()), name='generate_label'), + path('asset//list/label', views.GenerateLabels.as_view(), name='generate_labels'), path('cabletype/list/', login_required(views.CableTypeList.as_view()), name='cable_type_list'), path('cabletype/create/', permission_required_with_403('assets.add_cable_type')(views.CableTypeCreate.as_view()), name='cable_type_create'), diff --git a/assets/views.py b/assets/views.py index a449112d..28894de8 100644 --- a/assets/views.py +++ b/assets/views.py @@ -1,5 +1,8 @@ import simplejson import random +import base64 +from io import BytesIO + from django.contrib import messages from django.contrib.auth.mixins import LoginRequiredMixin from django.core import serializers @@ -11,10 +14,13 @@ from django.utils.decorators import method_decorator from django.views import generic from django.views.decorators.csrf import csrf_exempt from django.shortcuts import get_object_or_404 +from django.template.loader import get_template +from PyPDF2 import PdfFileMerger, PdfFileReader from PIL import Image, ImageDraw, ImageFont from barcode import Code39 from barcode.writer import ImageWriter +from z3c.rml import rml2pdf from PyRIGS.views import GenericListView, GenericDetailView, GenericUpdateView, GenericCreateView, ModalURLMixin, \ is_ajax, OEmbedView @@ -349,35 +355,82 @@ class CableTypeUpdate(generic.UpdateView): return reverse("cable_type_detail", kwargs={"pk": self.object.pk}) -class GenerateLabel(generic.View): - def get(self, request, pk): - black = (0, 0, 0) - white = (255, 255, 255) - size = (700, 200) - font = ImageFont.truetype("static/fonts/OpenSans-Regular.tff", 20) - obj = get_object_or_404(models.Asset, asset_id=pk) +def generate_label(pk): + black = (0, 0, 0) + white = (255, 255, 255) + size = (700, 200) + font = ImageFont.truetype("static/fonts/OpenSans-Regular.tff", 20) + obj = get_object_or_404(models.Asset, asset_id=pk) - asset_id = "Asset: {}".format(obj.asset_id) + asset_id = "Asset: {}".format(obj.asset_id) + if obj.is_cable: length = "Length: {}m".format(obj.length) csa = "CSA: {}mm²".format(obj.csa) - image = Image.new("RGB", size, white) - logo = Image.open("static/imgs/square_logo.png") - draw = ImageDraw.Draw(image) + image = Image.new("RGB", size, white) + logo = Image.open("static/imgs/square_logo.png") + draw = ImageDraw.Draw(image) - draw.text((210, 140), asset_id, fill=black, font=font) + draw.text((210, 140), asset_id, fill=black, font=font) + if obj.is_cable: draw.text((210, 170), length, fill=black, font=font) - draw.text((350, 170), csa, fill=black, font=font) - draw.multiline_text((500, 140), "TEC PA & Lighting\n(0115) 84 68720", fill=black, font=font) + draw.text((360, 170), csa, fill=black, font=font) + draw.multiline_text((500, 140), "TEC PA & Lighting\n(0115) 84 68720", fill=black, font=font) - barcode = Code39(str(obj.asset_id), writer=ImageWriter()) + barcode = Code39(str(obj.asset_id), writer=ImageWriter()) - logo_size = (200, 200) - image.paste(logo.resize(logo_size, Image.ANTIALIAS)) - barcode_image = barcode.render(writer_options={"quiet_zone": 0, "write_text": False}) - width, height = barcode_image.size - image.paste(barcode_image.crop((0, 0, width, 135)), (int(((size[0] + logo_size[0]) - width) / 2), 0)) + logo_size = (200, 200) + image.paste(logo.resize(logo_size, Image.ANTIALIAS)) + barcode_image = barcode.render(writer_options={"quiet_zone": 0, "write_text": False}) + width, height = barcode_image.size + image.paste(barcode_image.crop((0, 0, width, 135)), (int(((size[0] + logo_size[0]) - width) / 2), 0)) + return image + + +class GenerateLabel(generic.View): # TODO Caching + def get(self, request, pk): response = HttpResponse(content_type="image/png") - image.save(response, "PNG") + generate_label(pk).save(response, "PNG") + return response + + +class GenerateLabels(generic.View): + def get(self, request, ids): + response = HttpResponse(content_type='application/pdf') + template = get_template('labels_print.xml') + + images = [] + + for asset_id in ids: + image = generate_label(asset_id) + in_mem_file = BytesIO() + image.save(in_mem_file, format="PNG") + # reset file pointer to start + in_mem_file.seek(0) + img_bytes = in_mem_file.read() + + base64_encoded_result_bytes = base64.b64encode(img_bytes) + base64_encoded_result_str = base64_encoded_result_bytes.decode('ascii') + images.append(base64_encoded_result_str) + + context = { + 'images0': images[::4], + 'images1': images[1::4], + 'images2': images[2::4], + 'images3': images[3::4], + 'filename': "Asset Label Sheet generated at {}".format(timezone.now()) + } + merger = PdfFileMerger() + + rml = template.render(context) + buffer = rml2pdf.parseString(rml) + merger.append(PdfFileReader(buffer)) + buffer.close() + + merged = BytesIO() + merger.write(merged) + + response['Content-Disposition'] = 'filename="{}"'.format(context['filename']) + response.write(merged.getvalue()) return response