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 @@
+
+
+
+
+