Index

joshstock.in / 4508eab

Source for serving and static templating/compiling of https://joshstock.in.

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
17315 Mar 2023 13:542435438Fix Atom/RSS feed article orderingJosh Stockin11812G

Blob @ joshstock.in / site / targets.py

application/x-python7900 bytesdownload raw
1# targets.py / Template engine for my website
2# Joshua Stockin / josh@joshstock.in / https://joshstock.in
3
4import os
5import html
6from datetime import datetime, timezone, timedelta
7from xml.dom import minidom
8
9import markdown2
10import htmlgenerator as hg
11import readtime
12import sass
13from feedgen.feed import FeedGenerator
14
15from _utils import dotdict as namespace, current_dir, load_generators, list_files
16
17# Site generation metadata
18CONTENT_DIRECTORY = os.path.join(current_dir(), "content")
19SASS_DIRECTORY = os.path.join(current_dir(), "style")
20STATIC_DIRECTORY = os.path.join(current_dir(), "static")
21
22blog_description = "Barely coherent ramblings about engineering projects, software, hardware, and other things."
23
24# Fetch generator functions
25GENERATORS_MODULE = "generators"
26GENERATORS = [
27 "head.head",
28 "header",
29 "footer",
30 "blog.article",
31 "blog.index",
32 "blog.listing",
33]
34
35generate = load_generators(GENERATORS_MODULE, GENERATORS)
36
37sitemap_root = minidom.Document()
38sitemap_urlset = sitemap_root.createElementNS("http://www.sitemap.org/schemas/sitemap/0.9", "urlset")
39sitemap_urlset.setAttribute("xmlns", sitemap_urlset.namespaceURI)
40sitemap_root.appendChild(sitemap_urlset)
41
42site_footer = generate("footer")
43
44def add_sitemap_url(url):
45 url_obj = sitemap_root.createElement("url")
46 loc_obj = sitemap_root.createElement("loc")
47 loc_obj.appendChild(sitemap_root.createTextNode(url))
48 url_obj.appendChild(loc_obj)
49 sitemap_urlset.appendChild(url_obj)
50
51
52# Site template implementation; returns dict {filename: data}
53def template() -> {str: str}:
54 files = {}
55
56 articles_list = []
57 fg = FeedGenerator()
58 fg.id("https://joshstock.in/blog")
59 fg.title("Blog - Josh Stockin")
60 fg.author({"name": "Josh Stockin", "email": "josh@joshstock.in", "uri": "https://joshstock.in"})
61 fg.link(href="https://joshstock.in/blog", rel="alternate")
62 fg.subtitle(blog_description)
63 fg.link(href="https://joshstock.in/atom", rel="self")
64 fg.language("en")
65
66 for content_file in list_files(CONTENT_DIRECTORY, ".md"):
67 f = open(content_file, "r")
68 data = f.read()
69 f.close()
70
71 content_html = markdown2.markdown(
72 data,
73 safe_mode=False,
74 extras=[
75 "code-friendly",
76 "cuddled-lists",
77 "fenced-code-blocks",
78 "footnotes",
79 "header-ids",
80 "metadata",
81 "strike",
82 "tables",
83 "wiki-tables",
84 "tag-friendly",
85 "target-blank-links",
86 ],
87 )
88
89 page_data = namespace(content_html.metadata)
90
91 page_data.link = page_data.link or ""
92
93 if page_data.type == "website":
94 page_generator = hg.HTML(
95 generate("head.head", page_data),
96 hg.BODY(
97 *generate("header", page_data),
98 hg.DIV(
99 hg.DIV(hg.mark_safe(content_html), _class="content-body"),
100 hg.DIV(_class="vfill"),
101 site_footer,
102 _class="content-container",
103 ),
104 onscroll="scroll()",
105 ),
106 )
107 files[page_data.index] = hg.render(page_generator, {}).encode("utf-8")
108 if page_data.index != "index.html":
109 add_sitemap_url("/" + page_data.index.rsplit(".html")[0])
110 else:
111 add_sitemap_url("/")
112
113 elif page_data.type == "article": # Blog article handling
114 page_data.readtime = readtime.of_html(content_html, wpm=150)
115 page_data.thumbnail = page_data.banner_image
116 page_data.link = "/blog/" + page_data.identifier
117 page_data.links = page_data.links or {}
118 articles_list += [page_data]
119 page_data.content = content_html
120
121 page_generator = hg.HTML(
122 generate("head.head", page_data),
123 hg.BODY(
124 *generate("header", page_data),
125 hg.DIV(
126 hg.DIV(
127 *generate("blog.article", page_data), _class="content-body"
128 ),
129 hg.DIV(_class="vfill"),
130 site_footer,
131 _class="content-container",
132 ),
133 onscroll="scroll()",
134 ),
135 )
136
137 files["blog/" + page_data.identifier + ".html"] = hg.render(
138 page_generator, {}
139 ).encode("utf-8")
140
141
142 # Sort articles list by descending datestring
143 articles_list = sorted(articles_list, key=lambda x: x.datestring, reverse=True)
144
145 # Create article entries for feed generator
146 for page_data in articles_list:
147 fe = fg.add_entry()
148 fe.id("https://joshstock.in/blog/" + page_data.identifier)
149 fe.author({"name": "Josh Stockin", "email": "josh@joshstock.in", "uri": "https://joshstock.in"})
150 fe.title(page_data.title)
151 fe.summary(page_data.description + " / https://joshstock.in/blog/" + page_data.identifier)
152 datetime_pub = datetime.strptime(page_data.datestring, "%Y-%m-%d").replace(tzinfo=timezone(-timedelta(hours=6)))
153 fe.published(datetime_pub)
154 fe.updated(datetime_pub)
155 fe.link(href="https://joshstock.in/blog/" + page_data.identifier)
156
157 # Create blog index page
158 blog_page_data = namespace(
159 title="Blog",
160 banner_image="",
161 thumbnail="",
162 link="/blog",
163 description=fg.subtitle(),
164 )
165 blog_page_generator = hg.HTML(
166 generate("head.head", blog_page_data),
167 hg.BODY(
168 *generate("header", blog_page_data),
169 hg.DIV(
170 hg.DIV(
171 hg.DIV(
172 hg.H1("Blog ", hg.IMG(src="/static/svg/memo.svg", _class="inline svg")),
173 hg.P(
174 fg.subtitle(), hg.BR(),
175 hg.SPAN("[", hg.A("Atom feed", href="/atom"), "] ", style="font-size: 0.75em; color: var(--caption-color)"),
176 hg.SPAN("[", hg.A("RSS feed", href="/rss"), "]", style="font-size: 0.75em; color: var(--caption-color)")
177 )
178 ),
179 *[generate("blog.listing", x) for x in articles_list],
180 _class="content-body",
181 ),
182 hg.DIV(_class="vfill"),
183 site_footer,
184 _class="content-container",
185 ),
186 onscroll="scroll()",
187 ),
188 )
189 files["blog.html"] = hg.render(blog_page_generator, {}).encode("utf-8")
190 add_sitemap_url("/blog")
191
192 # Feeds
193 files["atom.xml"] = fg.atom_str(pretty=True)
194 fg.link(href="https://joshstock.in/rss", rel="self", replace=True)
195 files["rss.xml"] = fg.rss_str(pretty=True)
196
197 # Compile Sass stylesheets
198 for stylesheet_file in list_files(SASS_DIRECTORY, ".scss"):
199 if os.path.basename(stylesheet_file)[0] != "_":
200 files[
201 os.path.join(
202 "static",
203 "style",
204 os.path.splitext(os.path.relpath(stylesheet_file, SASS_DIRECTORY))[
205 0
206 ]
207 + ".css",
208 )
209 ] = sass.compile(filename=stylesheet_file, output_style="compressed").encode("utf-8")
210
211 # Copy content from static files
212 for static_file in list_files(STATIC_DIRECTORY):
213 f = open(static_file, "rb")
214 data = f.read()
215 f.close()
216
217 files[
218 os.path.join("static", os.path.relpath(static_file, STATIC_DIRECTORY))
219 ] = data
220
221 files["sitemap.xml"] = sitemap_root.toprettyxml(indent="\t").encode("utf-8")
222
223 return files
224