Commit
+102 -0 +/-8 browse
1 | diff --git a/gen/blog/building-this-website/index.html b/gen/blog/building-this-website/index.html |
2 | new file mode 100644 |
3 | index 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, "r") 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["parent"] = parent |
27 | + if "others" in link: |
28 | + _link(link["others"], parent=link) |
29 | + |
30 | + def _make_url(link): |
31 | + path = [link["name"]] |
32 | + parent = link["parent"] |
33 | + while parent is not None: |
34 | + path.append(parent["name"]) |
35 | + parent = parent["parent"] |
36 | + path.reverse() |
37 | + return "/".join(path) |
38 | + |
39 | + _link(links, parent=None) |
40 | + |
41 | + for link in flattened: |
42 | + target = "/" + _make_url(link) |
43 | + if target == current_path: |
44 | + link["active"] = True |
45 | + else: |
46 | + link["active"] = False |
47 | + link["url"] = 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("li") |
57 | + if "directory" in link and link["directory"]: |
58 | + elm.text = link["name"] |
59 | + else: |
60 | + lref = ET.Element("a", href=link["url"]) |
61 | + lref.text = link["name"] |
62 | + if link["active"]: |
63 | + lref.text = lref.text + " <" |
64 | + elm.append(lref) |
65 | + if "others" in link: |
66 | + others = link["others"] |
67 | + ul = ET.Element("ul") |
68 | + elm.append(ul) |
69 | + _populate(root=ul, links=others) |
70 | + root.append(elm) |
71 | + |
72 | + params = {"class": "tree"} |
73 | + |
74 | + root = ET.Element("ul", **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 & 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="$@" |
82 | + |
83 | + # some pretty colorized output |
84 | + _do_build() { |
85 | + $BUILD_CMD && { |
86 | + echo -e "\033[32;1;4mSuccess\033[0m" |
87 | + } || { |
88 | + echo -e "\033[31;1;4mFailure\033[0m" |
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 "file $file modified, rebuilding" |
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 |
106 | new file mode 100644 |
107 | index 0000000..e74e5b7 |
108 | Binary files /dev/null and b/gen/contact.png differ |
109 | diff --git a/gen/favicon.ico b/gen/favicon.ico |
110 | new file mode 100644 |
111 | index 0000000..59a4e28 |
112 | Binary files /dev/null and b/gen/favicon.ico differ |
113 | diff --git a/gen/index.html b/gen/index.html |
114 | new file mode 100644 |
115 | index 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 |
122 | new file mode 100644 |
123 | index 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 |
131 | new file mode 100644 |
132 | index 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 |
135 | new file mode 100644 |
136 | index 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 |
142 | new file mode 100644 |
143 | index 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 & 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 |