Просмотр исходного кода

Implementation of tags under /david/2021/

master
David Larlet 3 лет назад
Родитель
Сommit
62be8fff80
5 измененных файлов: 182 добавлений и 12 удалений
  1. 55
    0
      david/index.html
  2. 16
    0
      david/templates/article_2020.html
  3. 10
    0
      david/templates/profil.html
  4. 29
    0
      david/templates/tag_2021.html
  5. 72
    12
      site.py

+ 55
- 0
david/index.html Просмотреть файл

@@ -421,6 +421,61 @@
</p>
<h3 id="tags">
Par tags <svg class="icon icon-tags">
<use xlink:href="/static/david/icons2/symbol-defs.svg#icon-tags"></use>
</svg>
</h3>
<p>
<a href="/david/2021/accessibilite/">#accessibilité (16)</a>
<a href="/david/2021/accompagnement/">#accompagnement (21)</a>
<a href="/david/2021/allie/">#allié (12)</a>
<a href="/david/2021/alterego/">#alterego (3)</a>
<a href="/david/2021/anarchisme/">#anarchisme (4)</a>
<a href="/david/2021/anxiete/">#anxiété (30)</a>
<a href="/david/2021/capitalocene/">#capitalocène (21)</a>
<a href="/david/2021/climat/">#climat (13)</a>
<a href="/david/2021/contemplation/">#contemplation (7)</a>
<a href="/david/2021/cooperative/">#coopérative (6)</a>
<a href="/david/2021/design/">#design (9)</a>
<a href="/david/2021/ecole/">#école (8)</a>
<a href="/david/2021/ecriture/">#écriture (13)</a>
<a href="/david/2021/etiquette/">#étiquette (2)</a>
<a href="/david/2021/exploration/">#exploration (5)</a>
<a href="/david/2021/feminisme/">#féminisme (3)</a>
<a href="/david/2021/foret/">#forêt (39)</a>
<a href="/david/2021/frugalite/">#frugalité (6)</a>
<a href="/david/2021/gafam/">#gafam (14)</a>
<a href="/david/2021/gratitude/">#gratitude (15)</a>
<a href="/david/2021/inclassable/">#inclassable (4)</a>
<a href="/david/2021/incompetence/">#incompétence (33)</a>
<a href="/david/2021/lecture/">#lecture (11)</a>
<a href="/david/2021/materiel/">#matériel (4)</a>
<a href="/david/2021/misanthropie/">#misanthropie (23)</a>
<a href="/david/2021/neige/">#neige (7)</a>
<a href="/david/2021/open-source/">#open-source (12)</a>
<a href="/david/2021/pandemie/">#pandémie (38)</a>
<a href="/david/2021/parentalite/">#parentalité (11)</a>
<a href="/david/2021/partage/">#partage (23)</a>
<a href="/david/2021/pharmakon/">#pharmakon (10)</a>
<a href="/david/2021/poesie/">#poésie (4)</a>
<a href="/david/2021/premieres-nations/">#premières-nations (4)</a>
<a href="/david/2021/produit/">#produit (16)</a>
<a href="/david/2021/protopie/">#protopie (3)</a>
<a href="/david/2021/publication/">#publication (15)</a>
<a href="/david/2021/recherche/">#recherche (25)</a>
<a href="/david/2021/rythme/">#rythme (18)</a>
<a href="/david/2021/scopyleft/">#scopyleft (4)</a>
<a href="/david/2021/sociologie/">#sociologie (26)</a>
<a href="/david/2021/technique/">#technique (47)</a>
<a href="/david/2021/traces/">#traces (15)</a>
<a href="/david/2021/transformation/">#transformation (45)</a>
<a href="/david/2021/travail/">#travail (14)</a>
<a href="/david/2021/utilisateur-ice/">#utilisateur·ice (5)</a>
<a href="/david/2021/video/">#vidéo (20)</a>
<a href="/david/2021/web/">#web (23)</a>
</p>
</nav>

<h2>Publications 2020</h2>

+ 16
- 0
david/templates/article_2020.html Просмотреть файл

