Browse Source

Add blog generator

master
David Larlet 4 years ago
parent
commit
d99cd92a6d
No known key found for this signature in database
1 changed files with 98 additions and 15 deletions
  1. 98
    15
      runner.py

+ 98
- 15
runner.py View File

import locale import locale
import os import os
import socketserver import socketserver
from collections import namedtuple
from dataclasses import dataclass from dataclasses import dataclass
from datetime import date, datetime from datetime import date, datetime
from html import escape from html import escape




@dataclass @dataclass
class Note:
class Item:
title: str title: str
content: str content: str
file_path: str file_path: str


def __post_init__(self): def __post_init__(self):
suffix = len("/index.md")
prefix = len("YYYY/MM/DD") + suffix
date_str = self.file_path[-prefix:-suffix]
self.url = f"/david/stream/{date_str}/"
self.full_url = f"{DOMAIN}{self.url}" self.full_url = f"{DOMAIN}{self.url}"
self.date = datetime.strptime(date_str, "%Y/%m/%d").date()
self.normalized_date = self.date.strftime(NORMALIZED_STRFTIME) self.normalized_date = self.date.strftime(NORMALIZED_STRFTIME)
self.escaped_title = escape(self.title) self.escaped_title = escape(self.title)
self.escaped_content = escape( self.escaped_content = escape(
'src="/', f'src="{DOMAIN}/' 'src="/', f'src="{DOMAIN}/'
) )
) )
self.extract = self.content.split("</p>", 1)[0] + "</p>"


@property @property
def is_draft(self): def is_draft(self):
return self.date > date.today() return self.date > date.today()



@dataclass
class Note(Item):
def __post_init__(self):
suffix = len("/index.md")
prefix = len("YYYY/MM/DD") + suffix
date_str = self.file_path[-prefix:-suffix]
self.url = f"/david/stream/{date_str}/"
self.date = datetime.strptime(date_str, "%Y/%m/%d").date()
super().__post_init__()
self.extract = self.content.split("</p>", 1)[0] + "</p>"

@staticmethod @staticmethod
def all(source, only_published=True): def all(source, only_published=True):
"""Retrieve all (published) notes sorted by date desc.""" """Retrieve all (published) notes sorted by date desc."""
for file_path in each_markdown_from(source): for file_path in each_markdown_from(source):
title, content, _ = parse_markdown(file_path) title, content, _ = parse_markdown(file_path)
note = Note(title, content, file_path) note = Note(title, content, file_path)
if only_published and note.date > date.today():
if only_published and note.is_draft:
continue continue
note_list.append(note) note_list.append(note)
return sorted(note_list, key=attrgetter("date"), reverse=True) return sorted(note_list, key=attrgetter("date"), reverse=True)




@dataclass
class Post(Item):
date: str
slug: str
chapo: str
lang: str

def __post_init__(self):
self.url = f"/david/blog/{self.date.year}/{self.slug}/"
super().__post_init__()
self.url_image = f"/static/david/blog/{self.date.year}/{self.slug}.jpg"
self.url_image_thumbnail = (
f"/static/david/blog/{self.date.year}/thumbnails/{self.slug}.jpg"
)
self.full_img_url = f"{DOMAIN}{self.url_image}"
self.full_img_url_thumbnail = f"{DOMAIN}{self.url_image_thumbnail}"
self.escaped_content = self.escaped_content + escape(
f'<img src="{self.full_img_url_thumbnail}" width="500px" height="500px" />'
)
self.escaped_chapo = escape(self.chapo)

@staticmethod
def all(source, only_published=True):
"""Retrieve all (published) posts sorted by date desc."""
post_list = []
for file_path in each_markdown_from(source):
title, content, metadata = parse_markdown(file_path)
date = datetime.strptime(metadata["date"][0], "%Y-%m-%d").date()
slug = metadata["slug"][0]
chapo = metadata["chapo"][0]
lang = metadata.get("lang", ["fr"])[0]
post = Post(title, content, file_path, date, slug, chapo, lang)
if only_published and post.is_draft:
continue
post_list.append(post)
return sorted(post_list, key=attrgetter("date"), reverse=True)


