""" 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ă", "08_ANALIZA_ZILNICA": "Analiză zilnică", "02_CATALIZATORI": "Catalizatori", "17_STRATEGII": "Strategii"} 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, date_only: bool = False) -> str: """Taie prefixul de modul din titlu ("Modul 2 / #6 — ..." -> "#6 — ..."), pentru locurile unde modulul e deja scris alături: breadcrumb, bara de navigare, titlurile din index. `date_only` ține doar "Q&A ", pentru barele înguste de pe telefon — subiectul se vede oricum în H1, dedesubt.""" short = re.sub(r"^(?:[^/]*/\s*(?=#)|Q&A (?=\d))", "", title) return short.split(" — ")[0] if date_only else short SUBFOLDER_NAMES = {"fise": "fișe"} 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. Fișierele cu un nivel în plus (ex. 17_STRATEGII/fise/*.md) primesc un segment suplimentar de breadcrumb și link-uri către index cu un "../" în plus.""" rel_parts = md_path.relative_to(ROOT).parts up = "../" * (len(rel_parts) - 1) folder = rel_parts[0] subfolder = rel_parts[1] if len(rel_parts) == 3 else None sep = '/' crumbs = [f'Sumarizări', f'{escape(module_name(folder))}'] if subfolder: crumbs.append(escape(SUBFOLDER_NAMES.get(subfolder, subfolder))) if folder == "09_QA" and md_path.stem != "INDEX_TEMATIC": crumbs.append('index tematic') crumbs.append(escape(short_title(title, date_only=folder == "09_QA"))) top = f'' 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), date_only=folder == '09_QA'))}" if i > 0: prev_a = f'' if i < len(lessons) - 1: next_a = f'' bottom = (f'') return top, bottom def link_md_to_html(md_path: Path, body: str) -> str: """Trimiterile din .md duc la .md (ca să meargă și pe Gitea); în pagină trebuie să ducă la .html. Doar cele care rămân în summaries/ — un link spre triaj/ sau harvest/ n-are pagină randată. Acceptă și un nivel în plus (module/fise/x.md), care tot are pagină randată.""" def sub(m): tinta = (md_path.parent / (m.group(1) + ".md")).resolve() randata = tinta.parent.parent == ROOT or tinta.parent.parent.parent == ROOT if randata and tinta.exists(): # doar paginile randate return f'href="{m.group(1)}.html{m.group(2) or ""}"' return m.group(0) return re.sub(r'href="(?!https?:)([^":]+?)\.md(#[^"]*)?"', sub, body) def stash_svgs(text: str) -> tuple[str, list[str]]: """Scoate ... din calea lui markdown și lasă un marcaj în loc. Fără asta, markdown vede caracterele lui înăuntrul desenului: în `mp2_pivoti_excel` cele două `*` din formulele Excel (`=2*E2-C2` … `=2*E3-B3`) deveneau un `` pus peste ``, iar parserul browserului arunca tot restul SVG-ului — jumătate de tabel lipsea din pagină, fără niciun semn în .md sau în audit (02.10.2026). """ pastrate: list[str] = [] def scoate(m: re.Match) -> str: pastrate.append(m.group(0)) return f"" return re.sub(r"", scoate, text, flags=re.S), pastrate def render_file(md_path: Path) -> tuple[Path, str]: text = md_path.read_text(encoding="utf-8") check_svgs(md_path, text) fara_svg, svgs = stash_svgs(text) body = markdown.markdown(fara_svg, extensions=["extra", "toc"]) body = re.sub(r"(?:

\s*)?(?:\s*

)?", lambda m: svgs[int(m.group(1))], body) assert "