@@ -18,6 +18,22 @@
</nav>
<hr>
{{ page.content }}
{% if page.tags %}
<nav>
<p>
{% for tag in page.tags %}
<a href="/david/2021/{{ slugify(tag) }}/"
title="Liste de tous les articles associés à cette étiquette"
>#{{ tag }}</a>
{% endfor %}
<a href="/david/#tags"
title="Liste de toutes les étiquettes existantes"
><svg class="icon icon-tags">
<use xlink:href="/static/david/icons2/symbol-defs.svg#icon-tags"></use>
</svg> tous ?</a>
</p>
</nav>
{% endif%}
<nav>
<p class="center">
{% if prev and not prev.is_draft %}<a rel="prev" href="{{ prev.url }}" title="Publication précédente : {{ prev.title }}">← Précédent</a> •{% endif %}

+ 10
- 0
david/templates/profil.html Просмотреть файл

@@ -32,6 +32,16 @@
{% endfor %}
</p>
{% endfor %}
<h3 id="tags">
Par tags <svg class="icon icon-tags">
<use xlink:href="/static/david/icons2/symbol-defs.svg#icon-tags"></use>
</svg>
</h3>
<p>
{% for slug, name, counter in tags -%}
<a href="/david/2021/{{ slug }}/">#{{ name }} ({{ counter }})</a>
{% endfor %}
</p>
</nav>

<h2>Publications 2020</h2>

+ 29
- 0
david/templates/tag_2021.html Просмотреть файл

@@ -0,0 +1,29 @@
{% extends "base_2020.html" %}
{% block title %}Tag #{{ tag_name }}{% endblock %}
{% block description %}Publications relatives au tag #{{ tag_name }}{% endblock %}
{% block content %}
<header>
<h1>Publications relatives au tag #{{ tag_name }}</h1>
</header>
<nav>
<p class="center">
<a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
<use xlink:href="/static/david/icons2/symbol-defs.svg#icon-home"></use>
</svg> Accueil</a>
• <a rel="tags" href="/david/#tags" title="Liste de toutes les étiquettes"><svg class="icon icon-tags">
<use xlink:href="/static/david/icons2/symbol-defs.svg#icon-tags"></use>
</svg> Étiquettes</a>
</p>
</nav>
<hr>
<main>
<p>Liste des publications en ordre chronologique :</p>
{% for page in page_list %}
<h2><a href="{{ page.url }}" title="Lien permanent vers cet article">{{ page.title }}</a> ({{ page.date }})</h2>
<details>
<summary>Déplier pour lire le contenu de l’article</summary>
{{ page.content }}
</details>
{% endfor %}
</main>
{% endblock content %}

+ 72
- 12
site.py Просмотреть файл

@@ -2,11 +2,13 @@
import fnmatch
import locale
import os
from collections import defaultdict
from dataclasses import dataclass
from datetime import datetime, timedelta
from html import escape
from itertools import groupby
from pathlib import Path
from string import Template
from textwrap import dedent
from time import perf_counter

@@ -34,6 +36,8 @@ TODAY = datetime.today() + timedelta(hours=6)
PUBLICATION_BUFFER = TODAY - timedelta(days=7)
NB_ITEMS_IN_FEED = 30

all_tags = set()
pages_by_tags = defaultdict(list)
pages_by_url = {}


@@ -61,6 +65,20 @@ class MarkRenderer(mistune.HTMLRenderer):
return "<mark>" + text + "</mark>"


class TagsRenderer(mistune.HTMLRenderer):
"""Make the asumption each line starting with a `#` is a tag."""

def paragraph(self, text):
if text.startswith("#"):
tags = " ".join(
f'<a href="/david/2021/{slugify(tag.strip())}/">#{tag.strip()}</a>'
for tag in text.split("#")
if tag.strip()
)
return f"<nav><p>{tags}</p></nav>\n"
return super().paragraph(text)


class FrenchTypographyRenderer(mistune.HTMLRenderer):
"""Apply French typographic rules to text."""

@@ -89,7 +107,7 @@ class InternalLinkTitleRenderer(mistune.HTMLRenderer):


class CustomAndBlockquoteLanguageRenderer(
FrenchTypographyRenderer, InternalLinkTitleRenderer, MarkRenderer
FrenchTypographyRenderer, InternalLinkTitleRenderer, MarkRenderer, TagsRenderer
):
"""Sets the English language attribute for blockquotes with `[en]` prefix."""

