Commit
Author: Kevin Schoon [me@kevinschoon.com]
Hash: 775ae4bed0be4ce8041673ae1f9811382801eeed
Timestamp: Mon, 26 Aug 2024 16:55:18 +0000 (2 months ago)

+102 -0 +/-8 browse
generate
1diff --git a/gen/blog/building-this-website/index.html b/gen/blog/building-this-website/index.html
2new file mode 100644
3index 0000000..023ebc8
4--- /dev/null
5+++ b/gen/blog/building-this-website/index.html
6 @@ -0,0 +1,97 @@
7+ <!DOCTYPE html><html lang=en> <head><title>kevinschoon-dot-com</title><meta charset=UTF-8><meta name=viewport content="width=device-width, initial-scale=1"><link rel=stylesheet href=/main.css><link rel=alternate type=application/rss+xml title=RSS href=https://kevinschoon.com/index.xml></head> <body> <section> <div class=wrapper> <header class=page-header> <a href=/ >Home</a> </header> <main class=page-body> <h1>Building This Website</h1> <p>There is no deficiency of static website generators these days and so naturally I had to write my own.</p> <h2>Overview</h2> <p>The main functionality of the tool will be encapsulated in a Python script called <code>render.py</code>. Even though <a href=https://www.devever.net/~hl/stringtemplates#narrow>string based templates are the wrong solution</a> we're going to use a combination of <a href=https://jinja.palletsprojects.com/ >Jinja</a> and markdown to generate HTML output. Each page will be generated by a single call to the script which will allow the whole thing to be ran in parallel across multiple CPUs by wiring it together using <a href=https://ninja-build.org/ >ninja</a>.</p> <p>Here you can see an example invocation of the <code>render.py</code> script with some annotated flags.</p> <pre><code>./render.py \
8+ # the HTML template file
9+ --template=index.jinja \
10+ # this contains a map of each webpage which is available in the renderer
11+ # as well as page level configuration options.
12+ --sitemap sitemap.yaml \
13+ # markdown file, this one contains the text I'm currently typing!
14+ --content=blog/building-this-website/README.md \
15+ # output of the rendered HTML page
16+ gen/blog/building-this-website/index.html
17+ </code></pre> <h3>Hierarchical Sitemap</h3> <p>I want to generate a "tree" sitemap similar to a file browser like NERDTree. We'll pass in a YAML document to the <code>render.py</code> script and then transform it into an XML tree.</p> <pre><code>def _load_sitemap(path, current_path):
18+ by_name = dict()
19+ flattened = []
20+ with open(path, &quot;r&quot;) as fp:
21+ links = yaml.safe_load(fp.read())
22+
23+ def _link(links, parent=None):
24+ for link in links:
25+ flattened.append(link)
26+ link[&quot;parent&quot;] = parent
27+ if &quot;others&quot; in link:
28+ _link(link[&quot;others&quot;], parent=link)
29+
30+ def _make_url(link):
31+ path = [link[&quot;name&quot;]]
32+ parent = link[&quot;parent&quot;]
33+ while parent is not None:
34+ path.append(parent[&quot;name&quot;])
35+ parent = parent[&quot;parent&quot;]
36+ path.reverse()
37+ return &quot;/&quot;.join(path)
38+
39+ _link(links, parent=None)
40+
41+ for link in flattened:
42+ target = &quot;/&quot; + _make_url(link)
43+ if target == current_path:
44+ link[&quot;active&quot;] = True
45+ else:
46+ link[&quot;active&quot;] = False
47+ link[&quot;url&quot;] = target
48+ by_name[target] = link
49+
50+ return dict(by_name=by_name, links=links)
51+
52+
53+ def _make_tree(links):
54+ def _populate(root, links):
55+ for link in links:
56+ elm = ET.Element(&quot;li&quot;)
57+ if &quot;directory&quot; in link and link[&quot;directory&quot;]:
58+ elm.text = link[&quot;name&quot;]
59+ else:
60+ lref = ET.Element(&quot;a&quot;, href=link[&quot;url&quot;])
61+ lref.text = link[&quot;name&quot;]
62+ if link[&quot;active&quot;]:
63+ lref.text = lref.text + &quot; &lt;&quot;
64+ elm.append(lref)
65+ if &quot;others&quot; in link:
66+ others = link[&quot;others&quot;]
67+ ul = ET.Element(&quot;ul&quot;)
68+ elm.append(ul)
69+ _populate(root=ul, links=others)
70+ root.append(elm)
71+
72+ params = {&quot;class&quot;: &quot;tree&quot;}
73+
74+ root = ET.Element(&quot;ul&quot;, **params)
75+ _populate(root=root, links=links)
76+ return ET.tostring(root).decode()
77+ </code></pre> <h3>Dithering Engine</h3> <p>Images can be reduced in size with <a href=https://en.wikipedia.org/wiki/Dither>dithering algorithms</a> and they also look very cool. The idea for this was inspired by <a href=https://solar.lowtechmagazine.com/ >LOW←TECH MAGAZINE</a>.</p> <h3>Spell &amp; Grammar Checking</h3> <p>I can implement some basic spell checking against the website with <a href=http://aspell.net/ >aspell</a>.</p> <pre><code class=language-sh>find content -name '*.md' -exec aspell --mode=markdown check {} \;
78+ </code></pre> <h3>Filesystem "Watch" Support</h3> <p>We'll use the native <a href=https://en.wikipedia.org/wiki/Inotify>inotify</a> via the <a href=https://man7.org/linux/man-pages/man1/inotifywatch.1.html>inotifywatch</a> tool which is available on all Linux distributions.</p> <pre><code class=language-sh>#!/bin/sh
79+
80+ # the actual command we want to run each time any file changes in the project
81+ BUILD_CMD=&quot;$@&quot;
82+
83+ # some pretty colorized output
84+ _do_build() {
85+ $BUILD_CMD &amp;&amp; {
86+ echo -e &quot;\033[32;1;4mSuccess\033[0m&quot;
87+ } || {
88+ echo -e &quot;\033[31;1;4mFailure\033[0m&quot;
89+ }
90+ }
91+
92+ # build the project the first time you start up the watch
93+ _do_build
94+
95+ # watch all the files in the project except for the output directory
96+ find . -type d -not -path './gen*' -printf '%p ' | xargs inotifywait \
97+ -m -e close_write \
98+ --format %e/%f |
99+ while IFS=/ read -r events file; do
100+ echo &quot;file $file modified, rebuilding&quot;
101+ _do_build
102+ done
103+ </code></pre> <p>Now we can test the script by launching it <code>scripts/watch.sh build.py</code>.</p> <p>You can checkout the source for the whole site <a href=https://ayllu-forge.org/web/kevinschoon-dot-com>here</a>.</p> </main> <footer class=page-footer></footer> </div> </section> </body> </html>
104\ No newline at end of file
105 diff --git a/gen/contact.png b/gen/contact.png
106new file mode 100644
107index 0000000..e74e5b7
108 Binary files /dev/null and b/gen/contact.png differ
109 diff --git a/gen/favicon.ico b/gen/favicon.ico
110new file mode 100644
111index 0000000..59a4e28
112 Binary files /dev/null and b/gen/favicon.ico differ
113 diff --git a/gen/index.html b/gen/index.html
114new file mode 100644
115index 0000000..d7d83e2
116--- /dev/null
117+++ b/gen/index.html
118 @@ -0,0 +1 @@
119+ <!DOCTYPE html><html lang=en> <head><title>kevinschoon-dot-com</title><meta charset=UTF-8><meta name=viewport content="width=device-width, initial-scale=1"><link rel=stylesheet href=/main.css><link rel=alternate type=application/rss+xml title=RSS href=https://kevinschoon.com/index.xml></head> <body> <section> <div class=wrapper> <header class=page-header> </header> <main class=page-body> <p><a href=/ ><img alt="dithered photo of me" class=me src=/ks_stylized.png></a></p> <h1>Hi, thanks for stopping by!</h1> <p>I'm a programmer interested in free software.</p> <h3>Sitemap</h3> <p><ul class=tree><li><a href=/blog>blog</a><ul><li><a href=/blog/building-this-website>building-this-website [2023-04-04]</a></li></ul></li><li><a href=/projects>projects</a><ul><li><a href=/projects/air-quality-torino>air-quality-torino</a></li></ul></li></ul></p> <h3>Email</h3> <p>You can contact me at <a href=mailto:me@kevinschoon.com>me@kevinschoon.com</a>.</p> <h3>Software</h3> <p>Check out some of my past and present projects <a href=https://ayllu-forge.org/browse>here</a>.</p> <h3>Contact (QR)</h3> <p>Contact info (same as above, but a QR code):</p> <p><img alt="qr code" class=contact src=/contact.png></p> <h3>Subscribe</h3> <p><a href=/index.xml>RSS</a></p> </main> <footer class=page-footer></footer> </div> </section> </body> </html>
120\ No newline at end of file
121 diff --git a/gen/index.xml b/gen/index.xml
122new file mode 100644
123index 0000000..cc3663d
124--- /dev/null
125+++ b/gen/index.xml
126 @@ -0,0 +1,2 @@
127+ <?xml version='1.0' encoding='utf-8'?>
128+ <rss version="2.0" encoding="UTF-8"><channel><title>Blog of Kevin Schoon</title><link href="https://kevinschoon.com" /><description>Blog of Kevin Schoon</description><item><title>building-this-website</title><link href="https://kevinschoon.com/blog/building-this-website" /><guid>https://kevinschoon.com/blog/building-this-website</guid><description>There is no deficiency of static website generators these days and so I had to write my own.</description></item></channel></rss>
129\ No newline at end of file
130 diff --git a/gen/ks_stylized.png b/gen/ks_stylized.png
131new file mode 100644
132index 0000000..0123974
133 Binary files /dev/null and b/gen/ks_stylized.png differ
134 diff --git a/gen/main.css b/gen/main.css
135new file mode 100644
136index 0000000..418a962
137--- /dev/null
138+++ b/gen/main.css
139 @@ -0,0 +1 @@
140+ :root{--accent: #b1675d;--light-gray: #eee;--dark-gray: #c2c2c2;--white: #f8f9fa;--black: #2d2d2d;--border-width: 5px}table{text-align:justify;width:100%;border-collapse:collapse;margin-bottom:2rem}td,th{padding:0.5em;border-bottom:1px solid #4a4a4a}html,body{box-sizing:border-box;font-size:16px;height:100%;margin:0;padding:0;max-width:800px}section{height:100%}.wrapper{min-height:100%;display:grid;grid-template-rows:auto 1fr auto}.page-body{padding:20px}*,*:before,*:after{box-sizing:inherit}ol,ul{list-style:none}p{padding:1px;margin:10px}span{background:white;border:1px solid black}pre{background:#e2e2e2;font-family:monospace}img.me{border-radius:2em;max-width:350px}@media (prefers-color-scheme: dark){:root{background-color:#2c2c2c;color:white}pre{color:white;background:#3f3f3f}a:link{color:white;text-decoration:underline}a:visited{color:white;text-decoration:none}.notice{border-color:white}}@media (prefers-color-scheme: light){:root{background-color:#f5f5f5;color:black}pre{color:black;background:#e2e2e2}a:link{color:black;text-decoration:underline}a:visited{color:black;text-decoration:none}.notice{border-color:black}}.tree,.tree ul{font-size:18px;font:normal normal 14px/20px Helvetica, Arial, sans-serif;list-style-type:none;margin-left:0 0 0 10px;padding:0;position:relative;overflow:hidden}.tree li{margin:0;padding:0 12px;position:relative}.tree li::before,.tree li::after{content:"";position:absolute;left:0}.tree li::before{border-top:2px solid #999;top:10px;width:10px;height:0}.tree li:after{border-left:2px solid #999;height:100%;width:0px;top:-10px}.tree>li::after{top:10px}.tree>li:last-child::after{display:none}
141 diff --git a/gen/projects/air-quality-torino/index.html b/gen/projects/air-quality-torino/index.html
142new file mode 100644
143index 0000000..584a5b6
144--- /dev/null
145+++ b/gen/projects/air-quality-torino/index.html
146 @@ -0,0 +1 @@
147+ <!DOCTYPE html><html lang=en> <head><title>kevinschoon-dot-com</title><meta charset=UTF-8><meta name=viewport content="width=device-width, initial-scale=1"><link rel=stylesheet href=/main.css><link rel=alternate type=application/rss+xml title=RSS href=https://kevinschoon.com/index.xml></head> <body> <section> <div class=wrapper> <header class=page-header> <a href=/ >Home</a> </header> <main class=page-body> <h1>Torino Air Quality</h1> <p>Northern Italy is home to some of the worst air pollution in Europe due to factors such as it's rapid industrialization in the 20th century, pollution from automobiles, and it's lack of wind due to the region's position beside the Alps. <a href=#ref>1</a> <a href=#ref>2</a> <a href=#ref>3</a></p> <h2>Real Time Data</h2> <p>Below is real time data that is collected from an <a href=https://www.airgradient.com/ >Air Gradient</a> air quality monitor. It is positioned outside my apartment in the Vanchiglietta neighborhood of Torino, Italy.</p> <iframe width=425 height=350 src="https://www.openstreetmap.org/export/embed.html?bbox=7.695815563201904%2C45.05788017528605%2C7.731091976165772%2C45.086717679634475&layer=mapnik" style="border: 1px solid black"></iframe> <p><small><a href="https://www.openstreetmap.org/#map=16/45.07230/7.71345">View Larger Map</a></small></p> <h3>Particulate Matter</h3> <h4>EU Index Guidelines</h4> <table> <thead> <tr> <th>Index</th> <th>Good</th> <th>Fair</th> <th>Moderate</th> <th>Poor</th> <th>Very poor</th> <th>Extremely poor</th> </tr> </thead> <tbody> <tr> <td>PM2,5</td> <td>0–10 μg/m³</td> <td>10–20 μg/m³</td> <td>20–25 μg/m³</td> <td>25–50 μg/m³</td> <td>50–75 μg/m³</td> <td>75–800 μg/m³</td> </tr> <tr> <td>PM10</td> <td>0–20 μg/m³</td> <td>20–40 μg/m³</td> <td>40–50 μg/m³</td> <td>50–100 μg/m³</td> <td>100–150 μg/m³</td> <td>150–1200 μg/m³</td> </tr> </tbody> </table> <p><img src="https://charts.ayllu-forge.org/chart.svg?query=%7B__name__%3D~%22airgradient_(pm1_ugm3%7Cpm2d5_ugm3%7Cpm10_ugm3)%22%7D&label=%7B%7B.__name__%7D%7D&width=8&height=3&background-color=%23333333&foreground-color=%23e4e4e4&title=Particulates"></p> <h3>Nitrous Oxide (NOx)</h3> <p><img src="https://charts.ayllu-forge.org/chart.svg?query=airgradient_nox_raw&label=%7B%7B.__name__%7D%7D&width=8&height=3&background-color=%23333333&foreground-color=%23e4e4e4&title=NOx"></p> <h3>CO2</h3> <p><img src="https://charts.ayllu-forge.org/chart.svg?query=airgradient_co2_ppm&label=%7B%7B.__name__%7D%7D&width=8&height=3&background-color=%23333333&foreground-color=%23e4e4e4&title=CO2"></p> <h3>Temperature &amp; Humidity</h3> <p><img src="https://charts.ayllu-forge.org/chart.svg?query=%7B__name__%3D~%22(airgradient_humidity_percent%7Cairgradient_temperature_celsius)%22%7D&width=8&height=3&label=%7B%7B.__name__%7D%7D&background-color=%23333333&foreground-color=%23e4e4e4&min=0&title=Temperature"></p> <p><br><h2 id=ref> Refs </h2> </p> <ol> <li><a href=https://en.wikipedia.org/wiki/Northern_Italy#Pollution>Northern Italy Pollution (Wikipedia)</a></li> <li><a href=https://www.esa.int/ESA_Multimedia/Images/2019/05/Nitrogen_dioxide_over_northern_Italy>Nitrogen Dioxide over northern Italy</a></li> <li><a href=https://aqli.epic.uchicago.edu/news/air-pollution-hotspots-in-europe/ >Air Pollution in Europe</a></li> </ol> </main> <footer class=page-footer></footer> </div> </section> </body> </html>
148\ No newline at end of file