From a9f52aad98bb8e219c817a1dc84aea39f2c6f137 Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Mon, 18 Nov 2024 14:59:43 -0800 Subject: [PATCH] Rework a bunch of scripts into a single website script --- scripts/convert_page.py | 86 +++++++++++++++++ scripts/{website => erynwells_me}/__init__.py | 0 scripts/erynwells_me/blog.py | 12 +++ scripts/{website => erynwells_me}/dates.py | 0 scripts/{website => erynwells_me}/metadata.py | 0 scripts/{website => erynwells_me}/paths.py | 0 scripts/erynwells_me/scripting/__init__.py | 1 + scripts/erynwells_me/scripting/command.py | 24 +++++ scripts/erynwells_me/weeknotes.py | 88 ++++++++++++++++++ scripts/upload-asset.py | 26 ++++++ scripts/website | 38 ++++++++ scripts/weeknotes | 92 ------------------- 12 files changed, 275 insertions(+), 92 deletions(-) create mode 100755 scripts/convert_page.py rename scripts/{website => erynwells_me}/__init__.py (100%) create mode 100644 scripts/erynwells_me/blog.py rename scripts/{website => erynwells_me}/dates.py (100%) rename scripts/{website => erynwells_me}/metadata.py (100%) rename scripts/{website => erynwells_me}/paths.py (100%) create mode 100644 scripts/erynwells_me/scripting/__init__.py create mode 100644 scripts/erynwells_me/scripting/command.py create mode 100644 scripts/erynwells_me/weeknotes.py create mode 100644 scripts/upload-asset.py create mode 100755 scripts/website delete mode 100755 scripts/weeknotes diff --git a/scripts/convert_page.py b/scripts/convert_page.py new file mode 100755 index 0000000..452438a --- /dev/null +++ b/scripts/convert_page.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +# Eryn Wells + +''' +Convert a Hugo content page from a single page to a bundle and vice versa. +''' + +import argparse +import os.path + +def parse_args(argv, *a, **kw): + parser = argparse.ArgumentParser(*a, **kw) + parser.add_argument('page_path', metavar='PATH', help='Path to page or bundle to convert') + args = parser.parse_args(argv) + return args + +def path_is_page(path): + if not os.path.isfile(path): + return False + + _, extension = os.path.splitext(path) + if extension not in ['.md']: + return False + + return True + +def path_is_page_bundle(path): + if not os.path.isdir(path): + return False + + contents = os.listdir(path) + if 'index.md' not in contents: + return False + + return True + +def can_convert_page_bundle_to_page(path): + contents = os.listdir(path) + if len(contents) != 1: + return False + + if 'index.md' not in contents: + return False + + return True + +def convert_page_to_bundle(path) -> bool: + dirname = os.path.dirname(path) + name, extension = os.path.splitext(path) + + bundle_path = os.path.join(dirname, name) + index_path = os.path.join(dirname, 'index' + extension) + + try: + print(f'Creating {bundle_path} ... ', end='') + os.mkdir(bundle_path) + print('OK') + except FileExistsError as e: + print(f'\nCannot create {bundle_path}: directory already exists') + return False + + try: + print(f'Moving {path} -> {index_path} ... ', end='') + os.rename(path, index_path) + print('OK') + except: + print(f'\nCannot move page file to new index path {index_path}') + + return True + +def main(argv): + args = parse_args(argv[1:], prog=argv[0]) + + page_path = args.page_path + if path_is_page(page_path): + print(f'{page_path} is a page') + convert_page_to_bundle(page_path) + elif path_is_page_bundle(page_path): + print(f'{page_path} is a page bundle') + if not can_convert_page_bundle_to_page(page_path): + print(f'Cannot convert bundle {page_path} to page') + +if __name__ == '__main__': + import sys + result = main(sys.argv) + sys.exit(0 if not result else result) diff --git a/scripts/website/__init__.py b/scripts/erynwells_me/__init__.py similarity index 100% rename from scripts/website/__init__.py rename to scripts/erynwells_me/__init__.py diff --git a/scripts/erynwells_me/blog.py b/scripts/erynwells_me/blog.py new file mode 100644 index 0000000..6f5b0dd --- /dev/null +++ b/scripts/erynwells_me/blog.py @@ -0,0 +1,12 @@ +# Eryn Wells + +import datetime +import os.path as osp +from typing import Optional +from . import paths + + +def post_path(name, *, year: Optional[int] = None): + if not year: + year = datetime.date.today().year + return osp.join(paths.content_path(), 'blog', str(year), name) diff --git a/scripts/website/dates.py b/scripts/erynwells_me/dates.py similarity index 100% rename from scripts/website/dates.py rename to scripts/erynwells_me/dates.py diff --git a/scripts/website/metadata.py b/scripts/erynwells_me/metadata.py similarity index 100% rename from scripts/website/metadata.py rename to scripts/erynwells_me/metadata.py diff --git a/scripts/website/paths.py b/scripts/erynwells_me/paths.py similarity index 100% rename from scripts/website/paths.py rename to scripts/erynwells_me/paths.py diff --git a/scripts/erynwells_me/scripting/__init__.py b/scripts/erynwells_me/scripting/__init__.py new file mode 100644 index 0000000..fab6e56 --- /dev/null +++ b/scripts/erynwells_me/scripting/__init__.py @@ -0,0 +1 @@ +from .command import Command diff --git a/scripts/erynwells_me/scripting/command.py b/scripts/erynwells_me/scripting/command.py new file mode 100644 index 0000000..8014cb5 --- /dev/null +++ b/scripts/erynwells_me/scripting/command.py @@ -0,0 +1,24 @@ +# Eryn Wells + +import argparse +import re + +class Command: + def __init__(self) -> None: + pass + + @property + def name(self) -> str: + class_name = self.__class__.__name__ + if class_name.endswith('Command'): + trimmed_class_name = class_name.removesuffix('Command') + hyphenated_name = re.sub(r'\B([A-Z])', r'-\1', trimmed_class_name) + return hyphenated_name.lower() + return class_name.lower() + + @property + def help(self) -> str: + return '' + + def add_arguments(self, _: argparse.ArgumentParser): + raise NotImplementedError() diff --git a/scripts/erynwells_me/weeknotes.py b/scripts/erynwells_me/weeknotes.py new file mode 100644 index 0000000..aeaecbd --- /dev/null +++ b/scripts/erynwells_me/weeknotes.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3.12 +# Eryn Wells + +import argparse +import datetime +import os +import subprocess +import sys +from typing import Optional +from . import blog +from .dates import next_sunday_noon +from .scripting import Command + + +class WeeknotesCommand(Command): + @staticmethod + def weeknotes_filename(year: str, week: str) -> str: + return f'weeknotes-{year}w{week}.md' + + @staticmethod + def weeknotes_path(*, week_number: Optional[int] = None): + if week_number: + year = datetime.datetime.now().year + week_number_str = str(week_number) + else: + next_sunday = next_sunday_noon() + year = next_sunday.year + week_number_str = next_sunday.strftime('%V') + + return blog.post_path(WeeknotesCommand.weeknotes_filename( + str(year), + week_number_str + )) + + @property + def help(self) -> str: + return 'Work with weeknotes posts' + + def add_arguments(self, parser: argparse.ArgumentParser): + commands = parser.add_subparsers(title='Weeknotes', required=True) + + convert_command = commands.add_parser( + 'convert', + help='Convert a post from a single file to a page bundle and vice versa' + ) + convert_command.set_defaults(handler=self.handle_convert_command) + + edit_command = commands.add_parser( + 'edit', + help="Edit the current week's weeknotes post" + ) + edit_command.add_argument( + '--editor', '-e', + default=os.environ.get('EDITOR', 'nvim'), + ) + edit_command.add_argument('--week', '-w') + edit_command.set_defaults(handler=self.handle_edit_command) + + show_command = commands.add_parser( + 'show', + aliases=['print'], + help="Print a path to the current week's weeknotes post", + ) + show_command.add_argument('--week', '-w') + show_command.add_argument('--date', action='store_true') + show_command.set_defaults(handler=self.handle_show_command) + + def handle_convert_command(self, _: argparse.Namespace): + raise NotImplementedError() + + def handle_edit_command(self, args: argparse.Namespace): + path = WeeknotesCommand.weeknotes_path(week_number=args.week) + subprocess.run( + f'{args.editor} "{path}"', + stdin=sys.stdin, + stdout=sys.stdout, + stderr=sys.stderr, + env=os.environ, + shell=True, + ) + + def handle_show_command(self, args: argparse.Namespace): + if args.date: + if args.week: + raise NotImplementedError('Cannot print date with specified week number') + print(next_sunday_noon().isoformat()) + else: + print(WeeknotesCommand.weeknotes_path(week_number=args.week)) diff --git a/scripts/upload-asset.py b/scripts/upload-asset.py new file mode 100644 index 0000000..bee0168 --- /dev/null +++ b/scripts/upload-asset.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# Eryn Wells + +''' +Upload a static asset to a server. +''' + +import argparse +import subprocess + +def parse_args(argv, *a, **kw): + parser = argparse.ArgumentParser(*a, **kw) + parser.add_argument('file', metavar='FILE', nargs='+') + parser.add_argument('destination', metavar='DEST') + args = parser.parse_args(argv) + return args + +def main(argv): + args = parse_args(argv[1:], prog=argv[0]) + + + +if __name__ == '__main__': + import sys + result = main(sys.argv) + sys.exit(0 if not result else result) diff --git a/scripts/website b/scripts/website new file mode 100755 index 0000000..09a7151 --- /dev/null +++ b/scripts/website @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# Eryn Wells + +''' +A Python interface to my personal website, Erynwells.me. +''' + +import argparse +from typing import List +from erynwells_me.scripting import Command +from erynwells_me.weeknotes import WeeknotesCommand + + +COMMANDS: List[Command] = [ + WeeknotesCommand(), +] + + +def parse_args(commands: List[Command], argv, *a, **kw): + parser = argparse.ArgumentParser(*a, **kw) + + subcommands = parser.add_subparsers(title='Subcommands', required=True) + for command in commands: + subcommand_parser = subcommands.add_parser(command.name, help=command.help) + command.add_arguments(subcommand_parser) + + args = parser.parse_args(argv) + return args + + +def main(argv): + args = parse_args(COMMANDS, argv[1:], prog=argv[0]) + args.handler(args) + + +if __name__ == '__main__': + import sys + sys.exit(main(sys.argv)) diff --git a/scripts/weeknotes b/scripts/weeknotes deleted file mode 100755 index bb21523..0000000 --- a/scripts/weeknotes +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env python3.12 -# Eryn Wells - -import argparse -import datetime -import os.path -import subprocess -from typing import Optional -from website.dates import next_sunday_noon -from website.paths import content_path - - -def weeknotes_path(*, week_number: Optional[int] = None): - if week_number: - year = datetime.datetime.now().year - week_number_str = str(week_number) - else: - next_sunday = next_sunday_noon() - year = next_sunday.year - week_number_str = next_sunday.strftime('%V') - - weeknotes_filename = f'weeknotes-{year}w{week_number_str}.md' - return os.path.join(content_path(), 'blog', str(year), weeknotes_filename) - - -def parse_args(argv, *a, **kw): - parser = argparse.ArgumentParser(*a, **kw) - commands = parser.add_subparsers(title='Commands', required=True) - - convert_command = commands.add_parser( - 'convert', - help='Convert a post from a single file to a page bundle and vice versa' - ) - convert_command.set_defaults(handler=handle_convert_command) - - edit_command = commands.add_parser( - 'edit', - help="Edit the current week's weeknotes post" - ) - edit_command.add_argument( - '--editor', '-e', - default=os.environ.get('EDITOR', 'nvim'), - ) - edit_command.add_argument('--week', '-w') - edit_command.set_defaults(handler=handle_edit_command) - - show_command = commands.add_parser( - 'show', - aliases=['print'], - help="Print a path to the current week's weeknotes post", - ) - show_command.add_argument('--week', '-w') - show_command.add_argument('--date', action='store_true') - show_command.set_defaults(handler=handle_show_command) - - args = parser.parse_args(argv) - return args - - -def handle_convert_command(args): - raise NotImplementedError() - - -def handle_edit_command(args): - path = weeknotes_path(week_number=args.week) - subprocess.run( - f'{args.editor} "{path}"', - stdin=sys.stdin, - stdout=sys.stdout, - stderr=sys.stderr, - env=os.environ, - shell=True, - ) - - -def handle_show_command(args): - if args.date: - if args.week: - raise NotImplementedError('Cannot print date with specified week number') - print(next_sunday_noon().isoformat()) - else: - print(weeknotes_path(week_number=args.week)) - - -def main(argv): - args = parse_args(argv[1:], prog=argv[0]) - args.handler(args) - -if __name__ == '__main__': - import sys - result = main(sys.argv) - sys.exit(0 if not result else result)