From: Zachary Vance Date: Wed, 10 Jul 2024 22:46:50 +0000 (-0400) Subject: Add rss feed, authors, pagination magic X-Git-Url: https://git.za3k.com/?a=commitdiff_plain;h=9f5b098468c4650861d629dfd9d8ddfcdae79215;p=blog.git Add rss feed, authors, pagination magic --- diff --git a/blog b/blog index e0ef456..15db000 100755 --- a/blog +++ b/blog @@ -14,17 +14,18 @@ not documented. Other than that I think it's pretty nice! """ -# TODO: Pagination for main blog, categories, tag - import sys sys.dont_write_bytecode = True import argparse import chevron as mustache import collections +import datetime import math import collections import os, os.path +import re +import subprocess import yaml from pathlib import Path @@ -32,6 +33,11 @@ from pathlib import Path import frontmatter import monitor +NO_RESTART_EXIT_CODE = 27 +RESTART_EXIT_CODE = 28 +RFC822="%a, %d %b %Y %H:%M:%S %Z" +FRONTMATTER_DT="%Y-%m-%d %H:%M:%S%:z" + class PseudoMap(): def __getitem__(self, key): try: @@ -42,12 +48,41 @@ class PseudoMap(): setattr(self, key, value) # I have verified wordpress slugs match this format too -allowable="abcdefghijklmnopqrstuvwxyz0123456789-" +allowable="abcdefghijklmnopqrstuvwxyz0123456789-" def url_slug(title): - title = title.lower().replace(" ", "-") + title = title.lower().replace(" ", "-").replace(".", "-") title = "".join(x for x in title if x in allowable) return title +def paginated_property(f): + # Add .pages and .first10 with deep python magic + class Paginated(): + def __init__(self, lst): + self.lst = sorted(lst, key=lambda x: x.date, reverse=True) + def __iter__(self): + return iter(self.lst) + def __len__(self): + return len(self.lst) + @property + def pages(self, per_page=10): + for start in range(0, len(self.lst), per_page): + yield self.lst[start:start+per_page] + @property + def first10(self): + return self.lst[:10] + @property + def length(self): + return len(self.lst) + class AnonProperty(): + def __init__(self, fget): + self.fget = fget + def __set_name__(self, owner, name): + self._name = "_" + name + def __get__(self, obj, objtype=None): + return Paginated(self.fget(obj)) + + return AnonProperty(f) + def calc_range(l): it = iter(l) min = next(it) @@ -84,6 +119,43 @@ setTimeout(function() { """ +class Link(): + def __init__(self, original, blog, source): + self.original = original + self.partial = original.replace("https://blog.za3k.com/","") + if self.partial.endswith("/"): + self.partial = self.partial.removesuffix("/") + if "/" not in self.partial: + self.partial = "posts/" + self.partial + self.partial += ".html" + self.blog = blog + self.source = source + + @property + def wordpress(self): + return "https://blog.za3k.com/" + self.partial + + @property + def static(self): + return "../" + self.partial + + @property + def file(self): + return self.blog.destination + "/" + self.partial + + @property + def is_dead(self): + return not os.path.exists(self.file) + + def __hash__(self): + return hash(self.partial) + + def __lt__(self, other): + return self.partial < other.partial + + def __eq__(self, other): + return self.partial == other.partial + class Templatable(PseudoMap): use_layout = True def __init__(self, blog): @@ -98,12 +170,17 @@ class Templatable(PseudoMap): output_path_template = self.blog["{}_destination".format(self.type)] return Path(mustache.render(output_path_template, self.context)) - @staticmethod - def render_template(blog, name, context): + @property + def url(self): + return self.blog.web_root + "/" + self.output_path + + def render_template(source, blog, name, context): template_path = blog["{}_template".format(name)] with open(template_path, "r") as f: template = f.read() - return mustache.render(template, context, warn=True) + html = mustache.render(template, context, warn=True) + blog.replace_links(source, html) + return html def content(self): content = self.render_template(self.blog, self.type, self.context) @@ -112,7 +189,7 @@ class Templatable(PseudoMap): "content": content, }, self, self.blog)).encode("utf8") else: - return + return content.encode("utf8") def output(self): output = self.content() @@ -144,12 +221,17 @@ class Post(Templatable): self.post, self.comments = parsed.pop("content").split("\n") for k, v in parsed.items(): self[k] = v + + @property + def date_rfc822(self): + return self.date.strftime(RFC822) + @property def id(self): if hasattr(self, "wordpress_slug"): return self.wordpress_slug if hasattr(self, "slug"): return self.slug return url_slug(self.title) - + def __hash__(self): return hash(self.id) @@ -158,14 +240,14 @@ class Tag(Templatable): super().__init__(blog) self.tag = tag self._posts = set() - self.slug = url_slug(tag) + self.slug = {"minecraft": "minecraft-2"}.get(tag, url_slug(tag)) def add_post(self, post): self._posts.add(post) - @property + @paginated_property def posts(self): - return sorted(self._posts, key=lambda post: post.date, reverse=True) + return self._posts @property def num_posts(self): @@ -178,7 +260,18 @@ class Category(Tag): pass class Page(Templatable): - pass # TODO + def __init__(self, page_name, blog, use_layout=None): + super().__init__(blog) + self.page_name = page_name + if use_layout is not None: + self.use_layout = use_layout + + @property + def type(self): + return self.page_name + +class Author(Tag): + pass class Image(Templatable): use_layout = False @@ -188,8 +281,12 @@ class Blog(PseudoMap): def __init__(self, config="config.yaml", reload=False): self.tags = {} self.categories = {} - self.posts = [] + self.authors = {} + self._posts = [] self.reload = reload + self.links = set() + self.now = datetime.datetime.now(datetime.timezone.utc) + self.now_rfc822 = self.now.strftime(RFC822) self.config = os.path.abspath(config) self.load_config(config) @@ -206,17 +303,35 @@ class Blog(PseudoMap): v = os.path.join(self.source, os.path.expanduser(v)) self[k] = v + @property + def deadlinks(self): + return sorted(link for link in self.links if link.is_dead and all(x not in link.partial for x in ("?replytocom", "#comment"))) + + @paginated_property + def posts(self): + return self._posts + + def replace_links(self, source, html): + link_regex = '(? +

