First shot at a working backend

This commit is contained in:
Paul Mathieu 2023-07-03 16:36:23 +02:00
parent 471fad53e1
commit 0bbe9d9fd9
9 changed files with 288 additions and 4 deletions

View File

@ -1,3 +1,9 @@
from django.contrib import admin from django.contrib import admin
from .models import Tikette, Tikategory, Tisub
admin.site.register(Tikette)
admin.site.register(Tikategory)
admin.site.register(Tisub)
# Register your models here. # Register your models here.

View File

@ -0,0 +1,61 @@
import os
import subprocess
import tempfile
from .makesticker import makesticker
from .makeplanche import makeplanche
OUT_DIR = tempfile.gettempdir()
DEFAULT_DPI = 300
def inkscapize(svg_in, pdf_out):
png_out = tempfile.mktemp(suffix='.png')
try:
cmd = ['inkscape', '--export-type=png', f'--export-filename={png_out}',
f'--export-dpi={DEFAULT_DPI}', svg_in]
subprocess.check_call(cmd)
cmd = ['convert', png_out, pdf_out]
subprocess.check_call(cmd)
finally:
if os.path.exists(png_out):
os.unlink(png_out)
def generate(request, out_dir):
""" Generate a sticker sheet.
request: dict-like with the following fields:
sticker: mandatory. filename for the sticker
subs: mandatory. dict-like with key-value template subtitution fields
landscape: optional. defaults to False
out_dir: you get it
"""
# fill in sticker details
sticker_out = tempfile.mktemp(suffix='.svg')
try:
with open(sticker_out, 'w') as stickout:
landscape = request.get('landscape', False)
makesticker(os.path.join(out_dir, request['sticker']), stickout,
request['subs'], landscape=landscape)
# make sticker sheet
planche_out = tempfile.mktemp(suffix='.svg')
with open(sticker_out, 'r') as stickin:
with open(planche_out, 'w') as planchout:
makeplanche(stickin, planchout)
# process to printable pdf
pdf_out = tempfile.mktemp(dir=out_dir, suffix='.pdf')
inkscapize(planche_out, pdf_out)
return os.path.basename(pdf_out)
finally:
if os.path.exists(sticker_out):
os.unlink(sticker_out)
if 'planche_out' in locals() and os.path.exists(planche_out):
os.unlink(planche_out)

View File

@ -0,0 +1,60 @@
#!/usr/bin/env python3
import argparse
from pathlib import Path
from string import Template
import sys
DEFAULT_SHEET_TEMPLATE = Path(__file__).parent / 'planche.svg.in'
def parse_args():
parser = argparse.ArgumentParser(
description='Make a sheet with 12 stickers')
parser.add_argument('--template',
'-t',
default=DEFAULT_SHEET_TEMPLATE,
help='path to the sheet template')
parser.add_argument('--out',
'-o',
default=sys.stdout,
type=argparse.FileType('w'),
help='output path (default: stdout)')
parser.add_argument('sticker',
type=argparse.FileType('r'),
default=sys.stdin,
nargs='?',
help='path to the sticker SVG (default: stdin)')
return parser.parse_args()
def makeplanche(sticker, out, template=DEFAULT_SHEET_TEMPLATE):
with open(template) as tpl:
tpl_data = tpl.read()
lines = sticker.readlines()
if lines[0].startswith('<?xml'):
lines = lines[1:]
sticker_data = ''.join(lines)
subs = {
'left0': sticker_data,
'left1': sticker_data,
'left2': sticker_data,
'left3': sticker_data,
'left4': sticker_data,
'left5': sticker_data,
'right0': sticker_data,
'right1': sticker_data,
'right2': sticker_data,
'right3': sticker_data,
'right4': sticker_data,
'right5': sticker_data,
}
out.write(Template(tpl_data).substitute(subs))
if __name__ == "__main__":
makeplanche(**vars(parse_args()))

View File

