125 lines
5.1 KiB
Python
125 lines
5.1 KiB
Python
"""Regenere DOCS/kanban.html depuis DOCS/kanban/.
|
|
|
|
python3 DOCS/tools/build_kanban.py
|
|
|
|
Les compteurs de colonne, le bandeau de chiffres cles et le « Fait recemment »
|
|
sont calcules a partir du nombre de fichiers : ils ne peuvent plus se
|
|
desynchroniser du contenu reel.
|
|
|
|
Ajouter un chantier : creer un .md dans kanban/cards/<colonne>/
|
|
Deplacer un chantier : deplacer le fichier vers un autre dossier de colonne
|
|
Clore un chantier : deplacer le fichier vers kanban/done/ (ne garder que « title »)
|
|
L'ordre d'affichage suit le prefixe numerique du nom de fichier (pas de 10).
|
|
"""
|
|
import io
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
DOCS = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
KANBAN = os.path.join(DOCS, "kanban")
|
|
CARDS_DIR = os.path.join(KANBAN, "cards")
|
|
DONE_DIR = os.path.join(KANBAN, "done")
|
|
TPL = os.path.join(DOCS, "kanban-template.html")
|
|
OUT = os.path.join(DOCS, "kanban.html")
|
|
|
|
|
|
def read_item(path):
|
|
raw = io.open(path, encoding="utf-8", newline="").read().replace("\r\n", "\n")
|
|
if not raw.startswith("---\n") or "\n---\n" not in raw:
|
|
sys.exit("%s : frontmatter manquant ou mal ferme" % os.path.basename(path))
|
|
front, body = raw[4:].split("\n---\n", 1)
|
|
item = {"body": body.strip(), "tags": [], "flag": None, "horizon": None}
|
|
for line in front.strip().split("\n"):
|
|
key, _, value = line.partition(":")
|
|
key, value = key.strip(), value.strip()
|
|
if key == "tags":
|
|
item["tags"] = [t.strip() for t in value.split(",") if t.strip()]
|
|
elif key == "flag":
|
|
level, _, text = value.partition("|")
|
|
item["flag"] = {"level": level.strip(), "text": text.strip()}
|
|
else:
|
|
item[key] = value
|
|
if not item.get("title"):
|
|
sys.exit("%s : champ « title » manquant" % os.path.basename(path))
|
|
return item
|
|
|
|
|
|
def md_files(folder):
|
|
return [os.path.join(folder, f) for f in sorted(os.listdir(folder)) if f.endswith(".md")]
|
|
|
|
|
|
def render_card(card, path):
|
|
for required in ("area", "src"):
|
|
if not card.get(required):
|
|
sys.exit("%s : champ « %s » manquant" % (os.path.basename(path), required))
|
|
attrs = ' data-area="%s"' % card["area"]
|
|
if card["horizon"]:
|
|
attrs += ' data-horizon="%s"' % card["horizon"]
|
|
meta = "".join('<span class="tag">%s</span>' % t for t in card["tags"])
|
|
if card["flag"]:
|
|
meta += '<span class="flag f-%s">%s</span>' % (card["flag"]["level"], card["flag"]["text"])
|
|
body = "\n ".join(card["body"].split("\n"))
|
|
return (
|
|
' <article class="card"%s>\n'
|
|
' <div class="card-meta">%s</div>\n'
|
|
" <h3>%s</h3>\n"
|
|
" %s\n"
|
|
' <span class="src">%s</span>\n'
|
|
" </article>"
|
|
) % (attrs, meta, card["title"], body, card["src"])
|
|
|
|
|
|
board = json.load(io.open(TPL.replace("kanban-template.html", "kanban/board.json"), encoding="utf-8"))
|
|
|
|
sections, counts = [], []
|
|
for col in board["columns"]:
|
|
paths = md_files(os.path.join(CARDS_DIR, col["dir"]))
|
|
cards = [render_card(read_item(p), p) for p in paths]
|
|
counts.append((col, len(cards)))
|
|
sections.append(
|
|
" <!-- %s -->\n"
|
|
' <section class="col" style="--stripe: var(--%s)">\n'
|
|
' <div class="col-head"><h2>%s</h2><span class="count">%d</span></div>\n'
|
|
' <div class="stack">\n\n%s\n\n </div>\n'
|
|
" </section>" % (col["comment"], col["stripe"], col["title"], len(cards), "\n\n".join(cards))
|
|
)
|
|
|
|
done_items = [read_item(p) for p in md_files(DONE_DIR)]
|
|
done_html = "\n\n".join(
|
|
' <div class="done-item">\n <strong>%s</strong>\n %s\n </div>'
|
|
% (it["title"], "\n ".join(it["body"].split("\n")))
|
|
for it in done_items
|
|
)
|
|
done = board["done"]
|
|
done_section = (
|
|
'<section class="done">\n <h2>%s</h2>\n <p>%s</p>\n <div class="done-grid">\n\n%s\n\n </div>\n </section>'
|
|
% (done["heading"], done["intro"], done_html)
|
|
)
|
|
|
|
stats = [
|
|
' <div class="stat"><span class="n%s">%d</span><span class="k">%s</span></div>'
|
|
% ((" " + col["statClass"]) if col["statClass"] else "", n, col["title"])
|
|
for col, n in counts
|
|
]
|
|
stats.append(
|
|
' <div class="stat"><span class="n %s">%d</span><span class="k">%s</span></div>'
|
|
% (done["statClass"], len(done_items), done["statLabel"])
|
|
)
|
|
|
|
html = io.open(TPL, encoding="utf-8", newline="").read().replace("\r\n", "\n")
|
|
html = html.replace("{{INTRO}}", "\n".join(" <p>%s</p>" % p for p in board["intro"]))
|
|
html = html.replace("{{STAMP}}", "Mise à jour · %s<br>\n %s" % (board["updated"], board["stampNote"]))
|
|
html = html.replace(
|
|
"{{SUMMARY}}", ' <section class="summary" aria-label="Chiffres clés">\n%s\n </section>' % "\n".join(stats)
|
|
)
|
|
html = html.replace("{{BOARD}}", '<div class="board">\n\n%s\n\n </div>' % "\n\n".join(sections))
|
|
html = html.replace("{{DONE}}", done_section)
|
|
|
|
io.open(OUT, "w", encoding="utf-8", newline="\n").write(html)
|
|
|
|
print("kanban.html : %d o" % os.path.getsize(OUT))
|
|
print("%d chantiers ouverts + %d clos" % (sum(n for _, n in counts), len(done_items)))
|
|
for col, n in counts:
|
|
print(" %-14s %2d" % (col["title"], n))
|