"""
Render every summaries/**/*.md to a sibling .html — mobile-readable (viewport
meta + real font size), served alongside the .md via `tailscale serve`.
Also (re)writes summaries/index.html linking every rendered page.
Run after adding/editing any summary.
"""
import re
from html import escape
from pathlib import Path
import markdown
ROOT = Path(__file__).parent / "summaries"
PAGE_TEMPLATE = """
{title}
{body}
"""
def check_svgs(md_path: Path, text: str) -> None:
"""O linie goală în interiorul unui face python-markdown să rupă
blocul HTML în paragrafe, iar browserul aruncă restul elementelor —
diagrama se randează pe jumătate, fără nicio eroare. S-a întâmplat deja
de două ori; mai bine crapă aici decât să publici o diagramă stricată."""
for m in re.finditer(r"", text, re.S):
if re.search(r"\n[ \t]*\n", m.group(0)):
line = text[: m.start()].count("\n") + 1
raise SystemExit(f"{md_path}:{line}: linie goală în interiorul — markdown va rupe blocul")
MODULE_NAMES = {"09_QA": "Q&A", "16_ANALIZA_FUNDAMENTALA": "Analiză fundamentală",
"02_CATALIZATORI": "Catalizatori"}
def module_name(folder: str) -> str:
"""03_MODUL_2 -> Modulul 2; nume speciale în MODULE_NAMES."""
if folder in MODULE_NAMES:
return MODULE_NAMES[folder]
rest = folder.split("_", 1)[-1]
if rest.startswith("MODUL_"):
return "Modulul " + rest[6:]
return rest.replace("_", " ").capitalize()
def md_title(md_path: Path) -> str:
text = md_path.read_text(encoding="utf-8")
return text.splitlines()[0].lstrip("# ").strip() if text else md_path.stem
def short_title(title: str, folder: str) -> str:
"""Pentru bara de navigare. Titlul lecțiilor începe cu "Modul 2 / #6 — ...",
iar modulul e deja în breadcrumb. La Q&A titlul întreg ocupă trei rânduri pe
telefon, așa că rămâne doar "Q&A " — restul se vede în H1, dedesubt."""
short = re.sub(r"^Modul[^/]*/\s*", "", title)
return short.split(" — ")[0] if folder == "09_QA" else short
def nav_html(md_path: Path, title: str) -> tuple[str, str]:
"""Breadcrumb sus + înainte/înapoi jos. Vecinii vin din glob pe folder,
ca render_file să meargă apelat singur. Doar fișierele numerotate
(lecțiile) intră în secvență — INDEX_TEMATIC nu."""
folder = md_path.parent.name
sep = '/ '
crumbs = ['Sumarizări ',
f'{escape(module_name(folder))} ']
if folder == "09_QA" and md_path.stem != "INDEX_TEMATIC":
crumbs.append('index tematic ')
crumbs.append(escape(short_title(title, folder)))
top = f'{sep.join(crumbs)} '
lessons = sorted(f for f in md_path.parent.glob("*.md") if f.name[0].isdigit())
prev_a = next_a = ""
if md_path in lessons:
i = lessons.index(md_path)
label = lambda f: f"{f.name.split('_')[0]}. {escape(short_title(md_title(f), folder))}"
if i > 0:
prev_a = f'← {label(lessons[i-1])} '
if i < len(lessons) - 1:
next_a = f'{label(lessons[i+1])} → '
bottom = (f'')
return top, bottom
def render_file(md_path: Path) -> tuple[Path, str]:
text = md_path.read_text(encoding="utf-8")
check_svgs(md_path, text)
body = markdown.markdown(text, extensions=["extra", "toc"])
title = text.splitlines()[0].lstrip("# ").strip() if text else md_path.stem
top, bottom = nav_html(md_path, title)
html_path = md_path.with_suffix(".html")
html_path.write_text(PAGE_TEMPLATE.format(title=title, body=top + body + bottom), encoding="utf-8")
return html_path, title
def write_index(pages: list[tuple[Path, str]]):
"""Grupat pe module, cu titlul H1 al fiecărei pagini — numele fișierului
(o dată, un număr) nu spune nimic despre ce e înăuntru."""
sections = []
current = None
for path, title in sorted(pages):
if path.parent.name != current:
if current is not None:
sections.append("")
current = path.parent.name
sections.append(f'{escape(module_name(current))} ')
sections.append(f'{title} ')
if current is not None:
sections.append(" ")
(ROOT / "index.html").write_text(
PAGE_TEMPLATE.format(title="ATM — Sumarizări",
body="ATM — Sumarizări " + "".join(sections)),
encoding="utf-8",
)
def main():
md_files = sorted(ROOT.glob("*/*.md"))
pages = [render_file(f) for f in md_files]
write_index(pages)
print(f"Rendered {len(pages)} page(s) + index.html")
if __name__ == "__main__":
main()