Author Archives: {{ tag }}

+ + +{{# posts.first10 }} + {{& post }} +{{/ posts.first10 }} diff --git a/templates/category.mustache.html b/templates/category.mustache.html index 830c2cf..f558658 100644 --- a/templates/category.mustache.html +++ b/templates/category.mustache.html @@ -2,6 +2,6 @@

Category Archives: {{ tag }}

-{{# posts }} +{{# posts.first10 }} {{& post }} -{{/ posts }} +{{/ posts.first10 }} diff --git a/templates/deadlinks.mustache.html b/templates/deadlinks.mustache.html new file mode 100644 index 0000000..3003ce4 --- /dev/null +++ b/templates/deadlinks.mustache.html @@ -0,0 +1,5 @@ +
    +{{# deadlinks }} +
  1. {{partial}} [orig] [src] +{{/ deadlinks }} +
diff --git a/templates/feed.mustache.html b/templates/feed.mustache.html new file mode 100644 index 0000000..7406b84 --- /dev/null +++ b/templates/feed.mustache.html @@ -0,0 +1,37 @@ + + + + {{title}} + + {{web_root}} + {{title}} + {{now_rfc822}} + en-US + hourly + 1 + {{# posts.first10 }} + + {{title}} + {{web_root}}/post/{{id}}.html + + {{date_rfc822}} + {{# categories }} + + {{/ categories }} + {{# tags }} + + {{/ tags }} + {{id}} + + + + {{/ posts.first10 }} + + diff --git a/templates/layout.mustache.html b/templates/layout.mustache.html index e988917..97be356 100644 --- a/templates/layout.mustache.html +++ b/templates/layout.mustache.html @@ -4,9 +4,8 @@ - - +