Add simple mechanism for fast re-render.

This commit is contained in:
Sergio Martínez Portela 2022-06-27 21:02:33 +02:00
parent a8c4d6ef48
commit 57ed8fa15c
2 changed files with 89 additions and 13 deletions

View File

@ -17,12 +17,15 @@ import logging
import sys import sys
import os import os
import datetime import datetime
import jinja2
import shutil import shutil
import traceback
import time
import re
import jinja2
import inotify.adapters
import yaml import yaml
import markdown import markdown
import re
from unidecode import unidecode from unidecode import unidecode
NIKOLA_DATE_RE = re.compile(r'^([0-2]\d|30|31)\.(0\d|1[012])\.(\d{4}), (\d{1,2}):(\d{2})$') NIKOLA_DATE_RE = re.compile(r'^([0-2]\d|30|31)\.(0\d|1[012])\.(\d{4}), (\d{1,2}):(\d{2})$')
@ -44,7 +47,24 @@ JINJA_ENV = jinja2.Environment(
loader=jinja2.FileSystemLoader(STATIC_PATH), loader=jinja2.FileSystemLoader(STATIC_PATH),
autoescape=jinja2.select_autoescape() autoescape=jinja2.select_autoescape()
) )
ARTICLE_TEMPLATE = JINJA_ENV.get_template(ARTICLE_TEMPLATE_NAME)
def update_statics():
global ARTICLE_TEMPLATE
ARTICLE_TEMPLATE = JINJA_ENV.get_template(ARTICLE_TEMPLATE_NAME)
update_statics()
MONITORED_EVENT_TYPES = (
'IN_CREATE',
# 'IN_MODIFY',
'IN_CLOSE_WRITE',
'IN_DELETE',
'IN_MOVED_FROM',
'IN_MOVED_TO',
'IN_DELETE_SELF',
'IN_MOVE_SELF',
)
def parse_nikola_date(match): def parse_nikola_date(match):
return datetime.datetime(year=int(match.group(3)), return datetime.datetime(year=int(match.group(3)),
@ -53,9 +73,11 @@ def parse_nikola_date(match):
hour=int(match.group(4)), hour=int(match.group(4)),
minute=int(match.group(5))) minute=int(match.group(5)))
def parse_complete_date(match): def parse_complete_date(match):
return datetime.datetime.strptime(match.group(0), '%Y-%m-%d %H:%M:%S %Z%z') return datetime.datetime.strptime(match.group(0), '%Y-%m-%d %H:%M:%S %Z%z')
def slugify(title): def slugify(title):
""" """
Made for compatibility with Nikola's slugify within CodigoParaLlevar blog. Made for compatibility with Nikola's slugify within CodigoParaLlevar blog.
@ -66,6 +88,7 @@ def slugify(title):
return slug.strip() return slug.strip()
def read_markdown(path): def read_markdown(path):
with open(path, 'rt') as f: with open(path, 'rt') as f:
data = f.read() data = f.read()
@ -112,7 +135,7 @@ def get_out_path(front_matter):
def load_all(top_dir_relative): def load_all(top_dir_relative):
top = os.path.abspath(top_dir_relative) top = os.path.abspath(top_dir_relative)
docs = [] docs = {}
for root, dirs, files in os.walk(top): for root, dirs, files in os.walk(top):
for name in files: for name in files:
@ -124,29 +147,82 @@ def load_all(top_dir_relative):
path = os.path.join(root, name) path = os.path.join(root, name)
doc, front_matter = read_markdown(path) doc, front_matter = read_markdown(path)
out_path = get_out_path(front_matter) out_path = get_out_path(front_matter)
docs.append((doc, front_matter, out_path)) docs[path] = (doc, front_matter, out_path)
else: else:
raise NotImplementedError('Unknown filetype: {}'.format(name)) raise NotImplementedError('Unknown filetype: {}'.format(name))
return docs return docs
def render_article(doc, f):
result = ARTICLE_TEMPLATE.render(content=doc) def load_doc(filepath):
doc, front_matter = read_markdown(filepath)
out_path = get_out_path(front_matter)
return (doc, front_matter, out_path)
def render_article(doc, front_matter, f):
result = ARTICLE_TEMPLATE.render(content=doc, title=front_matter['title'])
f.write(result) f.write(result)
def main(source_top, dest_top): def regen_all(source_top, dest_top, docs=None):
if docs is None:
docs = load_all(source_top) docs = load_all(source_top)
for (doc, front_matter, out_path) in docs: for (doc, front_matter, out_path) in docs.values():
doc_full_path = os.path.join(dest_top, out_path) doc_full_path = os.path.join(dest_top, out_path)
os.makedirs(os.path.dirname(doc_full_path), exist_ok=True) os.makedirs(os.path.dirname(doc_full_path), exist_ok=True)
print("==", doc_full_path) # print("==", doc_full_path)
with open(doc_full_path + '.html', 'wt') as f: with open(doc_full_path + '.html', 'wt') as f:
render_article(doc, f) render_article(doc, front_matter, f)
for src, dest in STATIC_RESOURCES: for src, dest in STATIC_RESOURCES:
target_dest = os.path.join(dest_top, dest) target_dest = os.path.join(dest_top, dest)
os.makedirs(os.path.dirname(target_dest), exist_ok=True) os.makedirs(os.path.dirname(target_dest), exist_ok=True)
shutil.copy(os.path.join(STATIC_PATH, src), target_dest) shutil.copy(os.path.join(STATIC_PATH, src), target_dest)
return docs
def main(source_top, dest_top):
notifier = inotify.adapters.InotifyTrees([source_top, STATIC_PATH])
## Initial load
t0 = time.time()
logging.info("Initial load...")
docs = regen_all(source_top, dest_top)
logging.info("Initial load completed in {:.2f}s".format(time.time() - t0))
## Updating
for event in notifier.event_gen(yield_nones=False):
(ev, types, directory, file) = event
if not any([type in MONITORED_EVENT_TYPES for type in types]):
continue
filepath = os.path.join(directory, file)
if filepath.startswith(STATIC_PATH):
t0 = time.time()
update_statics()
for src, dest in STATIC_RESOURCES:
target_dest = os.path.join(dest_top, dest)
os.makedirs(os.path.dirname(target_dest), exist_ok=True)
shutil.copy(os.path.join(STATIC_PATH, src), target_dest)
docs = regen_all(source_top, dest_top, docs)
logging.info("Updated all in {:.2f}s".format(time.time() - t0))
else:
try:
(doc, front_matter, out_path) = load_doc(filepath)
except:
logging.error(traceback.format_exc())
logging.error("Skipping update 😿")
continue
t0 = time.time()
docs[filepath] = (doc, front_matter, out_path)
doc_full_path = os.path.join(dest_top, out_path)
os.makedirs(os.path.dirname(doc_full_path), exist_ok=True)
# print("==", doc_full_path)
with open(doc_full_path + '.html', 'wt') as f:
render_article(doc, front_matter, f)
logging.info("Updated all in {:.2f}s".format(time.time() - t0))
if __name__ == "__main__": if __name__ == "__main__":
if len(sys.argv) != 3: if len(sys.argv) != 3:

View File

@ -7,8 +7,8 @@
<link rel="stylesheet" href="../css/style.css" /> <link rel="stylesheet" href="../css/style.css" />
</head> </head>
<body> <body>
<article> <article>
<h2 class="post-title">{{ title }}</h2>
{{ content | safe }} {{ content | safe }}
</article> </article>
</body> </body>