diff --git a/config.yaml b/config.yaml
index 8065da7..01715d9 100644
--- a/config.yaml
+++ b/config.yaml
@@ -1,12 +1,8 @@
author: Jim Shepich III
-templates_folder: ./templates
-site_defaults:
- templates:
- partials: ./templates/partials
- components: ./templates/components
- pages: ./templates/pages
+templates_folder: ./site/templates
sites:
main:
+ title: Jimlab
base_url: http://localhost:8000
web_root: ./dist
build_cache: ./site
@@ -14,14 +10,20 @@ sites:
- /assets
articles:
- ./pages/*.md
+ template_selections:
+ article: templates.components.simple_article
+
resume:
+ title: Resume
base_url: http://localhost:8000
web_root: ./dist
git_repo: ssh://gitea/jim/resume.git
build_cache: ./build/resume
assets:
- 'shepich_resume.pdf'
+
dogma_jimfinium:
+ title: Dogma Jimfinium
base_url: http://localhost:8080/dogma-jimfinium
git_repo: ssh://gitea/jim/dogma-jimfinium.git
build_cache: ./build/dogma-jimfinium
diff --git a/jimsite/__init__.py b/jimsite/__init__.py
index 8572ae2..b07106c 100644
--- a/jimsite/__init__.py
+++ b/jimsite/__init__.py
@@ -10,11 +10,11 @@ from typing import Optional
from datetime import datetime, date
from dotmap import DotMap
-from .common import filepath_or_string, GlobalVars, SiteConfig
-from .templating import format_html_template, map_templates
+from .common import filepath_or_string, GlobalVars, SiteConfig, dotmap_access
+from .templating import format_html_template, map_templates, TemplateSelections
from .assets import pull_git_repo, copy_assets
from .articles import ArticleMetadata, load_markdown, build_articles, build_index
-
+from .blog import build_blog_archive, build_rss_feed
@@ -25,6 +25,10 @@ from .articles import ArticleMetadata, load_markdown, build_articles, build_inde
def build_site(site: SiteConfig, templates: DotMap):
+ # Do not build a site marked as unpublished.
+ if not site.published:
+ return None
+
# Initialize the build cache and web root, in case they do not exist.
os.makedirs(site.build_cache, exist_ok = True)
os.makedirs(site.web_root, exist_ok = True)
@@ -39,8 +43,15 @@ def build_site(site: SiteConfig, templates: DotMap):
# Load the site's articles into an index.
index = build_index(site)
+ # Determine which templates are to be used for explicit applications, e.g.
+ # the tag component.
+ template_selections = TemplateSelections(site, templates)
+
# Generate HTML pages for the articles.
- build_articles(site, index, templates)
+ build_articles(site, index, templates, template_selections)
+
+ if len(site.articles or []):
+ build_blog_archive(site, index, template_selections, templates = templates)
def main():
diff --git a/jimsite/articles.py b/jimsite/articles.py
index 01ef1de..9ee38d3 100644
--- a/jimsite/articles.py
+++ b/jimsite/articles.py
@@ -8,7 +8,7 @@ from dotmap import DotMap
from datetime import date
from .common import filepath_or_string, SiteConfig
-from .templating import format_html_template
+from .templating import format_html_template, TemplateSelections
class ArticleMetadata(pydantic.BaseModel):
title: str
@@ -70,30 +70,39 @@ def build_index(site: SiteConfig) -> dict:
return index
-def format_article_tags(tags: list[str], template = 'templates/components/blog_tag.html') -> list[str]:
+def format_article_tags(tags: list[str], tag_template, **kwargs) -> list[str]:
'''Generates HTML article tag components from a list of tag names.'''
return [
- format_html_template(template, tag_name = t) for t in tags
+ format_html_template(tag_template, tag_name = t, **kwargs) for t in tags
]
-def build_articles(site: SiteConfig, index: dict[str, tuple[ArticleMetadata, str]], templates: DotMap):
+def build_articles(
+ site: SiteConfig,
+ index: dict[str, tuple[ArticleMetadata, str]],
+ templates: DotMap,
+ template_selections: TemplateSelections
+ ):
'''Generates HTML files for all of a given site's Markdown articles
by interpolating the contents and metadata into the HTML templates.'''
for filestem, (metadata, content) in index.items():
article = format_html_template(
- templates.components.blog_article,
+ template_selections['article'],
content = content,
- blog_tags = ' '.join(format_article_tags(metadata.tags)),
+ blog_tags = ' '.join(format_article_tags(
+ metadata.tags, template_selections['tag'], site = site
+ )),
metadata = metadata,
- templates = templates
+ templates = templates,
+ site = site
)
page = format_html_template(
- templates.pages.default,
+ template_selections['page'],
content = article,
- templates = templates
+ templates = templates,
+ site = site
)
diff --git a/jimsite/blog.py b/jimsite/blog.py
index 806845b..bbefe98 100644
--- a/jimsite/blog.py
+++ b/jimsite/blog.py
@@ -4,14 +4,13 @@ import datetime
from .common import SiteConfig
from .articles import ArticleMetadata, format_article_tags
-from .templating import format_html_template
-
+from .templating import format_html_template, TemplateSelections
def build_blog_archive(
+ site: SiteConfig,
index: dict[str, tuple[str, str]],
- page_template = 'templates/pages/default.html',
- li_template = 'templates/components/blog_archive_li.html',
+ template_selections: TemplateSelections,
**kwargs
) -> str:
'''Converts an index, formatted as filestem: (metadata, contents) dict,
@@ -22,27 +21,50 @@ def build_blog_archive(
'''
# Add each article as a list item to an unordered list.
- archive_html_content = '
'
+ archive_html_list = '
'
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,
+ archive_html_list += format_html_template(
+ template_selections['archive_li'],
article_filestem = article,
- blog_tags = ' '.join(format_article_tags(metadata.tags)),
- metadata = metadata
-
+ blog_tags = ' '.join(format_article_tags(metadata.tags, template_selections['tag'], site = site)),
+ metadata = metadata,
+ site = site,
+ **kwargs
)
- archive_html_content +='
'
+ archive_html_list +='
'
+
+ # Generate the archive article.
+ archive_html_article = format_html_template(
+ template_selections['archive_article'],
+ content = archive_html_list,
+ site = site,
+ **kwargs
+ )
# Interpolate the article into the overall page template.
archive_html_page = format_html_template(
- page_template,
- content = archive_html_content,
+ template_selections['page'],
+ content = archive_html_article,
+ site = site,
**kwargs
)
- return archive_html_page
+ with open(f'{site.web_root.rstrip('/')}/archive.html', 'w') as f:
+ f.write(archive_html_page)
+
+
+def build_tag_reference(index: dict[str, tuple[str, str]]):
+ tag_index = {}
+ for article, (metadata, content) in index.items():
+ for tag in metadata.tags:
+ tag_index[tag] = (tag_index.get(tag,[])) + [article]
+
+ for tag, articles in tag_index.items():
+ pass
+
+
# TODO: Finish
def build_rss_feed(site: SiteConfig, index: dict[str, tuple[ArticleMetadata, str]]):
diff --git a/jimsite/common.py b/jimsite/common.py
index 6f129de..9bc82ed 100644
--- a/jimsite/common.py
+++ b/jimsite/common.py
@@ -1,6 +1,8 @@
import os
+import inspect
import subprocess
import pydantic
+from dotmap import DotMap
from typing import Optional
from datetime import date, datetime
@@ -28,6 +30,42 @@ class SiteConfig(pydantic.BaseModel):
base_url: str
web_root: str
build_cache: str
+ title: str
+ published: Optional[bool] = True
git_repo: Optional[str] = None
assets: Optional[list] = None
- articles: Optional[list] = None
\ No newline at end of file
+ articles: Optional[list] = None
+ template_selections: Optional[dict] = {}
+
+
+def get_var_names(var):
+ '''Gets the name(s) of an input variable.
+ From https://stackoverflow.com/questions/18425225/getting-the-name-of-a-variable-as-a-string.'''
+ callers_local_vars = inspect.currentframe().f_back.f_back.f_locals.items()
+ return [var_name for var_name, var_val in callers_local_vars if var_val is var]
+
+
+def dotmap_access(d: DotMap, s: str):
+ '''Facilitates nested subscripting into a DotMap using a
+ string containing period-delimited keys.
+
+ # Example
+ ```python
+ dd = DotMap({'a':{'b':{'c':42}}})
+ dotmap_access(dd, 'dd.a.b.c')
+ >>> 42
+ ```
+ '''
+
+ keys = s.split('.')
+
+ # If the string starts with the dotmap's name, ignore it.
+ if keys[0] in get_var_names(d):
+ keys.pop(0)
+
+ # Iteratively subscript into the nested dotmap until it's done.
+ result = d
+ for k in keys:
+ result = result[k]
+
+ return result
diff --git a/jimsite/templating.py b/jimsite/templating.py
index f05c332..3a1dfd9 100644
--- a/jimsite/templating.py
+++ b/jimsite/templating.py
@@ -1,7 +1,7 @@
import os
import re
from dotmap import DotMap
-from .common import filepath_or_string, GlobalVars
+from .common import filepath_or_string, GlobalVars, SiteConfig, dotmap_access
def extract_placeholders(s: str) -> set:
@@ -120,4 +120,33 @@ def map_templates(dir: str, parent = '') -> DotMap:
output[filestem] = html
- return DotMap(output)
\ No newline at end of file
+ return DotMap(output)
+
+
+# TODO: Come up with a more consistent naming scheme for defaults.
+
+class TemplateSelections:
+ '''An interface for retrieving templates that are explicitly used in the
+ compiler, such as the article and tag components, as well as the default page.
+
+ Relies on `SiteConfig.template_selection` for custom overrides, falling back on
+ `config[template_default_selections]`, and finally on defaults hard-coded into
+ the definition of this class.
+ '''
+ def __init__(self, site: SiteConfig, templates: DotMap, config: dict = None):
+ self.site = site
+ self.templates = templates
+ self.defaults = dict(
+ tag = 'templates.components.blog_tag',
+ article = 'templates.components.blog_article',
+ page = 'templates.pages.default',
+ archive_li = 'templates.components.blog_archive_li',
+ archive_article = 'templates.components.blog_archive'
+ ) | (config or {}).get('template_default_selections', {})
+
+ def __getitem__(self, key: str) -> str:
+
+ # Templates variable must be named `templates` for dotmap_access to work correctly.
+ templates = self.templates
+ return dotmap_access(templates, self.site.template_selections.get(key, self.defaults[key]))
+
diff --git a/site/assets/img/favicon.svg b/site/assets/img/favicon.svg
new file mode 100644
index 0000000..e33690b
--- /dev/null
+++ b/site/assets/img/favicon.svg
@@ -0,0 +1,129 @@
+
+
+
+
diff --git a/site/templates/blog_post.html b/site/templates/blog_post.html
deleted file mode 100644
index 6322b55..0000000
--- a/site/templates/blog_post.html
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
- {templates.partials.default_css}
- {templates.partials.header}
- {templates.partials.nav}
-
-
-
-
-
{metadata.title}
-
-
By Jim Shepich III
- First published:
- Last modified:
-
- {content}
-
-
- {templates.partials.footer}
-
\ No newline at end of file
diff --git a/site/templates/components/blog_archive.html b/site/templates/components/blog_archive.html
new file mode 100644
index 0000000..0c2033b
--- /dev/null
+++ b/site/templates/components/blog_archive.html
@@ -0,0 +1,5 @@
+
+
{site.title} Archive
+
+ {content}
+
\ No newline at end of file
diff --git a/site/templates/components/blog_tag.html b/site/templates/components/blog_tag.html
index 1a77d3b..8f1ebaa 100644
--- a/site/templates/components/blog_tag.html
+++ b/site/templates/components/blog_tag.html
@@ -1 +1 @@
-{tag_name}
\ No newline at end of file
+{tag_name}
\ No newline at end of file
diff --git a/site/templates/components/simple_article.html b/site/templates/components/simple_article.html
new file mode 100644
index 0000000..0b33bfe
--- /dev/null
+++ b/site/templates/components/simple_article.html
@@ -0,0 +1,5 @@
+
+