Now handles uploading new tempaltes!!1
Just point your browser to /zetikettes/newtikette.html Password is in web.py
This commit is contained in:
parent
c4e03c9550
commit
f3c2a2f725
@ -27,10 +27,7 @@ docker run -d --rm -p 8000:8000 -v /var/lib/zetikettes/templates:/data zetikette
|
|||||||
Change available templates
|
Change available templates
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
1. make or update templates
|
1. go to /zetikettes/newtikette.html
|
||||||
1. add them to tempaltes/
|
1. add the newtikette
|
||||||
1. update static/zetikettes.js
|
1. still no need to restart the container (magic!)
|
||||||
1. git commit && git push
|
|
||||||
1. update /var/lib/zetikettes
|
|
||||||
1. no need to restart the container (magic!)
|
|
||||||
2. profit.
|
2. profit.
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
<title>zetikettes 0.1</title>
|
<title>zetikettes 0.2</title>
|
||||||
|
|
||||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
<!-- Compiled and minified CSS -->
|
<!-- Compiled and minified CSS -->
|
||||||
|
45
static/newtikette.html
Normal file
45
static/newtikette.html
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
<title>zetikettes 0.2</title>
|
||||||
|
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
|
<!-- Compiled and minified CSS -->
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
|
||||||
|
|
||||||
|
<!-- Compiled and minified JavaScript -->
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
|
||||||
|
|
||||||
|
<script src="config.js"></script>
|
||||||
|
<script src="newtikette.js"></script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
min-height: 100vh;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
flex: 1 0 auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header class="container center orange-text">
|
||||||
|
<h1>Zétikwett's</h1>
|
||||||
|
</header>
|
||||||
|
<main class="container row">
|
||||||
|
<div class="col m6 offset-m3 s12">
|
||||||
|
<div id="appbody" class="container"></div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<footer class="page-footer">
|
||||||
|
<div class="container row">
|
||||||
|
<span class="col right">displayed with recycled electrons.</span>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
130
static/newtikette.js
Normal file
130
static/newtikette.js
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
const backend_api = {
|
||||||
|
'prod': '/zetikettes/srv/',
|
||||||
|
'dev': '/zetikettes.dev/srv/',
|
||||||
|
}[config];
|
||||||
|
|
||||||
|
const params = [
|
||||||
|
['dluo', 'DLUO', true],
|
||||||
|
['lot', 'Nº de lot', true],
|
||||||
|
['qty', 'Poids net (g)', true],
|
||||||
|
['teneur', 'Teneur en fruits (%)', false],
|
||||||
|
['fruit', 'Quantité de fruits pour 100g (g)', false],
|
||||||
|
]
|
||||||
|
|
||||||
|
function post() {
|
||||||
|
const lemot = $('input[type=password]').val();
|
||||||
|
const formdata = new FormData($('form')[0]);
|
||||||
|
formdata.append('lemotdepasse', lemot);
|
||||||
|
$.ajax({
|
||||||
|
url: backend_api + `newtikette`,
|
||||||
|
type: 'POST',
|
||||||
|
data: formdata,
|
||||||
|
|
||||||
|
contentType: false,
|
||||||
|
processData: false,
|
||||||
|
|
||||||
|
xhr: function () {
|
||||||
|
var myXhr = $.ajaxSettings.xhr();
|
||||||
|
if (myXhr.upload) {
|
||||||
|
// For handling the progress of the upload
|
||||||
|
myXhr.upload.addEventListener('progress', function (e) {
|
||||||
|
if (e.lengthComputable) {
|
||||||
|
$('progress').attr({
|
||||||
|
value: e.loaded,
|
||||||
|
max: e.total,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
return myXhr;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
console.log(data);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log(err);
|
||||||
|
if (err.status == 403) {
|
||||||
|
M.toast({html: 'access denied'});
|
||||||
|
} else if (err.status == 500) {
|
||||||
|
M.toast({html: 'server error'});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.always(() => {
|
||||||
|
$('.progress').hide();
|
||||||
|
$('.btn').removeClass('disabled');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(async () => {
|
||||||
|
const appbody = $("#appbody");
|
||||||
|
const block = $('<form class="section">');
|
||||||
|
block.append($(`<div class="input-field"><label class="active">Title</label>
|
||||||
|
<input type="text" name="title" value="Gelée de brandade aux cêpes biou">`));
|
||||||
|
block.append($(`<div class="input-field"><label class="active">
|
||||||
|
<input type="checkbox" name="landscape"><span>landscape</span></label>`));
|
||||||
|
const subst = $('<div class="section"><label class="active">Substitutions</label><p></p>');
|
||||||
|
for (let param of params) {
|
||||||
|
subst.append($(`<div><label class="active"><input type="checkbox" name="${param}" ${param[2] ? 'checked': ''}>
|
||||||
|
<span>\${${param[0]}}: ${param[1]}</span></label>`));
|
||||||
|
}
|
||||||
|
block.append(subst);
|
||||||
|
|
||||||
|
block.append(`<div class="file-field input-field">
|
||||||
|
<div class="btn">
|
||||||
|
<span>File</span>
|
||||||
|
<input type="file" name="sticker" accept=".svg">
|
||||||
|
</div>
|
||||||
|
<div class="file-path-wrapper">
|
||||||
|
<input class="file-path validate" type="text" placeholder="Template .svg file">
|
||||||
|
</div>
|
||||||
|
`)
|
||||||
|
|
||||||
|
const loader = $('<div class="progress"><div class="determinate"></div></div>')
|
||||||
|
.hide();
|
||||||
|
|
||||||
|
let setup = false;
|
||||||
|
|
||||||
|
const action = $('<div class="section">')
|
||||||
|
.append($('<a class="btn">add newtikette<a>')
|
||||||
|
.click(() => {
|
||||||
|
if (!setup) {
|
||||||
|
setup = true;
|
||||||
|
$('input[type=password]').on('keydown', e => {
|
||||||
|
if (e.keyCode == 13 && !e.repeat) {
|
||||||
|
$('.modal').modal('close');
|
||||||
|
post();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$("#pushgo").click(() => { post(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.modal').modal('open');
|
||||||
|
$('input[type=password]').focus();
|
||||||
|
|
||||||
|
loader.show();
|
||||||
|
$('.btn').addClass("disabled");
|
||||||
|
$('.modal-close').removeClass("disabled");
|
||||||
|
|
||||||
|
})
|
||||||
|
.append(loader));
|
||||||
|
|
||||||
|
const lemotdepasse = $(`
|
||||||
|
<div id="modal1" class="modal">
|
||||||
|
<div class="modal-content">
|
||||||
|
<h4>A lemotdepasse is needed</h4>
|
||||||
|
<div class="input-field"><label class="active">lemotdepasse</label>
|
||||||
|
<input type="password">
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<a class="btn modal-close" id="pushgo">go</a>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
appbody
|
||||||
|
.append(lemotdepasse)
|
||||||
|
.append(block)
|
||||||
|
.append(action);
|
||||||
|
|
||||||
|
$(".modal").modal();
|
||||||
|
});
|
@ -3,275 +3,6 @@ const backend_api = {
|
|||||||
'dev': '/zetikettes.dev/srv/',
|
'dev': '/zetikettes.dev/srv/',
|
||||||
}[config];
|
}[config];
|
||||||
|
|
||||||
const zetikettes = [
|
|
||||||
{
|
|
||||||
'title': 'Aromate herbes de Provence',
|
|
||||||
'sticker': 'Aromate - Herbes de Provence.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '40',
|
|
||||||
},
|
|
||||||
'landscape': false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Aromate herbes de Provence avec romarin',
|
|
||||||
'sticker': 'Aromate - Herbes de Provence avec romarin.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '40',
|
|
||||||
},
|
|
||||||
'landscape': false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Aromate thym',
|
|
||||||
'sticker': 'Aromate - Thym.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '40',
|
|
||||||
},
|
|
||||||
'landscape': false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Chocolat lavande',
|
|
||||||
'sticker': 'Chocolat - Lavande.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '100',
|
|
||||||
},
|
|
||||||
'landscape': false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Confiture de Mirabelle - non bio',
|
|
||||||
'sticker': 'Confiture - Mirabelle.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'fruit': '80',
|
|
||||||
'teneur': '50',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '370',
|
|
||||||
},
|
|
||||||
'landscape': true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Gelée Extra de groseille',
|
|
||||||
'sticker': 'Gelée Extra - Groseille.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'fruit': '80',
|
|
||||||
'teneur': '50',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '370',
|
|
||||||
},
|
|
||||||
'landscape': true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Gelée Extra de groseille au basilic',
|
|
||||||
'sticker': 'Gelée Extra - Groseille Basilic.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'fruit': '80',
|
|
||||||
'teneur': '50',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '370',
|
|
||||||
},
|
|
||||||
'landscape': true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Gelée Extra de cassis',
|
|
||||||
'sticker': 'Gelée Extra - Cassis.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'fruit': '80',
|
|
||||||
'teneur': '50',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '370',
|
|
||||||
},
|
|
||||||
'landscape': true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Pesto ail des ours',
|
|
||||||
'sticker': 'Pesto - Ail des Ours - 100% Olive.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '150',
|
|
||||||
},
|
|
||||||
'landscape': true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Sel grillade et légumes',
|
|
||||||
'sticker': 'Sel - Grillade.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '40',
|
|
||||||
},
|
|
||||||
'landscape': true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Sel herbes de Provence',
|
|
||||||
'sticker': 'Sel - Herbes de Provence.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '40',
|
|
||||||
},
|
|
||||||
'landscape': true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Sel viande blanche et poisson',
|
|
||||||
'sticker': 'Sel - Poisson et Viande Blanche.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '40',
|
|
||||||
},
|
|
||||||
'landscape': true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Sel salade',
|
|
||||||
'sticker': 'Sel - Salade.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '40',
|
|
||||||
},
|
|
||||||
'landscape': true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Sel salade sans basilic',
|
|
||||||
'sticker': 'Sel - Salade - Sans Basilic.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '40',
|
|
||||||
},
|
|
||||||
'landscape': true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Sirop de sureau',
|
|
||||||
'sticker': 'Sirop - Sureau.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '75',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Sirop de lavande',
|
|
||||||
'sticker': 'Sirop - Lavande.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '75',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Sirop de cassis',
|
|
||||||
'sticker': 'Sirop - Cassis.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '75',
|
|
||||||
},
|
|
||||||
'landscape': false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Sirop de thym',
|
|
||||||
'sticker': 'Sirop - Thym.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '75',
|
|
||||||
},
|
|
||||||
'landscape': false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Sirop de mélisse',
|
|
||||||
'sticker': 'Sirop - Melisse.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '75',
|
|
||||||
},
|
|
||||||
'landscape': false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Sirop de menthe verte',
|
|
||||||
'sticker': 'Sirop - Menthe Verte.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '75',
|
|
||||||
},
|
|
||||||
'landscape': false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Tisane Hiver',
|
|
||||||
'sticker': 'Tisane - Hiver.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '25',
|
|
||||||
},
|
|
||||||
'landscape': false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Tisane Joie de Vivre',
|
|
||||||
'sticker': 'Tisane - Joie de Vivre.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '25',
|
|
||||||
},
|
|
||||||
'landscape': false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Tisane Réconfort de la Gorge (Origan, Thym, Agastache, Mauve)',
|
|
||||||
'sticker': 'Tisane - Reconfort de la Gorge - Origan Thym Agastache Mauve.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '25',
|
|
||||||
},
|
|
||||||
'landscape': false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Tisane Nuit Étoilée (Mélisse, Lavande, Aubépine)',
|
|
||||||
'sticker': 'Tisane - Nuit Etoilee - Melisse Lavande Aubepine.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '25',
|
|
||||||
},
|
|
||||||
'landscape': false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Tisane Détox (Menthe, Sarriette, Basilic, Souci)',
|
|
||||||
'sticker': 'Tisane - Detox - Menthe Sarriette Basilic Souci.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '25',
|
|
||||||
},
|
|
||||||
'landscape': false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'title': 'Tisane digestion',
|
|
||||||
'sticker': 'Tisane - Digestion.svg',
|
|
||||||
'subs': {
|
|
||||||
'dluo': 'germinal 9999',
|
|
||||||
'lot': '0000-0',
|
|
||||||
'qty': '25',
|
|
||||||
},
|
|
||||||
'landscape': false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
'dluo': 'DLUO',
|
'dluo': 'DLUO',
|
||||||
'lot': 'Nº de lot',
|
'lot': 'Nº de lot',
|
||||||
@ -281,7 +12,7 @@ const params = {
|
|||||||
'fruit': 'Quantité de fruits pour 100g (g)',
|
'fruit': 'Quantité de fruits pour 100g (g)',
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(() => {
|
function loadAll(zetikettes) {
|
||||||
const appbody = $("#appbody");
|
const appbody = $("#appbody");
|
||||||
for (let zett of zetikettes) {
|
for (let zett of zetikettes) {
|
||||||
const block = $('<div class="section">');
|
const block = $('<div class="section">');
|
||||||
@ -331,4 +62,18 @@ $(document).ready(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$('.collapsible').collapsible();
|
$('.collapsible').collapsible();
|
||||||
});
|
}
|
||||||
|
|
||||||
|
$(document).ready(async () => {
|
||||||
|
try {
|
||||||
|
const resp = await $.ajax({
|
||||||
|
url: backend_api + 'list',
|
||||||
|
timeout: 1000,
|
||||||
|
});
|
||||||
|
loadAll(resp.tikettes);
|
||||||
|
} catch(e) {
|
||||||
|
const appbody = $("#appbody");
|
||||||
|
appbody.append(`<li>Could not reach backend server`);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
1
tikettes.json
Normal file
1
tikettes.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
[{"title":"Aromate herbes de Provence","sticker":"Aromate - Herbes de Provence.svg","subs":{"dluo":"germinal 9999","lot":"0000-0","qty":"40"},"landscape":false},{"title":"Aromate herbes de Provence avec romarin","sticker":"Aromate - Herbes de Provence avec romarin.svg","subs":{"dluo":"germinal 9999","lot":"0000-0","qty":"40"},"landscape":false},{"title":"Aromate thym","sticker":"Aromate - Thym.svg","subs":{"dluo":"germinal 9999","lot":"0000-0","qty":"40"},"landscape":false},{"title":"Chocolat lavande","sticker":"Chocolat - Lavande.svg","subs":{"dluo":"germinal 9999","lot":"0000-0","qty":"100"},"landscape":false},{"title":"Confiture de Mirabelle - non bio","sticker":"Confiture - Mirabelle.svg","subs":{"dluo":"germinal 9999","fruit":"80","teneur":"50","lot":"0000-0","qty":"370"},"landscape":true},{"title":"Gelée Extra de groseille","sticker":"Gelée Extra - Groseille.svg","subs":{"dluo":"germinal 9999","fruit":"80","teneur":"50","lot":"0000-0","qty":"370"},"landscape":true},{"title":"Gelée Extra de groseille au basilic","sticker":"Gelée Extra - Groseille Basilic.svg","subs":{"dluo":"germinal 9999","fruit":"80","teneur":"50","lot":"0000-0","qty":"370"},"landscape":true},{"title":"Gelée Extra de cassis","sticker":"Gelée Extra - Cassis.svg","subs":{"dluo":"germinal 9999","fruit":"80","teneur":"50","lot":"0000-0","qty":"370"},"landscape":true},{"title":"Pesto ail des ours","sticker":"Pesto - Ail des Ours - 100% Olive.svg","subs":{"dluo":"germinal 9999","lot":"0000-0","qty":"150"},"landscape":true},{"title":"Sel grillade et légumes","sticker":"Sel - Grillade.svg","subs":{"dluo":"germinal 9999","lot":"0000-0","qty":"40"},"landscape":true},{"title":"Sel herbes de Provence","sticker":"Sel - Herbes de Provence.svg","subs":{"dluo":"germinal 9999","lot":"0000-0","qty":"40"},"landscape":true},{"title":"Sel viande blanche et poisson","sticker":"Sel - Poisson et Viande Blanche.svg","subs":{"dluo":"germinal 9999","lot":"0000-0","qty":"40"},"landscape":true},{"title":"Sel salade","sticker":"Sel - Salade.svg","subs":{"dluo":"germinal 9999","lot":"0000-0","qty":"40"},"landscape":true},{"title":"Sel salade sans basilic","sticker":"Sel - Salade - Sans Basilic.svg","subs":{"dluo":"germinal 9999","lot":"0000-0","qty":"40"},"landscape":true},{"title":"Sirop de sureau","sticker":"Sirop - Sureau.svg","subs":{"dluo":"germinal 9999","lot":"0000-0","qty":"75"}},{"title":"Sirop de lavande","sticker":"Sirop - Lavande.svg","subs":{"dluo":"germinal 9999","lot":"0000-0","qty":"75"}},{"title":"Sirop de cassis","sticker":"Sirop - Cassis.svg","subs":{"dluo":"germinal 9999","lot":"0000-0","qty":"75"},"landscape":false},{"title":"Sirop de thym","sticker":"Sirop - Thym.svg","subs":{"dluo":"germinal 9999","lot":"0000-0","qty":"75"},"landscape":false},{"title":"Sirop de mélisse","sticker":"Sirop - Melisse.svg","subs":{"dluo":"germinal 9999","lot":"0000-0","qty":"75"},"landscape":false},{"title":"Sirop de menthe verte","sticker":"Sirop - Menthe Verte.svg","subs":{"dluo":"germinal 9999","lot":"0000-0","qty":"75"},"landscape":false},{"title":"Tisane Hiver","sticker":"Tisane - Hiver.svg","subs":{"dluo":"germinal 9999","lot":"0000-0","qty":"25"},"landscape":false},{"title":"Tisane Joie de Vivre","sticker":"Tisane - Joie de Vivre.svg","subs":{"dluo":"germinal 9999","lot":"0000-0","qty":"25"},"landscape":false},{"title":"Tisane Réconfort de la Gorge (Origan, Thym, Agastache, Mauve)","sticker":"Tisane - Reconfort de la Gorge - Origan Thym Agastache Mauve.svg","subs":{"dluo":"germinal 9999","lot":"0000-0","qty":"25"},"landscape":false},{"title":"Tisane Nuit Étoilée (Mélisse, Lavande, Aubépine)","sticker":"Tisane - Nuit Etoilee - Melisse Lavande Aubepine.svg","subs":{"dluo":"germinal 9999","lot":"0000-0","qty":"25"},"landscape":false},{"title":"Tisane Détox (Menthe, Sarriette, Basilic, Souci)","sticker":"Tisane - Detox - Menthe Sarriette Basilic Souci.svg","subs":{"dluo":"germinal 9999","lot":"0000-0","qty":"25"},"landscape":false},{"title":"Tisane digestion","sticker":"Tisane - Digestion.svg","subs":{"dluo":"germinal 9999","lot":"0000-0","qty":"25"},"landscape":false}]
|
164
web.py
164
web.py
@ -1,5 +1,8 @@
|
|||||||
|
import argparse
|
||||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||||
|
import io
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import string
|
import string
|
||||||
@ -11,10 +14,22 @@ import traceback
|
|||||||
from makesticker import makesticker
|
from makesticker import makesticker
|
||||||
from makeplanche import makeplanche
|
from makeplanche import makeplanche
|
||||||
|
|
||||||
|
# defaults
|
||||||
PORT = 8000
|
PORT = 8000
|
||||||
OUT_DIR = tempfile.gettempdir()
|
OUT_DIR = tempfile.gettempdir()
|
||||||
TEMPLATE_DIR = '/data'
|
DATA_DIR = '/data'
|
||||||
|
TEMPLATE_DIR = DATA_DIR
|
||||||
DEFAULT_DPI = 300
|
DEFAULT_DPI = 300
|
||||||
|
LEMOTDEPASSE = 'je veux ajouter une étiquette'
|
||||||
|
TIKETTES = os.path.join(DATA_DIR, 'tikettes.json')
|
||||||
|
|
||||||
|
config = {}
|
||||||
|
|
||||||
|
def tikettes_path():
|
||||||
|
return config.get('tikettes', TIKETTES)
|
||||||
|
|
||||||
|
def template_dir():
|
||||||
|
return config.get('template_dir', TEMPLATE_DIR)
|
||||||
|
|
||||||
|
|
||||||
def inkscapize(svg_in, pdf_out):
|
def inkscapize(svg_in, pdf_out):
|
||||||
@ -31,7 +46,41 @@ def inkscapize(svg_in, pdf_out):
|
|||||||
os.unlink(png_out)
|
os.unlink(png_out)
|
||||||
|
|
||||||
|
|
||||||
def parse_request(request):
|
def handle_newtikette(formdata):
|
||||||
|
with open(tikettes_path()) as f:
|
||||||
|
tikettes = json.load(f)
|
||||||
|
|
||||||
|
sticker = formdata['title'] + '.svg'
|
||||||
|
with open(os.path.join(template_dir(), sticker), 'w') as f:
|
||||||
|
f.write(formdata['sticker'])
|
||||||
|
|
||||||
|
known_subs = {
|
||||||
|
"dluo": "germinal 9999",
|
||||||
|
"fruit": "80",
|
||||||
|
"teneur": "50",
|
||||||
|
"lot": "0000-0",
|
||||||
|
"qty": "370",
|
||||||
|
"vol": "50",
|
||||||
|
}
|
||||||
|
|
||||||
|
newtikette = {
|
||||||
|
'title': formdata['title'],
|
||||||
|
'sticker': sticker,
|
||||||
|
'landscape': 'landscape' in formdata,
|
||||||
|
'subs': {k: v for k, v in known_subs.items() if k in formdata},
|
||||||
|
}
|
||||||
|
|
||||||
|
logging.info(f'adding newtikette: {newtikette}')
|
||||||
|
|
||||||
|
tikettes.append(newtikette)
|
||||||
|
|
||||||
|
with open(tikettes_path(), 'w') as f:
|
||||||
|
json.dump(tikettes, f, indent=2)
|
||||||
|
|
||||||
|
return json.dumps({'status': 'ok', 'message': 'newtikette added'})
|
||||||
|
|
||||||
|
|
||||||
|
def handle_generate(request):
|
||||||
request = json.loads(request)
|
request = json.loads(request)
|
||||||
|
|
||||||
# fill in sticker details
|
# fill in sticker details
|
||||||
@ -41,7 +90,7 @@ def parse_request(request):
|
|||||||
with open(sticker_out, 'w') as stickout:
|
with open(sticker_out, 'w') as stickout:
|
||||||
landscape = request.get('landscape', False)
|
landscape = request.get('landscape', False)
|
||||||
print(f'landscape: {landscape}')
|
print(f'landscape: {landscape}')
|
||||||
makesticker(os.path.join(TEMPLATE_DIR, request['sticker']), stickout,
|
makesticker(os.path.join(template_dir(), request['sticker']), stickout,
|
||||||
request['subs'], landscape=landscape)
|
request['subs'], landscape=landscape)
|
||||||
|
|
||||||
# make sticker sheet
|
# make sticker sheet
|
||||||
@ -65,8 +114,68 @@ def parse_request(request):
|
|||||||
os.unlink(planche_out)
|
os.unlink(planche_out)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_multipart(data, boundary):
|
||||||
|
logging.debug(f'parsing data with boundary: {boundary}')
|
||||||
|
f = io.StringIO(data)
|
||||||
|
out = []
|
||||||
|
|
||||||
|
if boundary not in f.readline():
|
||||||
|
return out
|
||||||
|
|
||||||
|
while True:
|
||||||
|
headers = []
|
||||||
|
while True:
|
||||||
|
h = f.readline().strip()
|
||||||
|
try:
|
||||||
|
k, v = re.findall(r'^([a-zA-Z-]+): (.*)$', h)[0]
|
||||||
|
except IndexError:
|
||||||
|
break
|
||||||
|
headers.append((k, v))
|
||||||
|
metadata = None
|
||||||
|
otherheaders = []
|
||||||
|
logging.debug(headers)
|
||||||
|
for k, v in headers:
|
||||||
|
if k == 'Content-Disposition':
|
||||||
|
parts = v.split('; ')
|
||||||
|
if parts[0] != 'form-data':
|
||||||
|
continue
|
||||||
|
d = dict(re.findall(r'([^=]+)="(.*)"$', p)[0] for p in parts[1:])
|
||||||
|
metadata = d
|
||||||
|
else:
|
||||||
|
otherheaders.append((k, v))
|
||||||
|
|
||||||
|
value = io.StringIO()
|
||||||
|
while True:
|
||||||
|
l = f.readline()
|
||||||
|
if not l:
|
||||||
|
break
|
||||||
|
if boundary in l:
|
||||||
|
# value.write(l.split(boundary)[0])
|
||||||
|
break
|
||||||
|
value.write(l)
|
||||||
|
if metadata is None:
|
||||||
|
break
|
||||||
|
logging.debug(f'({metadata}, {value.getvalue()[:-2]}, {otherheaders})')
|
||||||
|
out.append((metadata, value.getvalue()[:-2], otherheaders))
|
||||||
|
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
class MyServer(BaseHTTPRequestHandler):
|
class MyServer(BaseHTTPRequestHandler):
|
||||||
def do_GET(self):
|
def do_GET(self):
|
||||||
|
logging.info(f'GET {self.path}')
|
||||||
|
if self.path == '/list':
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header("Content-type", "application/json")
|
||||||
|
self.send_header('Access-Control-Allow-Origin', '*')
|
||||||
|
self.end_headers()
|
||||||
|
with open(tikettes_path()) as f:
|
||||||
|
tikettes = json.load(f)
|
||||||
|
|
||||||
|
resp = {'status': 'ok', 'message': 'this is the way',
|
||||||
|
'tikettes': tikettes}
|
||||||
|
self.wfile.write(json.dumps(resp).encode())
|
||||||
|
return
|
||||||
match = re.match(r'/data/(\w+\.pdf)', self.path)
|
match = re.match(r'/data/(\w+\.pdf)', self.path)
|
||||||
if match is not None:
|
if match is not None:
|
||||||
pdf_path = os.path.join(OUT_DIR, match.groups()[0])
|
pdf_path = os.path.join(OUT_DIR, match.groups()[0])
|
||||||
@ -89,14 +198,37 @@ class MyServer(BaseHTTPRequestHandler):
|
|||||||
self.wfile.write("<p>This is not the way.</p>".encode())
|
self.wfile.write("<p>This is not the way.</p>".encode())
|
||||||
self.wfile.write(b"</body></html>")
|
self.wfile.write(b"</body></html>")
|
||||||
|
|
||||||
|
def do_OPTIONS(self):
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header('Access-Control-Allow-Origin', '*')
|
||||||
|
self.end_headers()
|
||||||
|
|
||||||
def do_POST(self):
|
def do_POST(self):
|
||||||
|
logging.info(f'POST {self.path}')
|
||||||
try:
|
try:
|
||||||
length = int(self.headers['content-length'])
|
length = int(self.headers['content-length'])
|
||||||
req = self.rfile.read(length).decode()
|
req = self.rfile.read(length).decode()
|
||||||
resp = parse_request(req).encode()
|
if self.path == '/newtikette':
|
||||||
|
boundary = re.findall(r'boundary=(.*)$', self.headers['content-type'])[0]
|
||||||
|
formdata = {p['name']: v for p, v, _ in parse_multipart(req, boundary)}
|
||||||
|
logging.info({k: v[:100] for k, v in formdata.items()})
|
||||||
|
|
||||||
|
if formdata.get('lemotdepasse', None) != LEMOTDEPASSE:
|
||||||
|
logging.warning(f'wrong lemotdepasse')
|
||||||
|
self.send_response(403)
|
||||||
|
self.send_header("Content-type", "application/json")
|
||||||
|
self.send_header('Access-Control-Allow-Origin', '*')
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(json.dumps({'status': 'notok', 'message': 'bad lemotdepasse'}).encode())
|
||||||
|
return
|
||||||
|
|
||||||
|
resp = handle_newtikette(formdata).encode()
|
||||||
|
else:
|
||||||
|
resp = handle_generate(req).encode()
|
||||||
except:
|
except:
|
||||||
self.send_response(500)
|
self.send_response(500)
|
||||||
self.send_header("Content-type", "text/plain")
|
self.send_header("Content-type", "text/plain")
|
||||||
|
self.send_header('Access-Control-Allow-Origin', '*')
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
self.wfile.write(traceback.format_exc().encode())
|
self.wfile.write(traceback.format_exc().encode())
|
||||||
raise
|
raise
|
||||||
@ -108,9 +240,25 @@ class MyServer(BaseHTTPRequestHandler):
|
|||||||
self.wfile.write(resp)
|
self.wfile.write(resp)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
def parse_args():
|
||||||
webServer = HTTPServer(('', PORT), MyServer)
|
parser = argparse.ArgumentParser(description='Zetikette backend')
|
||||||
print(f"Server started on port {PORT}")
|
parser.add_argument('--port', type=int, help=f'default: {PORT}')
|
||||||
|
parser.add_argument('--data-dir', help=f'default: {DATA_DIR}')
|
||||||
|
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
args = parse_args()
|
||||||
|
|
||||||
|
config.update({
|
||||||
|
'tikettes': os.path.join(args.data_dir, 'tikettes.json'),
|
||||||
|
'template_dir': args.data_dir,
|
||||||
|
})
|
||||||
|
|
||||||
|
webServer = HTTPServer(('', args.port), MyServer)
|
||||||
|
print(f"Server started on port {args.port}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
webServer.serve_forever()
|
webServer.serve_forever()
|
||||||
@ -118,4 +266,4 @@ if __name__ == "__main__":
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
webServer.server_close()
|
webServer.server_close()
|
||||||
print("Server stopped.")
|
print("Server stopped.")
|
Loading…
Reference in New Issue
Block a user