FEAT(Asset): Add ability to generate whole page of labels

This commit is contained in:
2021-12-31 12:17:46 +00:00
parent fdbdaab52e
commit 484f155e43
10 changed files with 150 additions and 27 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -114,10 +114,8 @@ def orderby(request, field, attr):
return dict_.urlencode()
# Used for accessing outside of a form, i.e. in detail views of RiskAssessment and EventChecklist
@register.filter(needs_autoescape=True)
@register.filter(needs_autoescape=True) # Used for accessing outside of a form, i.e. in detail views of RiskAssessment and EventChecklist
def get_field(obj, field, autoescape=True):
value = getattr(obj, field)
if(isinstance(value, bool)):
@@ -221,7 +219,7 @@ def button(type, url=None, pk=None, clazz="", icon=None, text="", id=None, style
return {'target': url, 'pk': pk, 'class': clazz, 'icon': icon, 'text': text, 'id': id, 'style': style}
@register.simple_tag
@register.simple_tag # TODO Can these be done with annotation/aggregation?
def invoices_waiting():
return len(models.Event.objects.waiting_invoices())

View File

@@ -1,3 +1,6 @@
import urllib.parse
class AssetIDConverter: # Forces lowercase to uppercase
regex = '[^/]+'
@@ -6,3 +9,16 @@ class AssetIDConverter: # Forces lowercase to uppercase
def to_url(self, value):
return str(value).upper()
class ListConverter:
regex = '[^/]+'
def to_python(self, value):
return value.split(',')
def to_url(self, value):
string = ""
for i in value:
string += "," + str(i)
return string[1:]

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -1,6 +1,7 @@
{% extends 'base_assets.html' %}
{% load paginator from filters %}
{% load button from filters %}
{% load ids_from_objects from asset_tags %}
{% load widget_tweaks %}
{% load static %}
@@ -85,6 +86,9 @@
<div class="row my-2">
<div class="col text-right px-0">
{% button 'new' 'asset_create' style="width: 6em" %}
{% if object_list %}
<a class="btn btn-primary" href="{% url 'generate_labels' object_list|ids_from_objects %}"><span class="fas fa-barcode"></span> Generate Labels</a>
{% endif %}
</div>
</div>
<div class="row my-2">

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE document SYSTEM "rml.dtd">
{% load multiply from filters %}
{% load index from asset_tags %}
<document filename="{{filename}}">
<template>
<pageTemplate id="main">
<pageGraphics>
</pageGraphics>
<frame id="first" x1="5" y1="-10" width="581" height="842"/>
</pageTemplate>
</template>
<stylesheet>
<blockTableStyle id="table">
<!-- show a grid: this also comes in handy for debugging your tables.-->
<lineStyle kind="GRID" colorName="black" thickness="1" start="0,0" stop="-1,-1" />
</blockTableStyle>
</stylesheet>
<story>
<blockTable style="table">
{% for i in images0 %}
<tr>
<td>{% with images0|index:forloop.counter0 as image %}{% if image %}<illustration width="120" height="35"><image file="data:image/png;base64,{{image}}" x="0" y="0"
width="120" height="35"/></illustration>{% endif %}{% endwith %}</td>
<td>{% with images1|index:forloop.counter0 as image %}{% if image %}<illustration width="120" height="35"><image file="data:image/png;base64,{{image}}" x="0" y="0"
width="120" height="35"/></illustration>{% endif %}{% endwith %}</td>
<td>{% with images2|index:forloop.counter0 as image %}{% if image %}<illustration width="120" height="35"><image file="data:image/png;base64,{{image}}" x="0" y="0"
width="120" height="35"/></illustration>{% endif %}{% endwith %}</td>
<td>{% with images3|index:forloop.counter0 as image %}{% if image %}<illustration width="120" height="35"><image file="data:image/png;base64,{{image}}" x="0" y="0"
width="120" height="35"/></illustration>{% endif %}{% endwith %}</td>
</tr>
{% endfor %}
</blockTable>
</story>
</document>

View File

@@ -12,9 +12,7 @@
{% button 'edit' url='asset_update' pk=object.asset_id %}
{% button 'duplicate' url='asset_duplicate' pk=object.asset_id %}
<a type="button" class="btn btn-info" href="{% url 'asset_audit' object.asset_id %}"><span class="fas fa-certificate"></span> Audit</a>
{% if object.is_cable %}
<a type="button" class="btn btn-primary" href="{% url 'generate_label' object.asset_id %}"><span class="fas fa-barcode"></span> Generate Label</a>
{% endif %}
</div>
{% endif %}
{% if create or edit or duplicate %}

View File

@@ -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

View File

@@ -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/<asset:pk>/duplicate/', permission_required_with_403('assets.add_asset')
(views.AssetDuplicate.as_view()), name='asset_duplicate'),
path('asset/id/<asset:pk>/label', login_required(views.GenerateLabel.as_view()), name='generate_label'),
path('asset/<list:ids>/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'),

View File

@@ -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,8 +355,7 @@ class CableTypeUpdate(generic.UpdateView):
return reverse("cable_type_detail", kwargs={"pk": self.object.pk})
class GenerateLabel(generic.View):
def get(self, request, pk):
def generate_label(pk):
black = (0, 0, 0)
white = (255, 255, 255)
size = (700, 200)
@@ -358,6 +363,7 @@ class GenerateLabel(generic.View):
obj = get_object_or_404(models.Asset, asset_id=pk)
asset_id = "Asset: {}".format(obj.asset_id)
if obj.is_cable:
length = "Length: {}m".format(obj.length)
csa = "CSA: {}mm²".format(obj.csa)
@@ -366,8 +372,9 @@ class GenerateLabel(generic.View):
draw = ImageDraw.Draw(image)
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.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())
@@ -378,6 +385,52 @@ class GenerateLabel(generic.View):
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