@cli @cli
def note(when=None): def note(when=None):
"""Create a new note and open it in iA Writer. """Create a new note and open it in iA Writer.
template_article = environment.get_template("stream_2019_article.html") template_article = environment.get_template("stream_2019_article.html")
template_archives = environment.get_template("stream_2019_archives.html") template_archives = environment.get_template("stream_2019_archives.html")
# Default when you reach the last item. # Default when you reach the last item.
notes_2018 = Note(
title="Anciennes notes (2018)",
content="",
file_path="/david/stream/2018/12/31/index.md",
)
FakeNote = namedtuple("FakeNote", ["url", "title"])
notes_2018 = FakeNote(url="/david/stream/2018/", title="Anciennes notes (2018)")
note_base = DAVID / "stream" / "2019" note_base = DAVID / "stream" / "2019"
published = Note.all(source=note_base)
unpublished = Note.all(source=note_base, only_published=False) unpublished = Note.all(source=note_base, only_published=False)
published = [note for note in unpublished if not note.is_draft]
for previous, note, next_ in neighborhood(unpublished, last=notes_2018): for previous, note, next_ in neighborhood(unpublished, last=notes_2018):
if note.is_draft: if note.is_draft:
print(f"Soon: http://larlet.test:8001/{note.url} ({note.title})") print(f"Soon: http://larlet.test:8001/{note.url} ({note.title})")
print(f"Done: http://larlet.test:8001/{note_base}/") print(f"Done: http://larlet.test:8001/{note_base}/")




@cli
def blog():
"""Generate articles and archives for the blog."""
template_article = environment.get_template("blog_article.html")
template_archives = environment.get_template("blog_archives.html")
# Default when you reach the last item.
FakePost = namedtuple("FakePost", ["url", "title"])
posts_2012 = FakePost(
url="/david/thoughts/", title="Pensées précédentes (en anglais)"
)
post_base = DAVID / "blog"
unpublished = Post.all(source=post_base, only_published=False)
published = [post for post in unpublished if not post.is_draft]
published_en = [post for post in published if post.lang == "en"]
note_list = Note.all(source=DAVID / "stream" / "2019")
for previous, post, next_ in neighborhood(unpublished, last=posts_2012):
if post.is_draft:
print(f"Soon: http://larlet.test:8001{post.url} ({post.title})")
# Detect if there is code for syntax highlighting + monospaced font.
has_code = "<code>" in post.content
# Do not link to unpublished posts.
previous = previous and not previous.is_draft and previous or None
page_article = template_article.render(
post=post,
next=previous,
prev=next_,
has_code=has_code,
post_list=published,
published_posts_en=published_en,
note_list=note_list,
)
open(post_base / str(post.date.year) / post.slug / "index.html", "w",).write(
page_article
)

page_archive = template_archives.render(posts=published)
open(post_base / "index.html", "w").write(page_archive)
print(f"Done: http://larlet.test:8001/{post_base}/")


@cli @cli
def feed(): def feed():
"""Generate a feed from 15 last published Notes in stream."""
"""Generate a feed from last published items in stream."""
template = environment.get_template("feed.xml") template = environment.get_template("feed.xml")
content = template.render( content = template.render(
note_list=Note.all(source=DAVID / "stream" / "2019")[:15], note_list=Note.all(source=DAVID / "stream" / "2019")[:15],
post_list=Post.all(source=DAVID / "blog")[:5],
current_dt=datetime.now().strftime(NORMALIZED_STRFTIME), current_dt=datetime.now().strftime(NORMALIZED_STRFTIME),
BASE_URL=f"{DOMAIN}/david/", BASE_URL=f"{DOMAIN}/david/",
) )

Loading…
Cancel
Save