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 0000000..590ae18 Binary files /dev/null and b/__pycache__/main.cpython-312.pyc differ 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 @@ + \ 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 +}