pull/1/head
Yax 5 years ago
parent 24f3390ba4
commit 64ce3132a6

1
.gitignore vendored

@ -6,3 +6,4 @@ htmlcov
*.sw? *.sw?
.DS_Store .DS_Store
venv venv
.vscode

@ -0,0 +1 @@
<h2>Catégorie : {{ category }}</h2>

@ -48,7 +48,7 @@
<div class="pure-u-1-12"></div> <div class="pure-u-1-12"></div>
</div> </div>
{{ banner }} {{ header }}
<div class="pure-g"> <div class="pure-g">
<div class="pure-u-1-4"></div> <div class="pure-u-1-4"></div>
@ -56,6 +56,8 @@
<div class="pure-u-1-4"></div> <div class="pure-u-1-4"></div>
</div> </div>
{{ footer }}
<footer> <footer>
<section> <section>
<p> <p>

@ -0,0 +1 @@
<p>page <a href="{{ previous_page }}">précédente</a> / page <a href="{{ next_page }}">suivante</a></p>

@ -41,11 +41,12 @@ import unicodedata
import locale import locale
# set user locale # set user locale
locale.setlocale(locale.LC_ALL, '') locale.setlocale(locale.LC_ALL, "")
def fread(filename): def fread(filename):
"""Read file and close the file.""" """Read file and close the file."""
with open(filename, 'r') as f: with open(filename, "r") as f:
return f.read() return f.read()
@ -55,23 +56,23 @@ def fwrite(filename, text):
if not os.path.isdir(basedir): if not os.path.isdir(basedir):
os.makedirs(basedir) os.makedirs(basedir)
with open(filename, 'w') as f: with open(filename, "w") as f:
f.write(text) f.write(text)
def log(msg, *args): def log(msg, *args):
"""Log message with specified arguments.""" """Log message with specified arguments."""
sys.stderr.write(msg.format(*args) + '\n') sys.stderr.write(msg.format(*args) + "\n")
def truncate(text, words=25): def truncate(text, words=25):
"""Remove tags and truncate text to the specified number of words.""" """Remove tags and truncate text to the specified number of words."""
return ' '.join(re.sub('(?s)<.*?>', ' ', text).split()[:words]) return " ".join(re.sub("(?s)<.*?>", " ", text).split()[:words])
def read_headers(text): def read_headers(text):
"""Parse headers in text and yield (key, value, end-index) tuples.""" """Parse headers in text and yield (key, value, end-index) tuples."""
for match in re.finditer(r'\s*<!--\s*(.+?)\s*:\s*(.+?)\s*-->\s*|.+', text): for match in re.finditer(r"\s*<!--\s*(.+?)\s*:\s*(.+?)\s*-->\s*|.+", text):
if not match.group(1): if not match.group(1):
break break
yield match.group(1), match.group(2), match.end() yield match.group(1), match.group(2), match.end()
@ -79,7 +80,7 @@ def read_headers(text):
def rfc_2822_format(date_str): def rfc_2822_format(date_str):
"""Convert yyyy-mm-dd date string to RFC 2822 format date string.""" """Convert yyyy-mm-dd date string to RFC 2822 format date string."""
d = datetime.datetime.strptime(date_str, '%Y-%m-%d') d = datetime.datetime.strptime(date_str, "%Y-%m-%d")
dtuple = d.timetuple() dtuple = d.timetuple()
dtimestamp = time.mktime(dtuple) dtimestamp = time.mktime(dtuple)
return utils.formatdate(dtimestamp) return utils.formatdate(dtimestamp)
@ -91,9 +92,11 @@ def slugify(value):
underscores) and converts spaces to hyphens. Also strips leading and underscores) and converts spaces to hyphens. Also strips leading and
trailing whitespace. trailing whitespace.
""" """
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii') value = (
value = re.sub('[^\w\s-]', '', value).strip().lower() unicodedata.normalize("NFKD", value).encode("ascii", "ignore").decode("ascii")
return re.sub('[-\s]+', '-', value) )
value = re.sub("[^\w\s-]", "", value).strip().lower()
return re.sub("[-\s]+", "-", value)
def read_content(filename): def read_content(filename):
@ -102,12 +105,9 @@ def read_content(filename):
text = fread(filename) text = fread(filename)
# Read metadata and save it in a dictionary. # Read metadata and save it in a dictionary.
date_slug = os.path.basename(filename).split('.')[0] date_slug = os.path.basename(filename).split(".")[0]
match = re.search(r'^(?:(\d\d\d\d-\d\d-\d\d)-)?(.+)$', date_slug) match = re.search(r"^(?:(\d\d\d\d-\d\d-\d\d)-)?(.+)$", date_slug)
content = { content = {"date": match.group(1) or "1970-01-01", "slug": match.group(2)}
'date': match.group(1) or '1970-01-01',
'slug': match.group(2),
}
# Read headers. # Read headers.
end = 0 end = 0
@ -118,20 +118,18 @@ def read_content(filename):
text = text[end:] text = text[end:]
# Convert Markdown content to HTML. # Convert Markdown content to HTML.
if filename.endswith(('.md', '.mkd', '.mkdn', '.mdown', '.markdown')): if filename.endswith((".md", ".mkd", ".mkdn", ".mdown", ".markdown")):
try: try:
if _test == 'ImportError': if _test == "ImportError":
raise ImportError('Error forced by test') raise ImportError("Error forced by test")
import commonmark import commonmark
text = commonmark.commonmark(text) text = commonmark.commonmark(text)
except ImportError as e: except ImportError as e:
log('WARNING: Cannot render Markdown in {}: {}', filename, str(e)) log("WARNING: Cannot render Markdown in {}: {}", filename, str(e))
# Update the dictionary with content and RFC 2822 date. # Update the dictionary with content and RFC 2822 date.
content.update({ content.update({"content": text, "rfc_2822_date": rfc_2822_format(content["date"])})
'content': text,
'rfc_2822_date': rfc_2822_format(content['date'])
})
return content return content
@ -140,8 +138,8 @@ def clean_html_tag(text):
"""Remove HTML tags.""" """Remove HTML tags."""
while True: while True:
original_text = text original_text = text
text = re.sub('<\w+.*?>', '', text) text = re.sub("<\w+.*?>", "", text)
text = re.sub('<\/\w+>', '', text) text = re.sub("<\/\w+>", "", text)
if original_text == text: if original_text == text:
break break
return text return text
@ -149,14 +147,16 @@ def clean_html_tag(text):
def render(template, **params): def render(template, **params):
"""Replace placeholders in template with values from params.""" """Replace placeholders in template with values from params."""
return re.sub(r'{{\s*([^}\s]+)\s*}}', return re.sub(
r"{{\s*([^}\s]+)\s*}}",
lambda match: str(params.get(match.group(1), match.group(0))), lambda match: str(params.get(match.group(1), match.group(0))),
template) template,
)
def get_categories(page_params): def get_categories(page_params):
cat = [] cat = []
for s in page_params['category'].split(' '): for s in page_params["category"].split(" "):
if s.strip(): if s.strip():
cat.append(s.strip()) cat.append(s.strip())
return cat return cat
@ -170,27 +170,20 @@ def make_pages(src, dst, layout, **params):
content = read_content(src_path) content = read_content(src_path)
page_params = dict(params, **content) page_params = dict(params, **content)
# Populate placeholders in content if content-rendering is enabled.
# if page_params.get('render') == 'yes':
# rendered_content = render(page_params['content'], **page_params)
# page_params['content'] = rendered_content
# content['content'] = rendered_content
items.append(content) items.append(content)
dst_path = render(dst, **page_params) dst_path = render(dst, **page_params)
output = render(layout, **page_params) output = render(layout, **page_params)
log('Rendering {} => {} ...', src_path, dst_path) log("Rendering {} => {} ...", src_path, dst_path)
fwrite(dst_path, output) fwrite(dst_path, output)
return sorted(items, key=lambda x: x['date'], reverse=True) return sorted(items, key=lambda x: x["date"], reverse=True)
def get_friendly_date(date_str): def get_friendly_date(date_str):
dt = datetime.datetime.strptime(date_str, '%Y-%m-%d') dt = datetime.datetime.strptime(date_str, "%Y-%m-%d")
return dt.strftime('%d %b %Y') return dt.strftime("%d %b %Y")
def make_posts(src, src_pattern, dst, layout, category_layout, **params): def make_posts(src, src_pattern, dst, layout, category_layout, **params):
@ -202,10 +195,11 @@ def make_posts(src, src_pattern, dst, layout, category_layout, **params):
content = read_content(src_path) content = read_content(src_path)
page_params = dict(params, **content) page_params = dict(params, **content)
page_params['banner'] =' ' page_params["header"] = ""
page_params['date_path'] = page_params['date'].replace('-', '/') page_params["footer"] = ""
page_params['friendly_date'] = get_friendly_date(page_params['date']) page_params["date_path"] = page_params["date"].replace("-", "/")
page_params['year'] = page_params['date'].split('-')[0] page_params["friendly_date"] = get_friendly_date(page_params["date"])
page_params["year"] = page_params["date"].split("-")[0]
# categories # categories
categories = get_categories(page_params) categories = get_categories(page_params)
@ -213,135 +207,199 @@ def make_posts(src, src_pattern, dst, layout, category_layout, **params):
for category in categories: for category in categories:
out_cat = render(category_layout, category=category, url=slugify(category)) out_cat = render(category_layout, category=category, url=slugify(category))
out_cats.append(out_cat.strip()) out_cats.append(out_cat.strip())
page_params['categories'] = categories page_params["categories"] = categories
page_params['category_label'] = ''.join(out_cats) page_params["category_label"] = "".join(out_cats)
summary_index = page_params['content'].find('<!-- more') summary_index = page_params["content"].find("<!-- more")
if summary_index > 0: if summary_index > 0:
content['summary'] = clean_html_tag(render(page_params['content'][:summary_index], **page_params)) content["summary"] = clean_html_tag(
render(page_params["content"][:summary_index], **page_params)
content['year'] = page_params['year'] )
content['categories'] = page_params['categories']
content['category_label'] = page_params['category_label'] content["year"] = page_params["year"]
content['friendly_date'] = page_params['friendly_date'] content["categories"] = page_params["categories"]
content["category_label"] = page_params["category_label"]
content["friendly_date"] = page_params["friendly_date"]
items.append(content) items.append(content)
# TODO DEBUG # TODO DEBUG
#print(page_params) # print(page_params)
#print(content) # print(content)
#break # break
dst_path = render(dst, **page_params) dst_path = render(dst, **page_params)
output = render(layout, **page_params) output = render(layout, **page_params)
log('Rendering {} => {} ...', src_path, dst_path) log("Rendering {} => {} ...", src_path, dst_path)
fwrite(dst_path, output) fwrite(dst_path, output)
return sorted(items, key=lambda x: x['date'], reverse=True) return sorted(items, key=lambda x: x["date"], reverse=True)
def make_list(posts, dst, list_layout, item_layout, banner_layout, **params): def make_list(
posts, dst, list_layout, item_layout, header_layout, footer_layout, **params
):
"""Generate list page for a blog.""" """Generate list page for a blog."""
# header
if header_layout is None:
params["header"] = ""
else:
header = render(header_layout, **params)
params["header"] = header
# footer
if footer_layout is None:
params["footer"] = ""
else:
footer = render(footer_layout, **params)
params["footer"] = footer
# content
items = [] items = []
for post in posts: for post in posts:
item_params = dict(params, **post) item_params = dict(params, **post)
if 'summary' not in item_params: if "summary" not in item_params:
item_params['summary'] = truncate(post['content']) item_params["summary"] = truncate(post["content"])
item = render(item_layout, **item_params) item = render(item_layout, **item_params)
items.append(item) items.append(item)
params["content"] = "".join(items)
if banner_layout is None:
params['banner'] = ''
else:
banner = render(banner_layout, **params)
params['banner'] = banner
params['content'] = ''.join(items)
dst_path = render(dst, **params) dst_path = render(dst, **params)
output = render(list_layout, **params) output = render(list_layout, **params)
log('Rendering list => {} ...', dst_path) log("Rendering list => {} ...", dst_path)
fwrite(dst_path, output) fwrite(dst_path, output)
def main(): def main():
# Create a new _site directory from scratch. # Create a new _site directory from scratch.
if os.path.isdir('_site'): if os.path.isdir("_site"):
shutil.rmtree('_site') shutil.rmtree("_site")
shutil.copytree('static', '_site') shutil.copytree("static", "_site")
# Default parameters. # Default parameters.
params = { params = {
'title': 'Blog', "title": "Blog",
'subtitle': 'Lorem Ipsum', "subtitle": "Lorem Ipsum",
'author': 'Admin', "author": "Admin",
'site_url': 'http://localhost:8000', "site_url": "http://localhost:8000",
'current_year': datetime.datetime.now().year "current_year": datetime.datetime.now().year,
} }
# If params.json exists, load it. # If params.json exists, load it.
if os.path.isfile('params.json'): if os.path.isfile("params.json"):
params.update(json.loads(fread('params.json'))) params.update(json.loads(fread("params.json")))
# Load layouts. # Load layouts.
page_layout = fread('layout/page.html') banner_layout = fread("layout/banner.html")
post_layout = fread('layout/post.html') paging_layout = fread("layout/paging.html")
list_layout = fread('layout/list.html') category_title_layout = fread("layout/category_title.html")
item_layout = fread('layout/item.html') page_layout = fread("layout/page.html")
banner_layout = fread('layout/banner.html') post_layout = fread("layout/post.html")
category_layout = fread('layout/category.html') list_layout = fread("layout/list.html")
rss_xml = fread('layout/rss.xml') item_layout = fread("layout/item.html")
rss_item_xml = fread('layout/rss_item.xml') category_layout = fread("layout/category.html")
sitemap_xml = fread('layout/sitemap.xml') rss_xml = fread("layout/rss.xml")
sitemap_item_xml = fread('layout/sitemap_item.xml') rss_item_xml = fread("layout/rss_item.xml")
sitemap_xml = fread("layout/sitemap.xml")
sitemap_item_xml = fread("layout/sitemap_item.xml")
# Combine layouts to form final layouts. # Combine layouts to form final layouts.
post_layout = render(page_layout, content=post_layout) post_layout = render(page_layout, content=post_layout)
list_layout = render(page_layout, content=list_layout) list_layout = render(page_layout, content=list_layout)
# Create blogs. # Create blogs.
blog_posts = make_posts('posts', '**/*.md', blog_posts = make_posts(
'_site/{{ year }}/{{ slug }}.html', "posts",
post_layout, category_layout, **params) "**/*.md",
"_site/{{ year }}/{{ slug }}.html",
post_layout,
category_layout,
**params
)
# Create blog list pages.
page_size = 10 page_size = 10
chunk_posts = [blog_posts[i : i + page_size] for i in range(0, len(blog_posts), page_size)]
page = 1 page = 1
for chunk in [blog_posts[i:i + page_size] for i in range(0, len(blog_posts), page_size)]: last_page = len(chunk_posts)
params['page'] = page for chunk in chunk_posts:
make_list(chunk, '_site/page' + str(page) + '.html', params["page"] = page
list_layout, item_layout, banner_layout, **params) if page == last_page:
params['next_page'] = ''
else:
params['next_page'] = 'page' + str(page + 1) + '.html'
if page == 1:
params['previous_page'] = ''
make_list(
chunk,
"_site/index.html",
list_layout,
item_layout,
banner_layout,
paging_layout,
**params
)
else:
params['previous_page'] = 'page' + str(page - 1) + '.html'
make_list(
chunk,
"_site/page" + str(page) + ".html",
list_layout,
item_layout,
banner_layout,
paging_layout,
**params
)
page = page + 1 page = page + 1
# Create blog list pages.
make_list(blog_posts, '_site/index.html',
list_layout, item_layout, banner_layout, **params)
# Create category pages # Create category pages
catpost = {} catpost = {}
for post in blog_posts: for post in blog_posts:
for cat in post['categories']: for cat in post["categories"]:
if cat in catpost: if cat in catpost:
catpost[cat].append(post) catpost[cat].append(post)
else: else:
catpost[cat] = [post] catpost[cat] = [post]
for cat in catpost.keys(): for cat in catpost.keys():
make_list(catpost[cat], '_site/' + slugify(cat) + '.html', params["category"] = cat
list_layout, item_layout, None, **params) make_list(
catpost[cat],
#print(blog_posts) "_site/" + slugify(cat) + ".html",
list_layout,
item_layout,
category_title_layout,
None,
**params
)
# Create RSS feeds. # Create RSS feeds.
nb_items = min(10, len(blog_posts)) nb_items = min(10, len(blog_posts))
make_list(blog_posts[:nb_items], '_site/rss.xml', make_list(
rss_xml, rss_item_xml, None, **params) blog_posts[:nb_items],
"_site/rss.xml",
rss_xml,
rss_item_xml,
None,
None,
**params
)
# Create sitemap # Create sitemap
make_list(blog_posts, '_site/sitemap.xml', make_list(
sitemap_xml, sitemap_item_xml, None, **params) blog_posts,
"_site/sitemap.xml",
sitemap_xml,
sitemap_item_xml,
None,
None,
**params
)
# Test parameter to be set temporarily by unit tests. # Test parameter to be set temporarily by unit tests.
_test = None _test = None
if __name__ == '__main__': if __name__ == "__main__":
main() main()

Loading…
Cancel
Save