main.py 5.82 KiB
#!/usr/bin/env python
import argparse
import json
import os
import re
import time
import pathlib
import requests
from slugify import slugify
from fpdf import FPDF
HERE = pathlib.Path(__file__).resolve().parent
class Box:
re_title = re.compile(r"Box:\s*(.*)$")
re_weight = re.compile(r"\*\s*(.*)kg$")
re_content = re.compile(r"\*\s*(.*)\s*$")
re_size_2d = re.compile(r"\*\s*(\d+)[x\*](\d+)\s*$")
re_size_3d = re.compile(r"\*\s*(\d+)[x\*](\d+)[x\*](\d+)\s*$")
def __init__(self, lines):
self.title = ""
self.slug = ""
self.weight = ""
self.size = ""
self.content = []
for line in lines:
title = Box.re_title.match(line)
if title:
self.title = title[1]
self.slug = slugify(self.title)
continue
weight = Box.re_weight.match(line)
if weight:
self.weight = weight[1].strip()
continue
size_2d = Box.re_size_2d.match(line)
if size_2d:
self.size = f"{size_2d[1]}x{size_2d[2]}"
continue
size_3d = Box.re_size_3d.match(line)
if size_3d:
self.size = f"{size_3d[1]}x{size_3d[2]}x{size_3d[3]}"
continue
content = Box.re_content.match(line)
if content:
self.content.append(content[1])
def __str__(self):
return f"{self.title} ({self.size} | {self.weight} kg)"
class Loader:
def __init__(self, url="https://pad.stratum0.org/p/inventar_eventfoo/export/txt", max_age=60):
self._url = url
self.boxes = []
self._raw_lines = []
cache_base = os.getenv("XDG_CACHE_HOME")
if not cache_base:
cache_base = os.path.join(os.getenv("HOME"), ".cache")
cache_fn = os.path.join(cache_base, "hoa-inventory-boxes.json")
reload = True
if os.path.isfile(cache_fn):
try:
with open(cache_fn) as fh:
cache = json.load(fh)
if time.time() - cache["ts"] < max_age:
reload = False
self._raw_lines = cache["raw"]
except json.decoder.JSONDecodeError:
pass
if reload:
self._load_via_http()
with open(cache_fn, "w") as fh:
json.dump({"ts": time.time(), "raw": self._raw_lines}, fh)
else:
self._raw_lines = cache["raw"]
self._parse()
def _load_via_http(self):
r = requests.get(self._url)
r.raise_for_status()
self._raw_lines = [x.strip() for x in r.text.splitlines()]
def _parse(self):
in_list = False
curr = []
for line in self._raw_lines:
if not in_list:
if line.startswith("Box-Inventar"):
in_list = True
else:
if line.startswith("======="):
continue
if line.startswith("Box: "):
# this starts a new block
if curr:
self.boxes.append(Box(curr))
curr = []
if line:
curr.append(line)
class Brother100x62(FPDF):
def __init__(self):
super().__init__(orientation="l", format=(62, 100))
self.add_font(family="Geo", fname=HERE / "contrib" / "Geo-Regular.ttf")
self.add_font(family="Fira", fname=HERE / "contrib" / "FiraSans-Regular.ttf")
self.set_margin(0)
self.set_auto_page_break(margin=0, auto=True)
def header(self):
self.image(HERE / "contrib/specht.png", x=100 - 12, y=62 - 25, w=10, h=30, keep_aspect_ratio=True)
def add_box(self, box: Box):
self.add_page()
self.set_font("Geo")
self.set_font_size(30)
self.set_char_spacing(-1)
self.multi_cell(w=0, txt=box.title, align="L")
self.rect(x=0, y=20, w=100, h=0.5, style="F")
self.set_font("Fira")
self.set_font_size(12)
self.set_char_spacing(0)
self.text(x=1, y=24.5, txt=f"{box.size} cm³| {box.weight} kg")
self.set_xy(0, 27)
self.multi_cell(w=0, txt="\n".join([f"- {x}" for x in box.content]), align="L")
designs = {
"brother_100x62": Brother100x62,
}
def _list(args, loader):
for box in loader.boxes:
print(f"{box.slug:50} {box}")
def _print(args, loader):
if len(args.box_name) == 1 and args.box_name[0] == "all":
boxes = loader.boxes
else:
# check if all boxes are known
boxes = []
for bn in args.box_name:
for box in loader.boxes:
if bn == box.slug or bn == box.title:
boxes.append(box)
break
else:
print(f"Unknown box '{bn}'")
exit(1)
# generate labels for boxes
design = designs[args.label]
pdf = design()
for box in boxes:
pdf.add_box(box)
pdf.output(HERE / "tmp" / "out.pdf")
def main():
loader = Loader()
parser = argparse.ArgumentParser()
parser.add_argument("--no-cache", help="Do not use cache (if exists)", action="store_true")
subparsers = parser.add_subparsers(title="commands")
parser_list = subparsers.add_parser("list", help="Lists all defined boxes")
parser_list.set_defaults(func=_list)
parser_print = subparsers.add_parser("print", help="Prints labels for boxes")
parser_print.add_argument("box_name", help="Name of the box", nargs="*")
parser_print.add_argument("--label", "-l", help="Label identification", default="brother_100x62")
parser_print.set_defaults(func=_print)
args = parser.parse_args()
if args.no_cache:
loader = Loader(max_age=0)
else:
loader = Loader()
args.func(args, loader)
if __name__ == "__main__":
main()