commit 60cfcc70ddbbf0baa6cd07f6e41f089f08459aee Author: Kumi Date: Thu Aug 31 21:43:48 2023 +0200 feat: Add .gitignore, LICENSE, README, pyproject.toml, and kalente source files - Added .gitignore file to specify files and directories to ignore in version control. - Added LICENSE file with MIT license. - Added README.md file with project information and license details. - Added pyproject.toml file with project metadata and dependencies. - Added kalente source files for generating PDF calendars. - Implemented functions for generating weekly calendars, converting HTML to PDF, and CLI parsing. - Created a template for rendering weekly calendars in HTML format. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8f19b89 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +venv/ +*.pyc +__pycache__/ +*.pdf \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..59740a6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2023 Kumi Mitterer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..0ed931f --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# Kalente + +Kalente is a simple Python script for generating PDF calendars. It can be used +to generate weekly calendars at the moment, but it will be extended to support +monthly and yearly calendars as well. + +## License + +Kalente is licensed under the MIT license. See the `LICENSE` file for more +information. \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..23123cb --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,32 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "kalente" +version = "0.1.0" +authors = [ + { name="Kumi Mitterer", email="kalente@kumi.email" }, +] +description = "Simple Python script to generate a printable calendar" +readme = "README.md" +license = { file="LICENSE" } +requires-python = ">=3.10" +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", +] +dependencies = [ + "holidays", + "pdfkit", + "jinja2", + "python-dateutil", +] + +[project.urls] +"Homepage" = "https://kumig.it/kumitterer/kalente" +"Bug Tracker" = "https://kumig.it/kumitterer/kalente/issues" + +[project.scripts] +kalente = "kalente.__main__:main" \ No newline at end of file diff --git a/src/kalente/__init__.py b/src/kalente/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/kalente/__main__.py b/src/kalente/__main__.py new file mode 100644 index 0000000..a7e575e --- /dev/null +++ b/src/kalente/__main__.py @@ -0,0 +1,121 @@ +import holidays +import pdfkit + +from argparse import ArgumentParser +from datetime import date, timedelta +from pathlib import Path +from typing import Optional + +from jinja2 import Environment, FileSystemLoader +from dateutil.parser import parse + + +def get_week( + for_date: date = None, + country_code: Optional[str] = None, + date_format: str = "%b %d, %Y", +): + week_days = [] + + for_date = for_date or date.today() + week_start = for_date - timedelta(days=for_date.weekday()) + week_end = week_start + timedelta(days=6) + + if country_code: + holiday_list = holidays.CountryHoliday( + country_code, years=[for_date.year, week_end.year, week_start.year] + ) + else: + holiday_list = {} + + for i in range(7): + day = week_start + timedelta(days=i) + day_info = { + "day": day.strftime("%A"), + "date": day.strftime(date_format), + "holiday": holiday_list.get(day), + "is_weekend": (day.weekday() in [5, 6]), + } + week_days.append(day_info) + return week_days + + +def generate_weekly_html(week): + file_loader = FileSystemLoader(Path(__file__).parent.absolute() / "templates") + env = Environment(loader=file_loader) + template = env.get_template("weekly.html") + return template.render(week=week) + + +def convert_html_to_pdf(content, output_filename): + options = { + "page-size": "A4", + "orientation": "Landscape", + } + pdfkit.from_string(content, output_filename, options=options) + + +def main(): + parser = ArgumentParser() + + parser.add_argument( + "--country", + "-c", + help="Country code for the holidays", + required=False, + default=None, + ) + parser.add_argument( + "--output", "-o", help="Output filename", required=False, default="calendar.pdf" + ) + parser.add_argument( + "--date", + "-d", + help="Date to generate the calendar for", + required=False, + default=None, + ) + parser.add_argument( + "--date-format", + "-f", + help="Date format to use", + required=False, + default="%b %d, %Y", + ) + parser.add_argument( + "--type", + "-t", + help="Type of calendar to generate", + required=False, + default="weekly", + ) + + args = parser.parse_args() + + if args.country: + country_code = args.country.upper() + assert ( + country_code in holidays.list_supported_countries() + ), f"Country code {country_code} is not supported" + + else: + country_code = None + + if args.date: + try: + for_date = parse(args.date).date() + except ValueError: + raise ValueError(f"Unrecognized date format {args.date}") + else: + for_date = None + + if args.type != "weekly": + raise NotImplementedError(f"Calendar type {args.type} is not supported") + + week = get_week(for_date, country_code, args.date_format) + html_content = generate_weekly_html(week) + convert_html_to_pdf(html_content, args.output) + + +if __name__ == "__main__": + main() diff --git a/src/kalente/templates/weekly.html b/src/kalente/templates/weekly.html new file mode 100644 index 0000000..31cad9f --- /dev/null +++ b/src/kalente/templates/weekly.html @@ -0,0 +1,56 @@ + + + + + + +

Weekly Calendar

+ + + {% for day in week %} + + {% endfor %} + + + {% for day in week %} + + {% endfor %} + +
{{ day.day }}
{{ day.date }}
+ {% if day.holiday %} + {{ day.holiday }} + {% endif %} +
+ +