#!/usr/bin/env python """build.py — assembla le pagine pubblicate a partire da shell + dati prodotti dagli script. Le pagine sono deliverable, non sorgenti di verita': ogni numero che mostrano viene da uno script Python committato, e qui viene INIETTATO, mai trascritto a mano. La regola nasce da un errore di questa sessione — due anni di dati di un grafico scritti a memoria perche' l'output del terminale era troncato dal `tail` — e il modo di non ripeterlo e' non avere il passaggio manuale. # 1. produrre i dati uv run python scripts/research/r0807_growth_yearly.py --dep 800 --json /tmp/growth800.json uv run python scripts/research/r0807_asset_compare.py --json /tmp/compare.json uv run python scripts/research/r0807_best_strategy.py --anni 12 --json /tmp/best.json # 2. assemblare uv run python scripts/web/build.py --out /tmp/pagine \\ --dati simulatore=/tmp/series.json confronto=/tmp/compare.json scelta=/tmp/best.json # 3. verificare che le pagine ESEGUANO (node --check valida solo la sintassi) node scripts/web/smoke.js /tmp/pagine/*.html `pagina_crescita.html` ha i dati gia' dentro (due configurazioni di versamento a confronto) e non richiede iniezione: se manca il segnaposto, il file viene copiato tale e quale. """ from __future__ import annotations import argparse import shutil from pathlib import Path QUI = Path(__file__).resolve().parent SEGNAPOSTO = "/*__DATA__*/" MOTORE = "/*__ENGINE__*/" SERIE = "/*__SERIES__*/" def costruisci(shell: Path, dati: Path | None, out: Path) -> str: testo = shell.read_text() note = [] if MOTORE in testo: # la pagina interattiva porta dentro il motore eng = (QUI / "engine.js").read_text() eng = eng.split('if (typeof module !== "undefined")')[0].rstrip() testo = testo.replace(MOTORE, eng) note.append("motore") for tag in (SERIE, SEGNAPOSTO): if tag in testo: if dati is None: raise SystemExit(f"{shell.name}: serve --dati, il file contiene {tag}") testo = testo.replace(tag, dati.read_text().strip()) note.append(dati.name) out.write_text(testo) return ", ".join(note) if note else "nessuna iniezione (gia' completa)" def main() -> None: ap = argparse.ArgumentParser() ap.add_argument("--out", required=True, help="cartella di destinazione") ap.add_argument("--dati", nargs="*", default=[], metavar="nome=percorso.json") a = ap.parse_args() mappa = {} for voce in a.dati: nome, _, perc = voce.partition("=") p = Path(perc) if not p.exists(): raise SystemExit(f"dati mancanti per «{nome}»: {p}") mappa[nome] = p dest = Path(a.out) dest.mkdir(parents=True, exist_ok=True) for shell in sorted(QUI.glob("pagina_*.html")): nome = shell.stem.replace("pagina_", "") out = dest / f"{nome}.html" try: esito = costruisci(shell, mappa.get(nome), out) except SystemExit as e: print(f" {nome:12} SALTATA — {e}") continue print(f" {nome:12} -> {out} ({esito}, {out.stat().st_size/1024:.0f} KB)") for extra in ("engine.js", "smoke.js", "test_engine.js"): shutil.copy(QUI / extra, dest / extra) print(f"\n verifica: node {dest}/smoke.js {dest}/*.html") if __name__ == "__main__": main()