From 96d437baab4a622eb540719feb90ea810fb01756 Mon Sep 17 00:00:00 2001 From: Dmitry Ilvokhin Date: Sat, 19 Apr 2025 21:43:53 +0100 Subject: Add support for atom / rss feed Date format was changed to support `updated` in ISO format. Now every post will require date and time, which might be a usability regression, but we'll see. Also, `updated` field was introduced in metadata. If there is no such field, them date field will be used instead. --- README.md | 2 +- blog/atom.py | 34 ++++++++++++++++++ blog/blog.py | 4 +++ blog/feed.py | 9 ++--- blog/post.py | 50 +++++++++++++++----------- posts/hello-world/metadata.txt | 2 +- posts/jemalloc-hpa-reference/metadata.txt | 2 +- posts/libstdc++-std-unordered-map/metadata.txt | 2 +- share/style.css | 4 --- templates/atom.xml | 24 +++++++++++++ templates/feed.html | 7 ++-- templates/post.html | 11 ++++-- 12 files changed, 111 insertions(+), 40 deletions(-) create mode 100644 blog/atom.py create mode 100644 templates/atom.xml diff --git a/README.md b/README.md index 7ac9d4a..94afe61 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ $ pip3 install -r requirements.txt ``` $ mkdir posts/hello-world $ echo 'Title: Hello, World!' > posts/hello-world/metadata.txt -$ echo 'Date: 2022-12-23' >> posts/hello-world/metadata.txt +$ echo "Date: `date +"%Y-%m-%dT%H:%M:%S%z"`" >> posts/hello-world/metadata.txt $ echo 'Hello, world!' > posts/hello-world/hello-world.md $ make deploy ``` diff --git a/blog/atom.py b/blog/atom.py new file mode 100644 index 0000000..e6d836c --- /dev/null +++ b/blog/atom.py @@ -0,0 +1,34 @@ +import os +import datetime +import functools +from typing import List + +from jinja2 import Template + +import render +from post import Post, remove_drafts + + +class Atom(object): + template: Template + posts: List[Post] + + def __init__(self, template: Template, posts: List[Post]) -> None: + self.template = template + self.posts = remove_drafts(posts) + + @staticmethod + def _now() -> datetime.datetime: + now = datetime.datetime.now(datetime.timezone.utc) + return now.replace(microsecond=0) + + @functools.cached_property + def updated(self) -> datetime.datetime: + if not self.posts: + return self._now() + return self.posts[0].metadata.updated + + def generate(self, basedir: str) -> None: + atom = os.path.join(basedir, "atom.xml") + rendered = self.template.render(updated=self.updated, posts=self.posts) + render.write_file_content(atom, rendered) diff --git a/blog/blog.py b/blog/blog.py index 27845bc..4b9a345 100644 --- a/blog/blog.py +++ b/blog/blog.py @@ -10,6 +10,7 @@ from jinja2 import Template, Environment, FileSystemLoader, select_autoescape from post import Post from feed import Feed +from atom import Atom def recreate_workdir(basedir: str) -> None: @@ -49,6 +50,9 @@ def generate_blog() -> None: feed = Feed(env.get_template("feed.html"), posts) feed.generate(workdir) + atom = Atom(env.get_template("atom.xml"), posts) + atom.generate(workdir) + copy_share(workdir) diff --git a/blog/feed.py b/blog/feed.py index 3e5b2e0..381aab6 100644 --- a/blog/feed.py +++ b/blog/feed.py @@ -1,10 +1,11 @@ import os +import datetime from typing import List from jinja2 import Template import render -from post import Post +from post import Post, remove_drafts class Feed(object): @@ -13,11 +14,7 @@ class Feed(object): def __init__(self, template: Template, posts: List[Post]) -> None: self.template = template - self.posts = Feed._remove_drafts(posts) - - @staticmethod - def _remove_drafts(posts: List[Post]) -> List[Post]: - return list(filter(lambda x: x.metadata.status != "draft", posts)) + self.posts = remove_drafts(posts) def generate(self, basedir: str) -> None: index = os.path.join(basedir, "index.html") diff --git a/blog/post.py b/blog/post.py index 8a7c880..955724a 100644 --- a/blog/post.py +++ b/blog/post.py @@ -2,27 +2,23 @@ import os import shutil import datetime import functools -from typing import Dict +from typing import Dict, List from jinja2 import Template +from dataclasses import dataclass + import render +@dataclass(slots=True) class Metadata(object): - __slots__ = ("title", "author", "date", "status") title: str author: str - date: str + date: datetime.datetime + updated: datetime.datetime status: str - def __init__(self, title: str, author: str, - date: str, status: str) -> None: - self.title = title - self.author = author - self.date = date - self.status = status - class Post(object): template: Template @@ -52,33 +48,45 @@ class Post(object): title = raw["Title"] author = raw["Author"] - date = raw.get("Date", datetime.date.today().strftime("%Y-%m-%d")) + date = raw["Date"] + updated = raw.get("Updated", date) status = raw.get("Status", "draft") - return Metadata(title, author, date, status) + return Metadata( + title, + author, + datetime.datetime.fromisoformat(date), + datetime.datetime.fromisoformat(updated), + status + ) + + @functools.cached_property + def content(self) -> str: + md = None + for filename in os.listdir(self.directory): + if filename.endswith(".md"): + return render.to_html(os.path.join(self.directory, filename)) + assert False, f"There is no markdown file in `{self.directory}`" def generate(self, basedir: str) -> None: postdir = os.path.basename(self.directory) workdir = os.path.join(basedir, postdir) os.makedirs(workdir) - md = None for filename in os.listdir(self.directory): source = os.path.join(self.directory, filename) destination = os.path.join(workdir, filename) - shutil.copy(source, destination) - if filename.endswith(".md"): - md = source - - assert md, f"There is no markdown file in `{self.directory}`" - - content = render.to_html(md) rendered = self.template.render(title=self.metadata.title, author=self.metadata.author, date=self.metadata.date, + updated=self.metadata.updated, status=self.metadata.status, - content=content) + content=self.content) render.write_file_content(os.path.join(workdir, "index.html"), rendered) + + +def remove_drafts(posts: List[Post]) -> List[Post]: + return list(filter(lambda x: x.metadata.status != "draft", posts)) diff --git a/posts/hello-world/metadata.txt b/posts/hello-world/metadata.txt index 4c57cf0..6a41b6b 100644 --- a/posts/hello-world/metadata.txt +++ b/posts/hello-world/metadata.txt @@ -1,4 +1,4 @@ Title: Hello, World! Author: Dmitry Ilvokhin -Date: 2022-12-23 +Date: 2022-12-23T17:34:00+00:00 Status: published diff --git a/posts/jemalloc-hpa-reference/metadata.txt b/posts/jemalloc-hpa-reference/metadata.txt index 0a84976..44feafe 100644 --- a/posts/jemalloc-hpa-reference/metadata.txt +++ b/posts/jemalloc-hpa-reference/metadata.txt @@ -1,4 +1,4 @@ Title: Jemalloc HPA Reference Author: Dmitry Ilvokhin -Date: 2025-03-23 +Date: 2025-03-23T12:58:00+00:00 Status: published diff --git a/posts/libstdc++-std-unordered-map/metadata.txt b/posts/libstdc++-std-unordered-map/metadata.txt index 24b3750..603d518 100644 --- a/posts/libstdc++-std-unordered-map/metadata.txt +++ b/posts/libstdc++-std-unordered-map/metadata.txt @@ -1,4 +1,4 @@ Title: How libstdc++ `std::unordered_map` implemented? Author: Dmitry Ilvokhin -Date: 2023-07-02 +Date: 2023-07-02T13:08:00+01:00 Status: published diff --git a/share/style.css b/share/style.css index 592e612..a6b2576 100644 --- a/share/style.css +++ b/share/style.css @@ -112,10 +112,6 @@ ul.posts li span { ul.posts time { } -ul.posts li a:visited { - color: #8b6fcb; -} - .toclink { color: #222; } diff --git a/templates/atom.xml b/templates/atom.xml new file mode 100644 index 0000000..ba7fbda --- /dev/null +++ b/templates/atom.xml @@ -0,0 +1,24 @@ + + blog.ilvokhin.com + tag:blog.ilvokhin.com,2022:blog.ilvokhin.com + + + {{ updated.isoformat() }} + + Dmitry Ilvokhin + https://ilvokhin.com/ + d@ilvokhin.com + +{% if posts %} + {% for post in posts %} + + {{ post.metadata.title }} + tag:blog.ilvokhin.com,2022:blog.ilvokhin.com/{{ post.name }} + + {{ post.metadata.date.isoformat() }} + {{ post.metadata.updated.isoformat() }} + {{ post.content }} + + {% endfor %} +{% endif %} + diff --git a/templates/feed.html b/templates/feed.html index deb89c5..e17d3d0 100644 --- a/templates/feed.html +++ b/templates/feed.html @@ -28,8 +28,8 @@
  • - @@ -41,6 +41,9 @@ + diff --git a/templates/post.html b/templates/post.html index 20c3ac2..f61a2bd 100644 --- a/templates/post.html +++ b/templates/post.html @@ -22,9 +22,14 @@

    {{ title }}

    - + + + {% if date != updated %} + (updated ) + {% endif %} + {% if status == "draft" %} • {{ status }} {% endif %} -- cgit v1.2.3-70-g09d2