Pythonrender.py
-rwxr-xr-x 6.4 KiB
1#!/usr/bin/env python
2import argparse
3import os
4import sys
5import xml.etree.ElementTree as ET
6from io import StringIO
7from datetime import datetime
8
9import markdown
10import yaml
11
12from bs4 import BeautifulSoup
13from jinja2 import Environment, FunctionLoader
14
15
16def _load_sitemap(path, current_path):
17 by_name = dict()
18 flattened = []
19 with open(path, "r") as fp:
20 _sitemap = yaml.safe_load(fp.read())
21
22 if not _sitemap["enabled"]:
23 return None
24
25 variables = dict()
26 if "variables" in _sitemap:
27 variables = _sitemap["variables"]
28
29 def _filter_entries(entries):
30 entries = list(
31 filter(
32 lambda entry: "enabled" not in entry or entry["enabled"] is True,
33 entries,
34 )
35 )
36 for entry in entries:
37 if "others" in entry:
38 entry["others"] = _filter_entries(entry["others"])
39 return entries
40
41 entries = _filter_entries(_sitemap["entries"])
42
43 def _link(entries, parent=None):
44 for entry in entries:
45 flattened.append(entry)
46 entry["parent"] = parent
47 if "others" in entry:
48 _link(entry["others"], parent=entry)
49
50 def _make_url(link):
51 path = [link["name"]]
52 parent = link["parent"]
53 while parent is not None:
54 path.append(parent["name"])
55 parent = parent["parent"]
56 path.reverse()
57 return "/".join(path)
58
59 _link(entries, parent=None)
60
61 for entry in flattened:
62 target = "/" + _make_url(entry)
63 if target == current_path:
64 entry["active"] = True
65 else:
66 entry["active"] = False
67 entry["url"] = target
68 by_name[target] = entry
69
70 return dict(by_name=by_name, variables=variables, links=entries)
71
72
73def _make_tree(links):
74 def _populate(root, links):
75 for link in links:
76 elm = ET.Element("li")
77 if "directory" in link and link["directory"]:
78 elm.text = link["name"]
79 else:
80 lref = ET.Element("a", href=link["url"])
81 link_text = link["name"]
82 if "date" in link:
83 link_text = link_text + " [" + str(link["date"]) + "]"
84 lref.text = link_text
85 if link["active"]:
86 lref.text = lref.text + " <"
87 elm.append(lref)
88 if "others" in link:
89 others = link["others"]
90 ul = ET.Element("ul")
91 elm.append(ul)
92 _populate(root=ul, links=others)
93 root.append(elm)
94
95 params = {"class": "tree"}
96
97 root = ET.Element("ul", **params)
98 _populate(root=root, links=links)
99 return ET.tostring(root).decode()
100
101
102def load_content(path):
103 with open(path) as fp:
104 text = fp.read()
105 content = markdown.markdown(
106 text,
107 extensions=["fenced_code", "tables"],
108 )
109 return content
110
111
112def generate(template, content_path, stylesheet, link, sitemap, devmode=False):
113 tree = _make_tree(sitemap["links"])
114 with open(template, "r") as fp:
115 template = fp.read()
116 env = Environment(loader=FunctionLoader(lambda name: template))
117 base = env.get_template(template)
118 with open(stylesheet) as fp:
119 style = fp.read()
120 content = load_content(content_path)
121 env_content = Environment(loader=FunctionLoader(lambda name: content))
122 out = base.render(
123 {
124 "content": env_content.get_template("").render(
125 {"sitemap": sitemap, "tree": tree, "variables": sitemap["variables"]}
126 ),
127 "variables": sitemap["variables"],
128 "is_index": False,
129 "link": link,
130 "style": style,
131 "sitemap": sitemap,
132 "tree": tree,
133 "devmode": devmode,
134 "current_time": datetime.now(),
135 }
136 )
137 sys.stdout.write(out)
138
139
140def generate_rss(sitemap):
141 sitemap = _load_sitemap(sitemap, "/")
142
143 header = ET.Element(
144 "xml-stylesheet", attrib={"href": "/feed.xsl", "type": "text/xsl"}
145 )
146 rss = ET.Element("rss", attrib={"version": "2.0", "encoding": "utf-8"})
147 channel = ET.SubElement(rss, "channel")
148
149 title = ET.SubElement(channel, "title")
150 title.text = "Blog of Kevin Schoon"
151 link = ET.SubElement(channel, "link")
152 link.attrib["href"] = "https://kevinschoon.com"
153 description = ET.SubElement(channel, "description")
154 description.text = "Blog of Kevin Schoon"
155
156 for entry in sitemap["by_name"]["/blog"]["others"]:
157 absolute_url = "https://kevinschoon.com/blog/" + entry["name"]
158 item = ET.SubElement(channel, "item")
159 title_item = ET.SubElement(item, "title")
160 link_item = ET.SubElement(item, "link")
161 guid_item = ET.SubElement(item, "guid")
162 guid_item.text = absolute_url
163 description_item = ET.SubElement(item, "description")
164 title_item.text = entry["name"]
165 link_item.attrib["href"] = absolute_url
166 description_item.text = entry["tagline"]
167
168 xml_str = ET.tostring(rss, xml_declaration=True, encoding="unicode", method="xml")
169 xml_str = xml_str.replace(
170 "<?xml version='1.0' encoding='utf-8'?>",
171 "<?xml version='1.0' encoding='utf-8'?><?xml-stylesheet href='/style.xsl' type='text/xsl'?>",
172 )
173 sys.stdout.buffer.write(bytes(xml_str, encoding="utf-8"))
174
175
176if __name__ == "__main__":
177 parser = argparse.ArgumentParser(description="static renderer")
178 parser.add_argument("-rss", help="if rendering the rss feed", action="store_true")
179 parser.add_argument("-content", help="markdown file with content")
180 parser.add_argument(
181 "-template", default="index.html.jinja", help="jinja template file"
182 )
183 parser.add_argument("-stylesheet", default="./assets/main.css", help="style sheet")
184 parser.add_argument("-sitemap", default="sitemap.yaml", help="sitemap")
185 parser.add_argument("-link", default="/", help="current url")
186 parser.add_argument(
187 "-devmode", action="store_true", help="developer mode (websocket page refresh)"
188 )
189 args = parser.parse_args()
190 sitemap = _load_sitemap(args.sitemap, args.link)
191 if args.rss:
192 generate_rss(args.sitemap)
193 else:
194 generate(
195 template=args.template,
196 content_path=args.content,
197 stylesheet=args.stylesheet,
198 link=args.link,
199 sitemap=sitemap,
200 devmode=args.devmode,
201 )