From d56569668d6709610168e4269b7d0368745aeb26 Mon Sep 17 00:00:00 2001
From: Jim Shepich III
Date: Fri, 30 Jan 2026 10:02:09 -0500
Subject: [PATCH] Working on config and Pydantic types
---
.gitignore | 3 +-
__pycache__/main.cpython-312.pyc | Bin 3854 -> 0 bytes
config.yaml | 30 ++++
main.py | 71 +++++++-
pages/home.html | 8 -
pages/home.md | 10 ++
requirements.txt | 3 +-
templates/blog_post.html | 6 +-
templates/components/blog_archive_li.html | 2 +-
templates/components/blog_article.html | 8 +-
templates/pages/default.html | 14 ++
testbench.ipynb | 203 +++++++++++++++++-----
12 files changed, 295 insertions(+), 63 deletions(-)
delete mode 100644 __pycache__/main.cpython-312.pyc
delete mode 100644 pages/home.html
create mode 100644 pages/home.md
create mode 100644 templates/pages/default.html
diff --git a/.gitignore b/.gitignore
index 87a67e9..d9d0210 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,5 @@ shepich resume.pdf
**/.env
tmp
build
-dist
\ No newline at end of file
+dist
+**/__pycache__
\ No newline at end of file
diff --git a/__pycache__/main.cpython-312.pyc b/__pycache__/main.cpython-312.pyc
deleted file mode 100644
index 590ae1839a37bb4c5921b88943450ad4bf9aedd1..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 3854
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
diff --git a/config.yaml b/config.yaml
index e69de29..2ed6b33 100644
--- a/config.yaml
+++ b/config.yaml
@@ -0,0 +1,30 @@
+author: Jim Shepich III
+site_defaults:
+ base_url: http://localhost:8000
+ web_root: ./dist
+ templates:
+ partials: ./templates/partials
+ components: ./templates/components
+ pages: ./templates/pages
+sites:
+ main:
+ build_cache: ./build
+ assets:
+ - ./assets
+ articles:
+ - ./pages/*.md
+ resume:
+ git_repo: ssh://gitea/jim/resume.git
+ build_cache: ./build/resume
+ assets:
+ - '{build_cache}/shepich_resume.pdf'
+ dogma_jimfinium:
+ base_url: http://localhost:8080/dogma-jimfinium
+ git_repo: ssh://gitea/jim/dogma-jimfinium.git
+ build_cache: ./build/dogma-jimfinium
+ web_root: ./dist/dogma-jimfinium
+ assets:
+ - '{build_cache}/assets'
+ articles:
+ - '{build_cache}/*.md'
+
\ No newline at end of file
diff --git a/main.py b/main.py
index 23ffae5..3fca3d1 100644
--- a/main.py
+++ b/main.py
@@ -2,7 +2,9 @@ import os
import subprocess
import markdown
import yaml
-from datetime import datetime
+import pydantic
+from typing import Optional
+from datetime import datetime, date
def filepath_or_string(s: str) -> str:
'''Loads the contents of a string if it is a filepath, otherwise returns the string.'''
@@ -13,15 +15,37 @@ def filepath_or_string(s: str) -> str:
return s
-def load_markdown(md: str) -> tuple[dict, str]:
- '''Loads a Markdown file into a (metadata: dict, content: str) pair.'''
+with open('config.yaml', 'r') as config_file:
+ config = yaml.safe_load(config_file.read())
+
+class SiteConfig(pydantic.BaseModel):
+ base_url: Optional[str] = config['site_defaults'].get('base_url')
+ git_repo: Optional[str] = config['site_defaults'].get('git_repo')
+ build_cache: Optional[str] = config['site_defaults'].get('build_cache')
+ assets: Optional[list] = config['site_defaults'].get('assets')
+ web_root: Optional[str] = config['site_defaults'].get('web_root')
+ articles: Optional[list] = config['site_defaults'].get('articles')
+
+class ArticleMetadata(pydantic.BaseModel):
+ title: str
+ author: Optional[str] = config.get('author')
+ date: date
+ lastmod: Optional[date]
+ published: bool
+ tags: list
+ thumbnail: Optional[str] = None
+
+
+
+def load_markdown(md: str) -> tuple[ArticleMetadata|None, str]:
+ '''Loads a Markdown file into a (metadata: ArticleMetadata, 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
+ return None, md
# Split the metadata from the contents.
[raw_metadata, raw_article] = md.split('---')
@@ -32,7 +56,7 @@ def load_markdown(md: str) -> tuple[dict, str]:
# Convert the contents to a HTML string.
content = markdown.markdown(raw_article)
- return metadata, content
+ return ArticleMetadata(**metadata), content
def format_html_template(template: str, **kwargs) -> str:
@@ -107,5 +131,42 @@ def format_blog_tags(tags: list[str], template = 'templates/components/blog_tag.
format_html_template(template, tag_name = t) for t in tags
]
+
+def build_blog_archive(
+ index: dict[str, tuple[str, str]],
+ page_template = 'templates/pages/default.html',
+ li_template = 'templates/components/blog_archive_li.html',
+ **kwargs
+ ) -> str:
+ '''Converts an index, formatted as filestem: (metadata, contents) dict,
+ into an HTML page containing the list of articles, sorted from newest to oldest.
+
+ Note: partials must be expanded into the kwargs, as they are needed to generate
+ the overall page.
+ '''
+
+ # Add each article as a list item to an unordered list.
+ archive_html_content = '
'
+ for article, (metadata, contents) in sorted(index.items(), key = lambda item: item[1][0]['date'])[::-1]:
+
+ # Generate HTML for the article (including metadata tags).
+ archive_html_content += format_html_template(
+ li_template,
+ article_filestem = article,
+ blog_tags = ' '.join(format_blog_tags(metadata.tags)),
+ metadata = metadata
+
+ )
+ archive_html_content +='
'
+
+ # Interpolate the article into the overall page template.
+ archive_html_page = format_html_template(
+ page_template,
+ content = archive_html_content,
+ **kwargs
+ )
+
+ return archive_html_page
+
if __name__ == '__main__':
pass
\ No newline at end of file
diff --git a/pages/home.html b/pages/home.html
deleted file mode 100644
index 8b05c10..0000000
--- a/pages/home.html
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
Welcome!
-
-
Welcome to my little corner of the Internet! My name is Jim Shepich (@epicshepich). I like to introduce myself as a jack-of-all-trades with a Master's in data science. My main interests include STEM, martial arts (especially jūjutsu), sci-fi and fantasy, tabletop and video gaming, cartoons and comics, and DIY, but I also love to branch out and learn new things! My dream is to become "The Most Interesting Man in the World."
-
-
This website started as a way for me to exercise and showcase my skills with vanilla HTML, JavaScript, CSS, and PHP. Now, it's just a matter of filling the site with content! My vision for this website is for it to serve as a gift to my future reincarnated self (if that's what happens) — a way to pass on the little tips and tricks, bits of wisdom, resources, and treasures I've accumulated over the course of my life (think new game plus). And if some other kindred spirits find something here that helps them on their journey, that's a bonus!
-
-
diff --git a/pages/home.md b/pages/home.md
new file mode 100644
index 0000000..588b968
--- /dev/null
+++ b/pages/home.md
@@ -0,0 +1,10 @@
+---
+title: Welcome
+date: 2026-01-30
+published: true
+tags: []
+---
+
+Welcome to my little corner of the Internet! My name is Jim Shepich (@epicshepich). I like to introduce myself as a jack-of-all-trades with a Master's in data science. My main interests include STEM, martial arts (especially jūjutsu), sci-fi and fantasy, tabletop and video gaming, cartoons and comics, and DIY, but I also love to branch out and learn new things! My dream is to become "The Most Interesting Man in the World."
+
+This website started as a way for me to exercise and showcase my skills with vanilla HTML, JavaScript, CSS, and PHP. Now, it's just a matter of filling the site with content! My vision for this website is for it to serve as a gift to my future reincarnated self (if that's what happens) — a way to pass on the little tips and tricks, bits of wisdom, resources, and treasures I've accumulated over the course of my life (think _new game plus_). And if some other kindred spirits find something here that helps them on their journey, that's a bonus!
diff --git a/requirements.txt b/requirements.txt
index 735541d..f9c7c46 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,4 @@
ipykernel
markdown
-pyyaml
\ No newline at end of file
+pyyaml
+rfeed
\ No newline at end of file
diff --git a/templates/blog_post.html b/templates/blog_post.html
index 227fdfd..3a7c584 100644
--- a/templates/blog_post.html
+++ b/templates/blog_post.html
@@ -9,11 +9,11 @@
-
{metadata__title}
+
{metadata.title}
By Jim Shepich III
- First published:
- Last modified:
+ First published:
+ Last modified: