Create pages for individual reviews
authorAlex Chan <alex@alexwlchan.net>
Sun, 2 Feb 2020 16:23:18 +0000 (16:23 +0000)
committerAlex Chan <alex@alexwlchan.net>
Sun, 2 Feb 2020 16:23:18 +0000 (16:23 +0000)
15 files changed:
.gitignore [new file with mode: 0644]
requirements.in [new file with mode: 0644]
requirements.txt [new file with mode: 0644]
scripts/render_html.py [new file with mode: 0755]
src/covers/trans-like-me.jpg [new file with mode: 0644]
src/covers/why-buildings-fall-down.jpg [new file with mode: 0644]
src/reviews/2020/trans-like-me.md [new file with mode: 0644]
src/reviews/2020/why-buildings-fall-down.md [new file with mode: 0644]
src/static/specktre_tQt7j.png [new file with mode: 0644]
src/static/style.css [new file with mode: 0644]
templates/_footer.html [new file with mode: 0644]
templates/_header.html [new file with mode: 0644]
templates/base.html [new file with mode: 0644]
templates/review.html [new file with mode: 0644]
templates/reviews.html [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..d78b704
--- /dev/null
@@ -0,0 +1 @@
+_html
diff --git a/requirements.in b/requirements.in
new file mode 100644 (file)
index 0000000..10faa8d
--- /dev/null
@@ -0,0 +1,4 @@
+attrs
+jinja2
+Markdown
+python-frontmatter
diff --git a/requirements.txt b/requirements.txt
new file mode 100644 (file)
index 0000000..92ad0d5
--- /dev/null
@@ -0,0 +1,16 @@
+#
+# This file is autogenerated by pip-compile
+# To update, run:
+#
+#    pip-compile
+#
+attrs==19.3.0
+jinja2==2.11.1
+markdown==3.1.1
+markupsafe==1.1.1         # via jinja2
+python-frontmatter==0.5.0
+pyyaml==5.3               # via python-frontmatter
+six==1.14.0               # via python-frontmatter
+
+# The following packages are considered to be unsafe in a requirements file:
+# setuptools
diff --git a/scripts/render_html.py b/scripts/render_html.py
new file mode 100755 (executable)
index 0000000..051befc
--- /dev/null
@@ -0,0 +1,94 @@
+#!/usr/bin/env python
+
+import os
+import pathlib
+import subprocess
+import sys
+
+import attr
+import frontmatter
+import markdown
+from jinja2 import Environment, FileSystemLoader, select_autoescape
+
+
+def rsync(dir1, dir2):
+    subprocess.check_call(["rsync", "--recursive", "--delete", dir1, dir2])
+
+
+@attr.s
+class Book:
+    title = attr.ib()
+    author = attr.ib()
+    publication_year = attr.ib()
+    cover_image = attr.ib()
+    cover_desc = attr.ib()
+
+    isbn_13 = attr.ib(default="")
+
+
+@attr.s
+class Review:
+    date_read = attr.ib()
+    rating = attr.ib()
+    text = attr.ib()
+
+
+@attr.s
+class ReviewEntry:
+    path = attr.ib()
+    book = attr.ib()
+    review = attr.ib()
+
+
+def get_review_entry_from_path(path):
+    post = frontmatter.load(path)
+
+    book = Book(**post["book"])
+    review = Review(**post["review"], text=post.content)
+
+    return ReviewEntry(path=path, book=book, review=review)
+
+
+def get_reviews():
+    for dirpath, _, filenames in os.walk("src/reviews"):
+        for f in filenames:
+            if not f.endswith(".md"):
+                continue
+
+            path = pathlib.Path(dirpath) / f
+
+            try:
+                yield get_review_entry_from_path(path)
+            except Exception:
+                print(f"Error parsing {path}", file=sys.stderr)
+                raise
+
+
+def render_markdown(text):
+    return markdown.markdown(text)
+
+
+if __name__ == "__main__":
+    all_reviews = list(get_reviews())
+    all_reviews = sorted(
+        all_reviews, key=lambda rev: rev.review.date_read, reverse=True
+    )
+
+    env = Environment(
+        loader=FileSystemLoader("templates"),
+        autoescape=select_autoescape(["html", "xml"]),
+    )
+
+    env.filters["render_markdown"] = render_markdown
+
+    rsync("src/covers/", "_html/covers/")
+    rsync("src/static/", "_html/static/")
+
+    for review_entry in all_reviews:
+        template = env.get_template("review.html")
+        html = template.render(review_entry=review_entry)
+
+        out_name = review_entry.path.relative_to("src").with_suffix(".html")
+        out_path = pathlib.Path("_html") / out_name
+        out_path.parent.mkdir(exist_ok=True, parents=True)
+        out_path.write_text(html)
diff --git a/src/covers/trans-like-me.jpg b/src/covers/trans-like-me.jpg
new file mode 100644 (file)
index 0000000..28c5641
Binary files /dev/null and b/src/covers/trans-like-me.jpg differ
diff --git a/src/covers/why-buildings-fall-down.jpg b/src/covers/why-buildings-fall-down.jpg
new file mode 100644 (file)
index 0000000..558549c
Binary files /dev/null and b/src/covers/why-buildings-fall-down.jpg differ
diff --git a/src/reviews/2020/trans-like-me.md b/src/reviews/2020/trans-like-me.md
new file mode 100644 (file)
index 0000000..215d17c
--- /dev/null
@@ -0,0 +1,30 @@
+---
+book:
+  title: Trans Like Me
+  author: C. N. Lester
+  publication_year: 2017
+  cover_image: trans-like-me.jpg
+  cover_desc: >
+    The words “trans like me” in lowercased, large letters, set on a slightly off-white background.
+    The words “trans” and “me” are in red; the word “like” is in black.
+  isbn_13: 9780349008608
+
+review:
+  date_read: 2020-01-27
+  rating: 5
+---
+
+I put off reading this for ages, but I’m glad I finally got to reading it.
+
+I’m also somewhere in the trans/non-binary/genderfluid probability cloud, and I’d done a lot of reading before I transitioned – so there’s wasn’t a huge amount of new information about “what it’s like to be trans”. (And fair warning, some of it might make unpleasant reading if you’ve been the victim of transphobia yourself.)
+
+The stuff about history and community activism was new to me, and interesting.
+
+Throughout it’s clearly written and easy to read – I motored through the whole thing in a single session.
+
+Highly recommended for cis people who want to learn more about what it’s like to be trans, or for trans people looking for a bit more about our history.
+
+Friends who've read/reviewed it:
+
+-   <https://www.facebook.com/ekabrett/posts/10219162994287519>
+-   <https://notebook.drmaciver.com/posts/2018-11-12-06:24.html>
diff --git a/src/reviews/2020/why-buildings-fall-down.md b/src/reviews/2020/why-buildings-fall-down.md
new file mode 100644 (file)
index 0000000..5a84785
--- /dev/null
@@ -0,0 +1,23 @@
+---
+book:
+  title: Why Buildings Fall Down
+  author: Matthys Levy, Mario Salvadori
+  publication_year: 1992
+  cover_image: why-buildings-fall-down.jpg
+  cover_desc: >
+    White cover, with the words “Why Buildings Fall Down” in blue.
+    Below the title is a picture pof three buildings: (L-R) a multi-storey white building with rectangular windows, the Leaning Tower of Pisa on a blue-ish background, and a line of circular columns on a brown-ish background.
+
+review:
+  date_read: 2020-01-25
+  rating: 4
+
+---
+
+Sequel to “Why Buildings Stand Up”. It’s an easy read, covering a variety of building styles, and explaining incidents in which the buildings failed. As before, clear writing and great illustrations.
+
+I enjoyed it, but not as much as the original. I skimmed more this time – where the first gave a high-level overview of architectural principles, this one went into more details and specifics. For example, when explaining how a tower block collapsed, it might explain the exact configuration of joints that failed. The general overview is more interesting to me.
+
+I’m glad I read them both, but I’d only recommend the first one unless you want more of the details.
+
+Review of the prequel: <https://books.alexwlchan.net/reviews/25>
diff --git a/src/static/specktre_tQt7j.png b/src/static/specktre_tQt7j.png
new file mode 100644 (file)
index 0000000..ef62e21
Binary files /dev/null and b/src/static/specktre_tQt7j.png differ
diff --git a/src/static/style.css b/src/static/style.css
new file mode 100644 (file)
index 0000000..81114f1
--- /dev/null
@@ -0,0 +1,97 @@
+body {
+  margin: 0;
+  padding: 0;
+  font: 12pt monospace;
+  line-height: 1.4em;
+}
+
+h1 .book-title {
+  font-style: italic;
+  line-height: 1.2em;
+}
+
+.book-cover {
+  float: right;
+  max-width:  150px;
+  max-height: 250px;
+  margin-left: 10px;
+  margin-bottom: 10px;
+}
+
+aside {
+  background: url('/static/specktre_tQt7j.png') #333366;
+  background-size: auto 100%;
+  color: white;
+  padding: 8px 1em;
+  font-size: 12pt;
+  line-height: 1.35em;
+}
+
+aside h1 {
+  margin-top: 0;
+  margin-bottom: 0;
+  display: inline;
+  font-size: 1em;
+}
+
+aside h1::after {
+  content: " 📚";
+}
+
+main, aside #aside_inner, footer #footer_inner {
+  max-width: 750px;
+  margin-left:  auto;
+  margin-right: auto;
+}
+
+main {
+  padding: 1em;
+}
+
+a {
+  color: #4c3dae;
+}
+
+a:hover {
+  background: rgba(76, 61, 174, 0.3);
+}
+
+aside a {
+  color: white;
+}
+
+aside a:hover {
+  background: rgba(255, 255, 255, 0.3);
+}
+
+table {
+  border-collapse: collapse;
+  margin-bottom: 1em;
+}
+
+td {
+  padding-right: 6px;
+  padding-top:    2px;
+  padding-bottom: 2px;
+}
+
+th {
+  text-align: left;
+}
+
+footer {
+  border-top: 0.25px solid #bfbfbf;
+  color: #999;
+  font-size: 0.75em;
+  margin-top: 2em;
+  padding: 1em;
+  padding-bottom: 2em;
+}
+
+footer a {
+  color: gray;
+}
+
+footer a:hover {
+  background: rgba(128, 128, 128, 0.3);
+}
diff --git a/templates/_footer.html b/templates/_footer.html
new file mode 100644 (file)
index 0000000..ef975b7
--- /dev/null
@@ -0,0 +1,7 @@
+<footer>
+  <div id="footer_inner">
+    made with 💖 by <a href="https://twitter.com/alexwlchan">lexie</a>, who thinks you look great today
+    •
+    code on <a href="https://snekks.club/alexwlchan/books.alexwlchan.net">snekks.club</a>
+  </div>
+</footer>
diff --git a/templates/_header.html b/templates/_header.html
new file mode 100644 (file)
index 0000000..76f825a
--- /dev/null
@@ -0,0 +1,6 @@
+<aside>
+  <div id="aside_inner">
+    <h1 id="brand">lexie&rsquo;s book tracker</h1><br>
+    <a href="/reviews">reviews</a>
+  </div>
+</aside>
diff --git a/templates/base.html b/templates/base.html
new file mode 100644 (file)
index 0000000..d97c92e
--- /dev/null
@@ -0,0 +1,20 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+
+    <link rel="stylesheet" href="/static/style.css">
+
+    <title>{% if title %}{{ title | safe }} {% endif %}📚 lexie&rsquo;s book tracker</title>
+  </head>
+  <body>
+    {% include "_header.html" %}
+
+    <main>
+      {% block content %}{% endblock %}
+    </main>
+
+    {% include "_footer.html" %}
+  </body>
+</html>
diff --git a/templates/review.html b/templates/review.html
new file mode 100644 (file)
index 0000000..233e7f7
--- /dev/null
@@ -0,0 +1,37 @@
+{% extends "base.html" %}
+
+{% block content %}
+  {% if review_entry.book.cover_image %}
+  <img class="book-cover" src="/covers/{{ review_entry.book.cover_image }}" alt="Cover of {{ review_entry.book.title }}. {{ review_entry.book.cover_desc }}">
+  {% endif %}
+
+  <h1>My review of <span class="book-title">{{ review_entry.book.title }}</span></h1>
+
+  <table>
+    <tr>
+      <td>author:</td>
+      <th>{{ review_entry.book.author }}
+        {%- if review_entry.book.publication_year %}
+          ({{ review_entry.book.publication_year}})
+        {%- endif -%}
+      </th>
+    </tr>
+    <tr>
+      <td>rating:</td>
+      <th>
+        {% for _ in range(review_entry.review.rating) %}
+        ★
+        {% endfor %}
+        {% for _ in range(5 - review_entry.review.rating) %}
+        ☆
+        {% endfor %}
+      </th>
+    </tr>
+    <tr>
+      <td>date read:</td>
+      <th>{{ review_entry.review.date_read }}</th>
+    </tr>
+  </table>
+
+  {{ review_entry.review.text | render_markdown | safe }}
+{% endblock %}
diff --git a/templates/reviews.html b/templates/reviews.html
new file mode 100644 (file)
index 0000000..63913c1
--- /dev/null
@@ -0,0 +1 @@
+{% extends "base.html" %}
\ No newline at end of file