Index

joshstock.in / 05d74a2

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

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
8229 Mar 2020 15:4335b8efcAdd requirements list for Python/pip modulesJosh Stockin172G

Blob @ joshstock.in / root / compile.py

application/x-python6848 bytesdownload raw
1#!/usr/bin/env python3
2"""Open source template engine for compilation of the static joshstock.in"""
3
4import sys
5import os
6import shutil
7import json
8try:
9 import markdown2
10 import readtime
11except ImportError:
12 print("[GLOBAL] can't import required modules. are they installed?")
13 print("[GLOBAL] exiting...")
14 exit(1)
15
16
17def file_read(filename: str):
18 """Read text from file by filename"""
19 try:
20 with open(filename, "r") as file:
21 return file.read()
22 except FileNotFoundError:
23 print(f"[file_read] '{filename}' not found. exiting...")
24 exit(1)
25 except Exception as error:
26 print(f"[file_read] error while trying to read from '{filename}':")
27 print(error)
28 print("[file_read] exiting...")
29 exit(1)
30
31
32def file_write(filename: str, text: str):
33 """Write text to file by filename"""
34 try:
35 with open(filename, "w") as file:
36 file.write(text)
37 except Exception as error:
38 print(f"[file_write] error while trying to write to '{filename}':")
39 print(error)
40 print("[file_write] exiting...")
41 exit(1)
42
43
44def directory_empty(path: str):
45 """Clear file directory by path"""
46 for file in os.listdir(path):
47 filepath = os.path.join(path, file)
48 try:
49 if os.path.isfile(filepath):
50 os.unlink(filepath)
51 elif os.path.isdir(filepath):
52 shutil.rmtree(filepath)
53 except Exception as error:
54 print(f"[directory_empty] error while trying to empty directory {path}:")
55 print(error)
56 print("[directory_empty] exiting...")
57 exit(1)
58
59
60ROUTEMAP = {}
61TEMPLATES = {}
62
63
64def template_fill(template_string: str, template_keys: dict):
65 """Fills in all template key placeholders in template_string"""
66 global TEMPLATES
67 return_string = template_string
68 did_fill = False
69 for key in template_keys:
70 if f"${key}" in return_string:
71 return_string = return_string.replace(f"${key}", template_keys[key])
72 did_fill = True
73 for key in TEMPLATES:
74 if f"${key}" in return_string:
75 return_string = return_string.replace(f"${key}", TEMPLATES[key])
76 did_fill = True
77 if did_fill:
78 return_string = template_fill(return_string, template_keys)
79 return return_string
80
81
82def templates_load(templates_config: dict):
83 """Preload templates from their files"""
84 templates = {}
85 for temp in templates_config:
86 print(f"[templates_load] loading template '{temp}'")
87 templates[temp] = file_read(templates_config[temp])
88 return templates
89
90
91def template(output_path: str):
92 """The main template engine to generate the site's static content"""
93 global TEMPLATES
94 global ROUTEMAP
95 print("[template] emptying working directory")
96 directory_empty(output_path)
97
98 print("[template] reading config file at ./config.json")
99 config = json.loads(file_read("config.json"))
100
101 print("[template] copying static directory")
102 output_file = os.path.join(output_path, "static")
103 shutil.copytree(config["static_directory"], output_file)
104
105 print("[template] loading templates from config")
106 TEMPLATES = templates_load(config["templates"])
107
108 print("[template] running blog article generator")
109 blog_article_listings = ""
110 for article in config["articles"]:
111 article_url = f"/blog/{article['identifier']}"
112 print(f"[template/blog] creating article '{article['title']}' at {article_url}")
113
114 content = markdown2.markdown(file_read(article["markdown"]))
115 content_time = str(readtime.of_html(content))
116
117 # Create a new listing for the blog archive page
118 blog_article_listings += template_fill(
119 TEMPLATES["blog-listing"],
120 {
121 "title": article["title"],
122 "datestring": article["datestring"],
123 "readtime": content_time,
124 "banner": article["banner"],
125 "description": article["description"],
126 "permalink": article_url,
127 },
128 )
129
130 # Create blog article from template
131 blog_article = template_fill(
132 TEMPLATES["blog-article"],
133 {
134 "title": article["title"],
135 "datestring": article["datestring"],
136 "readtime": content_time,
137 "banner": article["banner"],
138 "description": article["description"],
139 "permalink": article_url,
140 "content": content,
141 },
142 )
143 output_file = os.path.join(output_path, f"blog-{article['identifier']}.html")
144 file_write(output_file, blog_article)
145 ROUTEMAP[f"{config['domain']}{article_url}"] = 0.7
146
147 TEMPLATES["@blog-listings"] = blog_article_listings
148
149 print("[template] running page generator")
150 for page in config["pages"]:
151 page_url = page["location"]
152 print(f"[template/page] creating page '{page['title']}' at {page_url}")
153 content = template_fill(
154 file_read(page["file"]),
155 {
156 "title": page["title"],
157 "description": page["description"],
158 "permalink": page_url,
159 },
160 )
161 output_file = os.path.join(output_path, page["destination"])
162 file_write(output_file, content)
163 ROUTEMAP[f"{config['domain']}{page_url}"] = page["priority"]
164
165 print("[template] copying custom static files")
166 for copy in config["copy"]:
167 print(f"[template/copy] copying file '{copy['file']}' to '{copy['location']}'")
168 output_file = os.path.join(output_path, copy["location"])
169 shutil.copy(copy["file"], output_file)
170
171 print("[template] compiling sitemap XML")
172 sitemap = TEMPLATES["sitemap"]
173 for route in ROUTEMAP:
174 sitemap += (
175 f"<url><loc>{route}</loc><priority>{ROUTEMAP[route]}</priority></url>"
176 )
177 sitemap += "</urlset>"
178 output_file = os.path.join(output_path, "sitemap.xml")
179 file_write(output_file, sitemap)
180
181 print("[template] finished")
182
183
184if __name__ == "__main__":
185 if len(sys.argv) < 2:
186 FOLDER_OUT = "/var/www/html"
187 else:
188 FOLDER_OUT = sys.argv[1]
189 print(f"[main] compile.py starting")
190 print(f"[main] changing active directory to script location")
191 os.chdir(sys.path[0])
192 if not os.path.isdir(FOLDER_OUT):
193 print(f"[main] {FOLDER_OUT} is not a valid folder location. exiting...")
194 exit(1)
195 OUTPUT_PATH = os.path.abspath(FOLDER_OUT)
196 print(f"[main] output path set to {OUTPUT_PATH}")
197 print(f"[main] running template engine routine")
198 template(OUTPUT_PATH)
199 print(f"[main] finished. exiting...")
200 exit(0)
201else:
202 print(f"[main] script is not __main__. exiting...")
203 exit(1)
204