From 8b38b3e86db0fd67d7bdf47cde9d9ddabca0d7e1 Mon Sep 17 00:00:00 2001 From: Jim Shepich III Date: Wed, 28 Jan 2026 16:56:03 -0500 Subject: [PATCH] Forgot to stage changes --- .gitignore | 7 +- __pycache__/main.cpython-312.pyc | Bin 0 -> 3854 bytes agenda.md | 21 ---- info.php | 5 - main.py | 79 +++++++++++++++ requirements.txt | 3 + templates/default.html | 23 +++++ templates/partials/blog_article.html | 0 templates/partials/default_css.html | 4 + templates/partials/footer.html | 3 + templates/partials/header.html | 3 + templates/partials/nav.html | 4 + testbench.ipynb | 142 +++++++++++++++++++++++++++ 13 files changed, 267 insertions(+), 27 deletions(-) create mode 100644 __pycache__/main.cpython-312.pyc delete mode 100644 agenda.md delete mode 100644 info.php create mode 100644 main.py create mode 100644 requirements.txt create mode 100644 templates/default.html create mode 100644 templates/partials/blog_article.html create mode 100644 templates/partials/default_css.html create mode 100644 templates/partials/footer.html create mode 100644 templates/partials/header.html create mode 100644 templates/partials/nav.html create mode 100644 testbench.ipynb diff --git a/.gitignore b/.gitignore index 78ecffe..87a67e9 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,6 @@ -shepich resume.pdf \ No newline at end of file +shepich resume.pdf +**/.venv +**/.env +tmp +build +dist \ No newline at end of file diff --git a/__pycache__/main.cpython-312.pyc b/__pycache__/main.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..590ae1839a37bb4c5921b88943450ad4bf9aedd1 GIT binary patch literal 3854 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 literal 0 HcmV?d00001 diff --git a/agenda.md b/agenda.md deleted file mode 100644 index c543ab3..0000000 --- a/agenda.md +++ /dev/null @@ -1,21 +0,0 @@ -## Pages -- Home -- About -- CV - - Everything in as much detail as possible -- Projects - - E&E - - M|Chroma - - SI-Formatter -- Socials (maybe better as a footer) - - Instagram - - Facebook - - LinkedIn - - GitHub - - MAL -- Bookmarks (?) - -## Reference Material -- https://www.tomscott.com/ -- http://vihart.com/ -- https://www.singingbanana.com/ diff --git a/info.php b/info.php deleted file mode 100644 index 47b3cb9..0000000 --- a/info.php +++ /dev/null @@ -1,5 +0,0 @@ - \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..cb46608 --- /dev/null +++ b/main.py @@ -0,0 +1,79 @@ +import os +import subprocess +import markdown +import yaml +from datetime import datetime + +def filepath_or_string(s: str) -> str: + '''Loads the contents of a string if it is a filepath, otherwise returns the string.''' + if os.path.isfile(s): + with open(s, 'r') as f: + return f.read() + else: + return s + + +def load_markdown(md: str) -> tuple[dict, str]: + '''Loads a Markdown file into a (metadata: dict, content: str) pair.''' + + # Load the file contents if a filepath is specified, and strip document delimiters ('---'). + md = filepath_or_string(md).strip().strip('---').strip() + + # If there is no `---` delimiter, then the article has no metadata. + if '---' not in md.strip('---'): + return {}, md + + # Split the metadata from the contents. + [raw_metadata, raw_article] = md.split('---') + + # Use YAML to parse the metadata. + metadata = yaml.safe_load(raw_metadata) + + # Convert the contents to a HTML string. + content = markdown.markdown(raw_article) + + return metadata, content + + +def format_html_template(template: str, **kwargs) -> str: + '''Interpolates variables specified as keyword arguments + into the given HTML template.''' + + # Load the template if a filepath is given. + template = filepath_or_string(template) + + # Interpolate the kwargs into the HTML template. + html = template.format(**kwargs) + + # Return the formatted HTML. + return html + + +REPOS = [ + 'ssh://gitea/jim/resume.git', + 'ssh://gitea/jim/dogma-jimfinium.git' +] +run = lambda cmd: subprocess.run(cmd.split(' '), stdout = subprocess.PIPE, stderr = subprocess.PIPE) +def update_git_repos(repos: list) -> None: + '''Pulls updates to repos in the build directory, or clones them if they don't exist.''' + for repo in repos: + local_path = 'build/'+repo.split('/')[-1].strip('.git') + print(local_path) + if os.path.exists(f'{local_path}/.git'): + run(f'git -C {local_path} pull origin') + else: + run(f'git clone {repo} {local_path}') + + +def load_partials() -> dict: + """Loads partial templates from the templates/partials directory.""" + partials = {} + for filename in os.listdir('templates/partials'): + with open(f'templates/partials/{filename}') as partial_file: + partial_template = partial_file.read() + + partials[f'partials__{os.path.splitext(filename)[0]}'] = format_html_template( + partial_template, + current_year = datetime.now().year + ) + return partials \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..735541d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +ipykernel +markdown +pyyaml \ No newline at end of file diff --git a/templates/default.html b/templates/default.html new file mode 100644 index 0000000..3cc4d24 --- /dev/null +++ b/templates/default.html @@ -0,0 +1,23 @@ + + + + + + {partials__default_css} + {partials__header} + {partials__nav} + + + + +
+

