summaryrefslogtreecommitdiff
path: root/blog
diff options
context:
space:
mode:
authorDmitry Ilvokhin <d@ilvokhin.com>2025-04-19 21:43:53 +0100
committerDmitry Ilvokhin <d@ilvokhin.com>2025-04-19 21:43:53 +0100
commit96d437baab4a622eb540719feb90ea810fb01756 (patch)
tree8757d41e9e2c673965483b2466ae306c9a7b8368 /blog
parentea523d411689338876abd7b87d85164009e7ec73 (diff)
downloadblog-master.tar.gz
blog-master.tar.bz2
blog-master.zip
Add support for atom / rss feedHEADmaster
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.
Diffstat (limited to 'blog')
-rw-r--r--blog/atom.py34
-rw-r--r--blog/blog.py4
-rw-r--r--blog/feed.py9
-rw-r--r--blog/post.py50
4 files changed, 70 insertions, 27 deletions
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))