First shot at a working backend
This commit is contained in:
parent
471fad53e1
commit
0bbe9d9fd9
@ -1,3 +1,9 @@
|
||||
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.
|
||||
|
61
backend/zetikettes/tikette/generate.py
Normal file
61
backend/zetikettes/tikette/generate.py
Normal 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)
|
60
backend/zetikettes/tikette/makeplanche.py
Executable file
60
backend/zetikettes/tikette/makeplanche.py
Executable 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()))
|
58
backend/zetikettes/tikette/makesticker.py
Executable file
58
backend/zetikettes/tikette/makesticker.py
Executable 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)
|
@ -1,3 +1,38 @@
|
||||
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.
|
||||
|
24
backend/zetikettes/tikette/planche.svg.in
Normal file
24
backend/zetikettes/tikette/planche.svg.in
Normal 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 |
@ -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)
|
||||
|
@ -15,12 +15,16 @@ from pathlib import Path
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
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
|
||||
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
|
||||
|
||||
# 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!
|
||||
DEBUG = True
|
||||
@ -37,6 +41,7 @@ INSTALLED_APPS = [
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'tikette',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
@ -14,9 +14,16 @@ Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
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.urls import path
|
||||
from django.views.generic.base import TemplateView
|
||||
|
||||
import tikette.views
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
]
|
||||
path('list', tikette.views.index),
|
||||
path('', tikette.views.generate),
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
Loading…
Reference in New Issue
Block a user