134 lines
5.6 KiB
Python
134 lines
5.6 KiB
Python
"""Extraction unique : kanban.html -> DOCS/kanban/ + kanban-template.html.
|
|
|
|
Une carte = un fichier, un « fait recemment » = un fichier. Ensuite kanban.html
|
|
est regenere par build_kanban.py et ne s'edite plus a la main.
|
|
"""
|
|
import io
|
|
import json
|
|
import os
|
|
import re
|
|
import unicodedata
|
|
|
|
DOCS = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
SRC = os.path.join(DOCS, "kanban.html")
|
|
KANBAN = os.path.join(DOCS, "kanban")
|
|
CARDS_DIR = os.path.join(KANBAN, "cards")
|
|
DONE_DIR = os.path.join(KANBAN, "done")
|
|
BOARD_OUT = os.path.join(KANBAN, "board.json")
|
|
TPL_OUT = os.path.join(DOCS, "kanban-template.html")
|
|
|
|
|
|
def write(path, text):
|
|
io.open(path, "w", encoding="utf-8", newline="\n").write(text)
|
|
|
|
|
|
def slug(text, maxlen=45):
|
|
text = re.sub(r"<[^>]+>", "", text)
|
|
text = unicodedata.normalize("NFKD", text).encode("ascii", "ignore").decode()
|
|
text = re.sub(r"[^a-zA-Z0-9]+", "-", text).strip("-").lower()
|
|
return text[:maxlen].rstrip("-") or "sans-titre"
|
|
|
|
|
|
html = io.open(SRC, encoding="utf-8", newline="").read()
|
|
|
|
masthead = re.search(r'<header class="masthead">(.*?)</header>', html, re.S).group(1)
|
|
intro = [p.strip() for p in re.findall(r"<p>(.*?)</p>", masthead, re.S)]
|
|
stamp = re.search(r'<div class="stamp">(.*?)</div>', masthead, re.S).group(1).strip()
|
|
|
|
summary_block = re.search(r'<section class="summary".*?</section>', html, re.S).group(0)
|
|
stats = re.findall(r'<span class="n([^"]*)">(\d+)</span><span class="k">([^<]*)</span>', summary_block)
|
|
stat_class = {k: cls.strip() for cls, n, k in stats}
|
|
|
|
done_start = html.index('<section class="done">')
|
|
board_start = html.index('<div class="board">')
|
|
board_end = html.rindex("</section>", 0, done_start)
|
|
board_end = html.index("</div>", board_end) + len("</div>")
|
|
board = html[board_start:board_end]
|
|
|
|
columns = []
|
|
n_cards = 0
|
|
for pos, col in enumerate(
|
|
re.finditer(
|
|
r'<!--\s*([^>]*?)\s*-->\s*<section class="col" style="--stripe: var\(--([\w-]+)\)">\s*'
|
|
r'<div class="col-head"><h2>([^<]*)</h2><span class="count">\d+</span></div>\s*'
|
|
r'<div class="stack">(.*?)</div>\s*</section>',
|
|
board,
|
|
re.S,
|
|
),
|
|
start=1,
|
|
):
|
|
comment, stripe, title, stack = col.groups()
|
|
dirname = "%d-%s" % (pos, slug(title))
|
|
os.makedirs(os.path.join(CARDS_DIR, dirname), exist_ok=True)
|
|
|
|
for rank, card in enumerate(re.finditer(r'<article class="card"([^>]*)>(.*?)</article>', stack, re.S), start=1):
|
|
attrs, inner = card.groups()
|
|
horizon = re.search(r'data-horizon="([^"]*)"', attrs)
|
|
meta = re.search(r'<div class="card-meta">(.*?)</div>', inner, re.S).group(1)
|
|
flag = re.search(r'<span class="flag f-(\w+)">(.*?)</span>', meta, re.S)
|
|
card_title = re.search(r"<h3>(.*?)</h3>", inner, re.S).group(1).strip()
|
|
|
|
front = ["title: " + card_title, "area: " + re.search(r'data-area="([^"]*)"', attrs).group(1)]
|
|
if horizon:
|
|
front.append("horizon: " + horizon.group(1))
|
|
front.append("tags: " + ", ".join(re.findall(r'<span class="tag">(.*?)</span>', meta, re.S)))
|
|
if flag:
|
|
front.append("flag: %s | %s" % (flag.group(1), flag.group(2).strip()))
|
|
front.append("src: " + re.search(r'<span class="src">(.*?)</span>', inner, re.S).group(1).strip())
|
|
|
|
body = "\n".join("<p>%s</p>" % p.strip() for p in re.findall(r"<p>(.*?)</p>", inner, re.S))
|
|
write(
|
|
os.path.join(CARDS_DIR, dirname, "%03d-%s.md" % (rank * 10, slug(card_title))),
|
|
"---\n" + "\n".join(front) + "\n---\n" + body + "\n",
|
|
)
|
|
n_cards += 1
|
|
|
|
columns.append(
|
|
{"dir": dirname, "title": title, "stripe": stripe, "statClass": stat_class.get(title, ""), "comment": comment}
|
|
)
|
|
|
|
done_section = html[done_start : html.index("</section>", html.index('<div class="done-grid">', done_start))]
|
|
os.makedirs(DONE_DIR, exist_ok=True)
|
|
n_done = 0
|
|
for rank, item in enumerate(re.finditer(r'<div class="done-item">\s*(.*?)\s*</div>', done_section, re.S), start=1):
|
|
inner = item.group(1)
|
|
title = re.search(r"<strong>(.*?)</strong>", inner, re.S).group(1).strip()
|
|
body = inner[inner.index("</strong>") + len("</strong>") :].strip()
|
|
write(
|
|
os.path.join(DONE_DIR, "%03d-%s.md" % (rank * 10, slug(title))),
|
|
"---\ntitle: " + title + "\n---\n" + body + "\n",
|
|
)
|
|
n_done += 1
|
|
|
|
board_meta = {
|
|
"updated": re.search(r"Mise à jour · ([\d-]+)", stamp).group(1),
|
|
"stampNote": stamp.split("<br>")[-1].strip(),
|
|
"intro": intro,
|
|
"columns": columns,
|
|
"done": {
|
|
"heading": re.search(r"<h2>(.*?)</h2>", done_section, re.S).group(1).strip(),
|
|
"statLabel": next((k for k in stat_class if "Fait" in k), "Fait récemment"),
|
|
"statClass": next((v for k, v in stat_class.items() if "Fait" in k), "n-good"),
|
|
"intro": re.search(r"<p>(.*?)</p>", done_section, re.S).group(1).strip(),
|
|
},
|
|
}
|
|
write(BOARD_OUT, json.dumps(board_meta, ensure_ascii=False, indent=2) + "\n")
|
|
|
|
template = (
|
|
html[: html.index('<header class="masthead">')]
|
|
+ '<header class="masthead">\n <div>\n '
|
|
+ re.search(r"<h1>.*?</h1>", masthead, re.S).group(0)
|
|
+ "\n{{INTRO}}\n"
|
|
+ ' <div class="stamp">\n {{STAMP}}\n </div>\n </header>\n\n'
|
|
+ "{{SUMMARY}}\n\n"
|
|
+ html[html.index('<div class="filters"') : board_start]
|
|
+ "{{BOARD}}\n\n {{DONE}}\n"
|
|
+ html[html.index("</section>", html.index('<div class="done-grid">')) + len("</section>") :]
|
|
)
|
|
write(TPL_OUT, template)
|
|
|
|
print("cartes : %d fait-recemment : %d" % (n_cards, n_done))
|
|
for c in columns:
|
|
print(" %-14s %2d" % (c["title"], len(os.listdir(os.path.join(CARDS_DIR, c["dir"])))))
|
|
print("board.json %d o template %d o" % (os.path.getsize(BOARD_OUT), os.path.getsize(TPL_OUT)))
|