@ -0,0 +1,58 @@
#!/usr/bin/env python3
import argparse
from string import Template
import sys
import typing
def parse_args():
parser = argparse.ArgumentParser(description='Fill in sticker details')
parser.add_argument('--out',
'-o',
default=sys.stdout,
type=argparse.FileType('w'),
help='output path (default: stdout)')
parser.add_argument('--dluo',
'-d',
required=True,
help='Date Limite d\'Utilisation Optimale')
parser.add_argument('--lot', '-l', required=True, help='Numéro de lot')
parser.add_argument('--quantite', '-q', required=False, help='Quantité (volume ou masse)')
parser.add_argument('--teneur', '-t', required=False, help='Teneur en sucre')
parser.add_argument('--fruit', '-f', required=False, help='Quantité de fruits')
parser.add_argument('--size', '-s', required=False, help='Masse de produit')
parser.add_argument('--landscape',
action='store_true',
help='input sticker is in landscape orientation')
parser.add_argument('sticker', help='path to the sticker template')
return parser.parse_args()
def makesticker(sticker: str, out: typing.IO, subs: dict, landscape=False):
with open(sticker) as fin:
lines = fin.readlines()
if lines[0].startswith('<?xml'):
lines = lines[1:]
sticker_data = ''.join(lines)
if landscape:
# Rotate the sticker 90 degrees
sticker_data = "<g transform=\"rotate(-90,0,0) translate(-102,0)\">{}</g>".format(sticker_data)
out.write(Template(sticker_data).substitute(subs))
if __name__ == "__main__":
args = vars(parse_args())
subs = {
'dluo': args.pop('dluo'),
'lot': args.pop('lot'),
'teneur': args.pop('teneur'),
'fruit': args.pop('fruit'),
'qty': args.pop('quantite'),
'size': args.pop('size'),
}
args['subs'] = subs
makesticker(**args)

View File

@ -1,3 +1,38 @@
from django.db import models from django.db import models
class Tikategory(models.Model):
name = models.CharField(max_length=50)
landscape = models.BooleanField()
def __str__(self):
return self.name
class Meta:
verbose_name_plural = "tikategoriez"
class Tisub(models.Model):
name = models.CharField(max_length=50)
descritpion = models.TextField()
default = models.TextField()
def __str__(self):
return self.name
class Meta:
verbose_name_plural = "tisubz"
class Tikette(models.Model):
title = models.CharField(max_length=100)
category = models.ForeignKey(Tikategory, on_delete=models.CASCADE)
svg = models.FileField()
subs = models.ManyToManyField(Tisub)
def __str__(self):
return self.title
class Meta:
verbose_name_plural = "tikettz"
# Create your models here. # Create your models here.

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 210 297"
height="297mm"
width="210mm">
<g transform="translate(3, 4.5)">
<g transform="translate(-42,144) rotate(-90,144,0)">
<g transform="translate( 0,0)">$right0</g>
<g transform="translate( 48,0)">$right1</g>
<g transform="translate( 96,0)">$right2</g>
<g transform="translate(144,0)">$right3</g>
<g transform="translate(192,0)">$right4</g>
<g transform="translate(240,0)">$right5</g>
</g>
<g transform="translate(-42,144) rotate(90,144,0)">
<g transform="translate( 0,0)">$left0</g>
<g transform="translate( 48,0)">$left1</g>
<g transform="translate( 96,0)">$left2</g>
<g transform="translate(144,0)">$left3</g>
<g transform="translate(192,0)">$left4</g>
<g transform="translate(240,0)">$left5</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 896 B

View File

@ -1,3 +1,31 @@
from django.shortcuts import render import json
# Create your views here. from django.conf import settings
from django.http import JsonResponse
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
from . import generate as stickersheet
from .models import Tikette, Tikategory
CORS={'access-control-allow-origin': '*'}
def index(request):
tikettes = [{
'title': x.title,
'category': x.category.name,
'sticker': x.svg.name,
'landscape': x.category.landscape,
'subs': {x.name: x.default for x in x.subs.all()},
} for x in Tikette.objects.all()]
return JsonResponse({'status': 'ok', 'tikettes': tikettes}, headers=CORS)
@csrf_exempt
def generate(request):
if request.method != "POST":
return JsonResponse({'status': 'notok', 'message': 'this isn\'t the way'})
payload = json.loads(request.body)
pdfpath = stickersheet.generate(payload, out_dir=settings.TIKETTE_OUT_DIR)
return JsonResponse({'status': 'ok', 'file': pdfpath}, headers=CORS)

View File

@ -15,12 +15,16 @@ from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'. # Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent BASE_DIR = Path(__file__).resolve().parent.parent
#TIKETTE_OUT_DIR = BASE_DIR / 'data'
TIKETTE_OUT_DIR = Path('/data')
MEDIA_ROOT = TIKETTE_OUT_DIR
MEDIA_URL = 'data/'
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-a$!y$504(^om%yac)!!vb68arv*e*+r^t-4%y=d%8fda5x38=o' SECRET_KEY = 'django-insecure-64qxpe55#9wy=5@#dl0)3w7ywxh48m!f&!slp9e7v4lh@hjdct'
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True DEBUG = True
@ -37,6 +41,7 @@ INSTALLED_APPS = [
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'tikette',
] ]
MIDDLEWARE = [ MIDDLEWARE = [

View File

@ -14,9 +14,16 @@ Including another URLconf
1. Import the include() function: from django.urls import include, path 1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
""" """
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin from django.contrib import admin
from django.urls import path from django.urls import path
from django.views.generic.base import TemplateView
import tikette.views
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
] path('list', tikette.views.index),
path('', tikette.views.generate),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)