diff options
author | Elena ``of Valhalla'' Grandi <valhalla@trueelena.org> | 2021-12-28 16:16:59 +0100 |
---|---|---|
committer | Elena ``of Valhalla'' Grandi <valhalla@trueelena.org> | 2021-12-28 16:16:59 +0100 |
commit | 81a6987e04106e8168b55cd84fbe6a7cd8a9d912 (patch) | |
tree | a1769bed4da630f39c823af8d61a6d26d8146058 /planner_generator.py | |
parent | bfaa3d29bbc6e485da1923afb1cebfcf9f0a1477 (diff) |
Converted weekly planner generator into a proper command
Diffstat (limited to 'planner_generator.py')
-rwxr-xr-x | planner_generator.py | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/planner_generator.py b/planner_generator.py new file mode 100755 index 0000000..b86ce68 --- /dev/null +++ b/planner_generator.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python3 + +import argparse +import calendar +import datetime +import locale +import logging +import os +import shutil +import subprocess +import sys + +from typing import Optional + +try: + from lesana.command import _get_first_docstring_line # type: ignore +except ImportError: + def _get_first_docstring_line(obj): + return "" + +try: + import argcomplete # type: ignore +except ImportError: + argcomplete = False + +import jinja2 + + +locale.setlocale(locale.LC_ALL, '') + + +class Generator: + """ + """ + default_template = "planner-A6" + + def __init__( + self, + year: Optional[int] = None, + template: Optional[str] = None, + out_file: Optional[str] = None, + build_dir: Optional[str] = "build", + ): + self.year = year or ( + datetime.date.today() + datetime.timedelta(days=334) + ).year + + self.out_file = out_file or self.default_template + ".pdf" + + env = jinja2.Environment() + self.templates_dir = "templates" + loader = jinja2.FileSystemLoader(self.templates_dir) + if not template: + template = self.default_template + self.template_recto = loader.load(env, template + "-r.svg") + self.template_verso = loader.load(env, template + "-v.svg") + self.template_cover = loader.load(env, "cover-A6-r.svg") + self.build_dir = build_dir + self.page_fname = os.path.join( + self.build_dir, + template + "-{year}-{page:03}.svg" + ) + + def render_page(self, page: int, **kw): + # page counts starts with 0 + if page == 0: + template = self.template_cover + elif page % 2 == 0: + template = self.template_recto + else: + template = self.template_verso + with open(self.page_fname.format( + year=self.year, page=page + ), "w") as fp: + fp.write(template.render(**kw)) + + def run(self): + self.clean_build_dir() + self.generate_cover_page() + self.generate_pages() + self.convert_pages_to_svg() + self.join_pages() + + def clean_build_dir(self): + try: + shutil.rmtree(self.build_dir) + except FileNotFoundError: + pass + os.makedirs(self.build_dir) + + def generate_cover_page(self): + self.render_page(page=0, year=self.year) + + def generate_pages(self): + pass + + def convert_pages_to_svg(self): + inkscape_commands = ";\n".join([ + ( + "file-open:{build_dir}/{svg};" + + " export-type: pdf;" + + " export-filename:build/{pdf};" + + " export-text-to-path;" + + " export-do" + ).format( + build_dir=self.build_dir, + svg=s, + pdf=os.path.splitext(s)[0] + ".pdf", + ) + for s in os.listdir(self.build_dir) + ]) + try: + subprocess.run( + ["inkscape", "--shell"], + input=inkscape_commands, + text=True, + ) + except FileNotFoundError: + logging.warning("Inkscape is not installed, can't convert to pdf") + logging.warning("Stopping here, you can use the svgs as you like") + sys.exit(1) + + def get_pdf_pages(self): + pdf_pages = sorted([ + os.path.join(self.build_dir, p) + for p in os.listdir(self.build_dir) + if p.endswith(".pdf") + ]) + return pdf_pages + + def join_pages(self): + pdf_pages = self.get_pdf_pages() + try: + subprocess.run([ + "pdfjam", + "--outfile", self.out_file, + *pdf_pages + ]) + except FileNotFoundError: + logging.warning("pdfjam is not installed") + logging.warning("you will have to join the pdf pages yourself") + sys.exit(1) + + +class WeeklyGenerator(Generator): + default_template = "week_on_two_pages-A6" + + def generate_pages(self): + cal = calendar.Calendar() + weeks = sum( + [r[0] for r in cal.yeardatescalendar(self.year, width=1)], + [] + ) + + last_monday = None + page = 1 + for week in weeks: + # yeardatescalendar will have the same week twice at the + # margin of a month, but we want to skip one of those + if week[0] == last_monday: + continue + last_monday = week[0] + + self.render_page(page=page, week=week) + page += 1 + + self.render_page(page=page, week=week) + page += 1 + + +class Command: + """ + Generate a planner + """ + def get_parser(self): + desc = _get_first_docstring_line(self) + parser = argparse.ArgumentParser(description=desc) + parser.add_argument( + "--year", '-y', + default=None, + help="Default is next year, or this year in January." + ) + parser.add_argument( + "--template", '-t', + default=None, + help="Base name of the template (without -[rv].svg)", + ) + parser.add_argument( + "command", + ) + return parser + + def main(self): + self.parser = self.get_parser() + if argcomplete: + argcomplete.autocomplete(self.parser) + self.args = self.parser.parse_args() + + generator = getattr( + sys.modules[__name__], + self.args.command.capitalize() + "Generator", + None + ) + if generator: + generator( + year=self.args.year, + template=self.args.template, + ).run() + else: + print("command not supported: {}".format(self.args.command)) + + +if __name__ == "__main__": + Command().main() |