2024-07-23 02:55:43 -04:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
|
|
import subprocess
|
|
|
|
import pathlib
|
|
|
|
import argparse
|
|
|
|
import datetime
|
|
|
|
import tempfile
|
|
|
|
import tomllib
|
2024-09-01 00:02:45 -04:00
|
|
|
from PIL import Image
|
2024-07-23 02:56:14 -04:00
|
|
|
|
2024-07-23 03:30:02 -04:00
|
|
|
def get_period_string(
|
|
|
|
start: datetime.datetime,
|
|
|
|
end: datetime.datetime,
|
|
|
|
year: bool = False,
|
|
|
|
) -> str:
|
|
|
|
"""Get a string representation of the period
|
|
|
|
|
|
|
|
Args:
|
|
|
|
start (datetime.datetime): The start of the period
|
|
|
|
end (datetime.datetime): The end of the period
|
|
|
|
year (bool, optional): Include the year in the string. Defaults to False.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
str: The period string
|
|
|
|
"""
|
|
|
|
if period_start.year == period_end.year:
|
|
|
|
if period_start.month == period_end.month:
|
|
|
|
period_string = (
|
2024-08-09 14:53:28 -04:00
|
|
|
f"{period_start.strftime('%B %-d')} - {period_end.strftime('%-d')}"
|
2024-07-23 03:30:02 -04:00
|
|
|
)
|
|
|
|
else:
|
|
|
|
period_string = (
|
2024-08-09 14:53:28 -04:00
|
|
|
f"{period_start.strftime('%B %-d')} - {period_end.strftime('%B %-d')}"
|
2024-07-23 03:30:02 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
if year:
|
|
|
|
period_string += f", {period_start.year}"
|
2024-07-23 02:56:14 -04:00
|
|
|
|
2024-07-23 03:30:02 -04:00
|
|
|
else:
|
|
|
|
period_string = (
|
2024-08-09 14:53:28 -04:00
|
|
|
f"{period_start.strftime('%B %-d, %Y')} - {period_end.strftime('%B %-d, %Y')}"
|
2024-07-23 03:30:02 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
return period_string
|
|
|
|
|
|
|
|
|
|
|
|
def create_issue(
|
|
|
|
issue_number: int, period_start: datetime.datetime, period_end: datetime.datetime
|
|
|
|
) -> None:
|
|
|
|
"""Create a new issue
|
|
|
|
|
|
|
|
Args:
|
|
|
|
issue_number (int): The issue number
|
|
|
|
period_start (datetime.datetime): The start of the period
|
|
|
|
period_end (datetime.datetime): The end of the period
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
subprocess.CalledProcessError: If the Hugo command fails
|
|
|
|
IOError: If any of the Python file operations fail
|
|
|
|
"""
|
2024-07-23 03:31:16 -04:00
|
|
|
# Use the Hugo CLI to create a new issue
|
2024-07-23 03:30:02 -04:00
|
|
|
subprocess.run(
|
|
|
|
["hugo", "new", f"weekly/issue-{issue_number}/_index.md"], check=True
|
|
|
|
)
|
|
|
|
|
2024-07-23 03:31:16 -04:00
|
|
|
# Read the content of the new file
|
2024-07-23 03:30:02 -04:00
|
|
|
with open(f"content/weekly/issue-{issue_number}/_index.md", "r") as f:
|
|
|
|
content = f.read()
|
|
|
|
|
|
|
|
# Get line that starts with "title" and replace it with the new title
|
|
|
|
lines = content.split("\n")
|
|
|
|
|
|
|
|
period_string = get_period_string(period_start, period_end, True)
|
|
|
|
|
|
|
|
for i, line in enumerate(lines):
|
|
|
|
if line.startswith("title:"):
|
2024-07-25 15:45:07 -04:00
|
|
|
lines[i] = f'title: "Issue {issue_number}: {period_string}"'
|
2024-07-23 03:30:02 -04:00
|
|
|
|
|
|
|
content = "\n".join(lines)
|
|
|
|
|
2024-07-23 03:31:16 -04:00
|
|
|
# Overwrite the file with the new content
|
2024-07-23 03:30:02 -04:00
|
|
|
with open(f"content/weekly/issue-{issue_number}/_index.md", "w") as f:
|
|
|
|
f.write(content)
|
2024-07-23 02:55:43 -04:00
|
|
|
|
2024-07-23 03:30:02 -04:00
|
|
|
|
|
|
|
def create_issue_image(
|
|
|
|
site_title: str,
|
|
|
|
issue_number: int,
|
|
|
|
period_start: datetime.datetime,
|
|
|
|
period_end: datetime.datetime,
|
|
|
|
) -> None:
|
|
|
|
"""Create a cover image for the issue
|
|
|
|
|
|
|
|
Args:
|
|
|
|
site_title (str): The title of the site
|
|
|
|
issue_number (int): The issue number
|
|
|
|
period_start (datetime.datetime): The start of the period
|
|
|
|
period_end (datetime.datetime): The end of the period
|
|
|
|
|
|
|
|
Raises:
|
|
|
|
FileNotFoundError: If the cover template is not found
|
|
|
|
IOError: If any of the file operations fail
|
|
|
|
"""
|
2024-07-23 02:56:14 -04:00
|
|
|
with open("assets/img/cover-template.svg") as f:
|
|
|
|
cover_template = f.read()
|
2024-07-23 02:55:43 -04:00
|
|
|
|
2024-07-23 03:30:02 -04:00
|
|
|
period_string = get_period_string(period_start, period_end)
|
|
|
|
|
2024-07-23 02:56:14 -04:00
|
|
|
cover_template = cover_template.replace("__SITE__", site_title)
|
2024-08-07 02:46:30 -04:00
|
|
|
cover_template = cover_template.replace("__TITLE__", f"ISSUE {issue_number}")
|
2024-07-23 03:30:02 -04:00
|
|
|
cover_template = cover_template.replace("__DATE__", period_string)
|
2024-07-23 02:55:43 -04:00
|
|
|
|
2024-07-23 02:56:14 -04:00
|
|
|
with tempfile.TemporaryDirectory() as tempdir:
|
|
|
|
cover_path = pathlib.Path(tempdir) / "cover.svg"
|
|
|
|
with open(cover_path, "w") as f:
|
|
|
|
f.write(cover_template)
|
|
|
|
|
|
|
|
subprocess.run(
|
|
|
|
[
|
|
|
|
"inkscape",
|
|
|
|
str(cover_path),
|
2024-07-23 03:30:02 -04:00
|
|
|
"--export-filename",
|
2024-07-23 02:56:14 -04:00
|
|
|
f"content/weekly/issue-{issue_number}/cover.png",
|
|
|
|
]
|
|
|
|
)
|
2024-07-23 02:55:43 -04:00
|
|
|
|
2024-09-01 00:02:45 -04:00
|
|
|
image = Image.open(f"content/weekly/issue-{issue_number}/cover.png")
|
|
|
|
image = image.convert("RGB").quantize(colors=256)
|
|
|
|
image.save(f"content/weekly/issue-{issue_number}/cover.png", "PNG", compress_level=9)
|
2024-07-23 02:55:43 -04:00
|
|
|
|
2024-07-23 03:30:02 -04:00
|
|
|
def get_latest_issue() -> int:
|
|
|
|
"""Get the latest issue number from the weekly directory
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
int: The latest issue number
|
|
|
|
"""
|
2024-07-23 02:56:14 -04:00
|
|
|
issues = list(pathlib.Path("content/weekly").iterdir())
|
|
|
|
latest_issue = max(issues, key=lambda x: int(x.name.split("-")[1]))
|
2024-07-23 03:30:02 -04:00
|
|
|
return int(latest_issue.name.split("-")[1])
|
2024-07-23 02:56:14 -04:00
|
|
|
|
2024-07-23 02:55:43 -04:00
|
|
|
|
2024-07-23 02:56:14 -04:00
|
|
|
if __name__ == "__main__":
|
|
|
|
with open("hugo.toml", "rb") as f:
|
|
|
|
hugo_config = tomllib.load(f)
|
2024-07-23 02:55:43 -04:00
|
|
|
|
2024-07-23 02:56:14 -04:00
|
|
|
parser = argparse.ArgumentParser(description="Create a new issue")
|
|
|
|
parser.add_argument("--issue", type=int, help="Issue number")
|
2024-07-23 03:30:02 -04:00
|
|
|
parser.add_argument("--period-start", type=str, help="Period start")
|
|
|
|
parser.add_argument("--period-end", type=str, help="Period end")
|
|
|
|
parser.add_argument("--quiet", "-q", action="store_true", help="Quiet mode")
|
2024-07-23 02:56:14 -04:00
|
|
|
args = parser.parse_args()
|
2024-07-23 02:55:43 -04:00
|
|
|
|
2024-07-23 03:30:02 -04:00
|
|
|
if not (args.period_start and args.period_end):
|
|
|
|
if not args.quiet:
|
|
|
|
# Query the user for the period start and end
|
|
|
|
today = datetime.datetime.now().strftime("%Y-%m-%d")
|
|
|
|
|
|
|
|
period_start_str = input(f"Period start [{today}]: ") or today
|
|
|
|
period_start = datetime.datetime.strptime(period_start_str, "%Y-%m-%d")
|
|
|
|
|
|
|
|
seven_days = (period_start + datetime.timedelta(days=7)).strftime(
|
|
|
|
"%Y-%m-%d"
|
|
|
|
)
|
|
|
|
|
|
|
|
period_end_str = input(f"Period end [{seven_days}]: ") or seven_days
|
|
|
|
period_end = datetime.datetime.strptime(period_end_str, "%Y-%m-%d")
|
|
|
|
|
|
|
|
else:
|
|
|
|
# Assume the period is the next 7 days - # TODO: Check if this is correct
|
|
|
|
period_start = datetime.datetime.now()
|
|
|
|
period_end = datetime.datetime.now() + datetime.timedelta(days=7)
|
|
|
|
|
2024-07-23 02:56:14 -04:00
|
|
|
if args.issue:
|
|
|
|
new_issue = args.issue
|
|
|
|
else:
|
|
|
|
latest_issue = get_latest_issue()
|
|
|
|
new_issue = int(latest_issue) + 1
|
2024-07-23 02:55:43 -04:00
|
|
|
|
2024-07-23 03:30:02 -04:00
|
|
|
create_issue(new_issue, period_start, period_end)
|
2024-08-09 14:45:44 -04:00
|
|
|
create_issue_image("Revuo Weekly", new_issue, period_start, period_end)
|