Create pages for individual reviews
[books.alexwlchan.net] / scripts / render_html.py
1 #!/usr/bin/env python
2
3 import os
4 import pathlib
5 import subprocess
6 import sys
7
8 import attr
9 import frontmatter
10 import markdown
11 from jinja2 import Environment, FileSystemLoader, select_autoescape
12
13
14 def rsync(dir1, dir2):
15     subprocess.check_call(["rsync", "--recursive", "--delete", dir1, dir2])
16
17
18 @attr.s
19 class Book:
20     title = attr.ib()
21     author = attr.ib()
22     publication_year = attr.ib()
23     cover_image = attr.ib()
24     cover_desc = attr.ib()
25
26     isbn_13 = attr.ib(default="")
27
28
29 @attr.s
30 class Review:
31     date_read = attr.ib()
32     rating = attr.ib()
33     text = attr.ib()
34
35
36 @attr.s
37 class ReviewEntry:
38     path = attr.ib()
39     book = attr.ib()
40     review = attr.ib()
41
42
43 def get_review_entry_from_path(path):
44     post = frontmatter.load(path)
45
46     book = Book(**post["book"])
47     review = Review(**post["review"], text=post.content)
48
49     return ReviewEntry(path=path, book=book, review=review)
50
51
52 def get_reviews():
53     for dirpath, _, filenames in os.walk("src/reviews"):
54         for f in filenames:
55             if not f.endswith(".md"):
56                 continue
57
58             path = pathlib.Path(dirpath) / f
59
60             try:
61                 yield get_review_entry_from_path(path)
62             except Exception:
63                 print(f"Error parsing {path}", file=sys.stderr)
64                 raise
65
66
67 def render_markdown(text):
68     return markdown.markdown(text)
69
70
71 if __name__ == "__main__":
72     all_reviews = list(get_reviews())
73     all_reviews = sorted(
74         all_reviews, key=lambda rev: rev.review.date_read, reverse=True
75     )
76
77     env = Environment(
78         loader=FileSystemLoader("templates"),
79         autoescape=select_autoescape(["html", "xml"]),
80     )
81
82     env.filters["render_markdown"] = render_markdown
83
84     rsync("src/covers/", "_html/covers/")
85     rsync("src/static/", "_html/static/")
86
87     for review_entry in all_reviews:
88         template = env.get_template("review.html")
89         html = template.render(review_entry=review_entry)
90
91         out_name = review_entry.path.relative_to("src").with_suffix(".html")
92         out_path = pathlib.Path("_html") / out_name
93         out_path.parent.mkdir(exist_ok=True, parents=True)
94         out_path.write_text(html)