{metadata__title}

+
By
+
First published: +
Last modified: +

+ {content} +
+ {partials__footer} + \ No newline at end of file diff --git a/templates/partials/blog_article.html b/templates/partials/blog_article.html new file mode 100644 index 0000000..e69de29 diff --git a/templates/partials/default_css.html b/templates/partials/default_css.html new file mode 100644 index 0000000..0bc0f4c --- /dev/null +++ b/templates/partials/default_css.html @@ -0,0 +1,4 @@ + + + + diff --git a/templates/partials/footer.html b/templates/partials/footer.html new file mode 100644 index 0000000..4764231 --- /dev/null +++ b/templates/partials/footer.html @@ -0,0 +1,3 @@ +
+
Copyright © 2021-{current_year} Jim Shepich +
\ No newline at end of file diff --git a/templates/partials/header.html b/templates/partials/header.html new file mode 100644 index 0000000..45c5c32 --- /dev/null +++ b/templates/partials/header.html @@ -0,0 +1,3 @@ +
+ Jimlab +
\ No newline at end of file diff --git a/templates/partials/nav.html b/templates/partials/nav.html new file mode 100644 index 0000000..9e34918 --- /dev/null +++ b/templates/partials/nav.html @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/testbench.ipynb b/testbench.ipynb new file mode 100644 index 0000000..9269cad --- /dev/null +++ b/testbench.ipynb @@ -0,0 +1,142 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "dda60de8", + "metadata": {}, + "source": [ + "## Roadmap\n", + "\n", + "- [x] Load markdown\n", + "- [] Determine static website structure\n", + " - Where to put assets for subsites like dogma jimfinium\n", + " - How to otherwise organize pages\n", + "- [] Resolve markdown links\n", + "- [] Consider separating article templates and overall page templates\n", + "- [] RSS feed\n", + "\n", + "\n", + "WEBROOT\n", + "- assets\n", + "- main pages\n", + "- resume\n", + "- dogma-jimfinium/\n", + " - assets/\n", + " - pages" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "207d2510", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import markdown\n", + "import yaml\n", + "import subprocess\n", + "\n", + "\n", + "from datetime import datetime\n", + "from main import *" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "d2361c42", + "metadata": {}, + "outputs": [], + "source": [ + "metadata, content = load_markdown('tmp/dogma-jimfinium/blowouts.md')\n", + "content = content.replace('src=\"assets', 'src=\"../tmp/dogma-jimfinium/assets')" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "ed7b3b2f", + "metadata": {}, + "outputs": [], + "source": [ + "PARTIALS = load_partials()\n", + "html = format_html_template('templates/default.html', content = content, **{'metadata__'+k:v for k,v in metadata.items()}, **PARTIALS)\n", + "with open('dist/home.html', 'w') as f:\n", + " f.write(html)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b8c87620", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "57383c24", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2025-12-01\n" + ] + }, + { + "data": { + "text/plain": [ + "CompletedProcess(args=['cp', 'build/resume/2025-12-01/shepich_resume.pdf', 'dist/shepich_resume.pdf'], returncode=0, stdout=b'', stderr=b'')" + ] + }, + "execution_count": 170, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def get_latest_resume():\n", + " max_date = '0000-00-00'\n", + " for resume_folder in os.listdir('build/resume'):\n", + " try:\n", + " datetime.strptime(resume_folder,'%Y-%m-%d')\n", + " except Exception:\n", + " continue\n", + " \n", + " if resume_folder > max_date:\n", + " max_date = resume_folder\n", + " \n", + "\n", + " print(max_date)\n", + " # print(max_date.strftime('%Y-%m-%d'))\n", + " \n", + " run(f'cp build/resume/{max_date}/shepich_resume.pdf dist/shepich_resume.pdf')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}