summaryrefslogtreecommitdiff
path: root/planner_generator.py
diff options
context:
space:
mode:
authorElena ``of Valhalla'' Grandi <valhalla@trueelena.org>2023-08-12 10:13:06 +0200
committerElena ``of Valhalla'' Grandi <valhalla@trueelena.org>2023-08-12 10:13:06 +0200
commiteb9d06cb1cd79f37edcf44f122a36f124a82503e (patch)
tree82591be71aad59c0393890c051a022a2c1d78d64 /planner_generator.py
parente15dddfe0da883915a49d6120feaceb1a42d314a (diff)
Moved planner generator to a subdirectory
Diffstat (limited to 'planner_generator.py')
-rwxr-xr-xplanner_generator.py396
1 files changed, 0 insertions, 396 deletions
diff --git a/planner_generator.py b/planner_generator.py
deleted file mode 100755
index f2c6408..0000000
--- a/planner_generator.py
+++ /dev/null
@@ -1,396 +0,0 @@
-#!/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,
- cover_template: Optional[str] = None,
- out_file: Optional[str] = None,
- build_dir: Optional[str] = "build",
- latitude: Optional[float] = None,
- longitude: Optional[float] = None,
- timezone: Optional[str] = None,
- ):
- self.year = year or (
- datetime.date.today() + datetime.timedelta(days=334)
- ).year
-
- self.latitude = latitude
- self.longitude = longitude
- self.timezone = timezone
-
- self.out_file = out_file or (
- template or self.default_template
- ) + ".pdf"
-
- self.paper_size = self._get_paper_size(template)
-
- 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,
- self._get_cover_name(cover_template)
- )
- self.build_dir = build_dir
- self.page_fname = os.path.join(
- self.build_dir,
- template + "-{year}-{page:03}.svg"
- )
-
- def _get_paper_size(self, template):
- if "A6" in template:
- return "a6"
- if "A5" in template:
- return "a5"
- if "A4" in template:
- return "a4"
- return "a6"
-
- def _get_cover_name(self, cover_template):
- if cover_template:
- return cover_template
- return "cover-{}-r.svg".format(self.paper_size.upper())
-
- 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,
- "--scale", "1",
- "--paper", "{}paper".format(self.paper_size),
- *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 DailyGenerator(Generator):
- """
- """
- default_template = "daily-A6"
-
- def generate_pages(self):
- day = datetime.date(self.year, 1, 1)
-
- # we want to start with a left side page (starting from 0)
- page = 2
- while day.year == self.year:
- self.render_page(page=page, day=day)
- page += 1
- day += datetime.timedelta(days=1)
-
- if day.year > self.year:
- break
-
- self.render_page(page=page, day=day)
- page += 1
- day += datetime.timedelta(days=1)
-
- def get_pdf_pages(self):
- pdf_pages = super().get_pdf_pages()
- # insert an empty page on the second page, to start the year on
- # a left page
- pdf_pages.insert(1, "1, {}")
-
- return pdf_pages
-
-
-class MonthGenerator(Generator):
- """
- """
- default_template = "month-A6"
-
- def generate_pages(self):
- cal = calendar.Calendar()
- full_year = cal.yeardatescalendar(self.year, width=1)
- months = []
-
- for i in range(12):
- months.append([
- day for week in full_year[i][0] for day in week
- if day.month == i + 1
- ])
-
- texts = self.get_texts()
-
- page = 2
- for i, month in enumerate(months):
- self.render_page(page=page, month=month, text=texts[i])
- page += 1
-
- def generate_cover_page(self):
- pass
-
- def get_texts(self):
- return [[] for i in range(12)]
-
-
-class EphemerismonthGenerator(MonthGenerator):
- """
- """
- default_template = "month-A6"
-
- def get_texts(self):
- # we import suntime just here, because it's a third party
- # library and not used elsewhere
- try:
- import astral
- except ImportError:
- print("Printing a month planner with ephemeris requires"
- "the astral library.")
- sys.exit(1)
-
- if not self.latitude or not self.longitude or not self.timezone:
- print("Printing ephemeris requires latitude and longitude")
- sys.exit(1)
-
- location = astral.Location((
- "",
- "",
- self.latitude,
- self.longitude,
- self.timezone,
- 0
- ))
-
- day = datetime.date(self.year, 1, 1)
-
- texts = []
- while day.year == self.year:
- month = []
- this_month = day.month
- while day.month == this_month:
- sunrise = location.sunrise(day)
- noon = location.solar_noon(day)
- sunset = location.sunset(day)
- moon_phase = location.moon_phase(day)
- if moon_phase < 7:
- moon_icon = "●"
- elif moon_phase < 14:
- moon_icon = "☽"
- elif moon_phase < 21:
- moon_icon = "○"
- else:
- moon_icon = "☾"
- text = ("☼ {sunrise} — {noon} — {sunset} "
- + "{moon_icon} {moon_phase}").format(
- sunrise=sunrise.strftime("%H:%M"),
- noon=noon.strftime("%H:%M"),
- sunset=sunset.strftime("%H:%M"),
- moon_icon=moon_icon,
- moon_phase=moon_phase,
- )
- month.append(text)
- day += datetime.timedelta(days=1)
- texts.append(month)
-
- return texts
-
-
-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(
- "--cover-template",
- default=None,
- help="Full name of the template (including -[rv].svg)",
- )
- parser.add_argument(
- "--latitude",
- default=None,
- type=float,
- help="Latitude for ephemeris calculation",
- )
- parser.add_argument(
- "--longitude",
- default=None,
- type=float,
- help="Longitude for ephemeris calculation",
- )
- parser.add_argument(
- "--timezone",
- default=None,
- help="Timezone for ephemeris calculation (e.g. Europe/Rome)",
- )
- 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,
- cover_template=self.args.cover_template,
- latitude=self.args.latitude,
- longitude=self.args.longitude,
- timezone=self.args.timezone,
- ).run()
- else:
- print("command not supported: {}".format(self.args.command))
-
-
-if __name__ == "__main__":
- Command().main()