@@ -114,7 +132,7 @@ class ImgsWithSizesRenderer(CustomAndBlockquoteLanguageRenderer):
# In case of a figure, we do not want the (non-standard) paragraph.
if text.strip().startswith("<figure>"):
return text
return f"<p>{text}</p>\n"
return super().paragraph(text)

def image(self, src, alt="", title=None):
full_path = STATIC / Path(src[1:])
@@ -224,6 +242,7 @@ def each_markdown_from(source_dir, file_name="*.md"):
class Page:
title: str
content: str
tags: list
file_path: str
lang: str = "fr"

@@ -234,15 +253,26 @@ class Page:
self.full_url = f"{DOMAIN}{self.url}"
self.normalized_date = self.date.strftime(NORMALIZED_STRFTIME)
self.escaped_title = escape(self.title)
tag_template = Template(
f'<a href="{DOMAIN}/david/2021/$tag_slug/">#$tag_name</a>'
)
tag_links = " ".join(
tag_template.substitute(tag_slug=slugify(tag), tag_name=tag)
for tag in self.tags
)
self.escaped_content = escape(
self.content.replace('href="/', f'href="{DOMAIN}/')
.replace('src="/', f'src="{DOMAIN}/')
.replace('href="#', f'href="{self.full_url}#')
+ f"<nav><p>{tag_links}</p></nav>"
+ '<hr/><p><a href="mailto:david@larlet.fr">Réagir ?</a></p>'
)
# Extract first paragraph.
self.extract = self.content.split("</p>", 1)[0] + "</p>"

def __eq__(self, other):
return self.url == other.url

def __lt__(self, other: "Page"):
if not isinstance(other, Page):
return NotImplemented
@@ -259,8 +289,24 @@ class Page:
title, content = result.split("</h1>", 1)
h1_opening_size = len("<h1>")
title = title[h1_opening_size:]
page = Page(title, content, file_name)
tags = {}
if "<nav><p>" in content:
# Extract the tags from the generated page.
content, tags_links = content.split("<nav><p>", 1)
nav_closing_size = len("</p></nav>\n")
tags_links = tags_links[:-nav_closing_size]
tags = {
tag.strip().split("#", 1)[1]
for tag in tags_links.split("</a>")
if tag.strip()
}
page = Page(title, content, tags, file_name)
pages_by_url[page.url] = page
if not page.is_draft:
all_tags.update(tags)
for tag in tags:
if page not in pages_by_tags[tag]:
pages_by_tags[tag].append(page)
if only_published and page.is_draft:
continue
page_list.append(page)
@@ -276,7 +322,7 @@ class Page:

@cli
def pages():
"""Build the agregations from fragments."""
"""Build article pages."""
root_path = DAVID / "2021"
source_path = root_path / "sources"
for previous, page, next_ in neighborhood(
@@ -288,11 +334,7 @@ def pages():
},
):
template = environment.get_template("article_2020.html")
content = template.render(
page=page,
prev=previous,
next=next_,
)
content = template.render(page=page, prev=previous, next=next_, slugify=slugify)
target_path = Path(page.url[1:])
target_path.mkdir(parents=True, exist_ok=True)
open(target_path / "index.html", "w").write(content)
@@ -303,6 +345,24 @@ def pages():
open(root_path / "index.html", "w").write(content)


@cli
def tags():
"""Build tags pages."""
root_path = DAVID / "2021"
source_path = root_path / "sources"
# Parse all pages to collect tags.
Page.all(source=source_path, only_published=True)
for tag in all_tags:
template = environment.get_template("tag_2021.html")
content = template.render(
page_list=pages_by_tags[tag],
tag_name=tag,
)
target_path = DAVID / "2021" / slugify(tag)
target_path.mkdir(parents=True, exist_ok=True)
open(target_path / "index.html", "w").write(content)


@cli
def home():
"""Build the home page with last published items."""
@@ -311,10 +371,10 @@ def home():
return item.date.strftime("%B %Y").title()

template = environment.get_template("profil.html")
page_list = Page.all(source=DAVID / "2021" / "sources")
tags = sorted((slugify(tag), tag, len(pages_by_tags[tag])) for tag in all_tags)
content = template.render(
page_list=groupby(
Page.all(source=DAVID / "2021" / "sources"), key=group_by_month_year
),
page_list=groupby(page_list, key=group_by_month_year), tags=tags
)
open(DAVID / "index.html", "w").write(content)


Загрузка…
Отмена
Сохранить