SPB Git forge

spb/wp12_uqo

Public
5commits 1branches 0releases
1.2 MBsize
maindefault branch
1 mo agolast push
Python 75.9% TeX 24%
3.4 KB · 107 lines python
Raw Blame History
1#!/usr/bin/env python32"""3================================================================4Auteur  : Simon-Pierre Boucher5Contact : contact@spboucher.ai6Projet  : Prévision de volatilité réalisée multi-actifs7          (HAR-RV vs GARCH vs Machine Learning)8Fichier : 09_build_paper.py9Description : Étape 09 — Vérification des headers de tous les10              fichiers de code, présence des tables/figures11              référencées, et compilation du PDF (latexmk).12Usage : python scripts/09_build_paper.py [--no-compile]13================================================================14"""1516from __future__ import annotations1718import argparse19import logging20import re21import subprocess22import sys23from pathlib import Path2425sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))2627from wp12 import config  # noqa: E4022829logger = logging.getLogger("09_build_paper")3031ROOT = config.ROOT32HEADER_MARK = "Auteur  : Simon-Pierre Boucher"333435def check_headers() -> bool:36    """Every .py / config / paper source file must carry the project header."""37    ok = True38    files = (39        list((ROOT / "src").rglob("*.py"))40        + list((ROOT / "scripts").glob("*.py"))41        + list((ROOT / "tests").glob("*.py"))42        + [ROOT / "config.yaml", ROOT / "requirements.txt"]43        + list((ROOT / "paper").glob("*.tex"))44        + list((ROOT / "paper" / "sections").glob("*.tex"))45    )46    for f in files:47        if "__pycache__" in str(f):48            continue49        head = f.read_text(encoding="utf-8", errors="ignore")[:600]50        if HEADER_MARK not in head:51            logger.error("MISSING HEADER: %s", f.relative_to(ROOT))52            ok = False53    logger.info("header check: %d files, %s", len(files), "OK" if ok else "FAILURES")54    return ok555657def check_inputs() -> bool:58    """All tables/figures referenced by the paper must exist on disk."""59    ok = True60    tex = ""61    for f in [ROOT / "paper" / "main.tex", *(ROOT / "paper" / "sections").glob("*.tex")]:62        tex += f.read_text(encoding="utf-8")63    for m in re.finditer(r"\\input\{(\.\./results/tables/[^}]+)\}", tex):64        p = (ROOT / "paper" / m.group(1)).resolve()65        p = p if p.suffix else p.with_suffix(".tex")66        if not p.exists():67            logger.error("missing table: %s", m.group(1))68            ok = False69    for m in re.finditer(r"\\includegraphics\[[^\]]*\]\{([^}]+)\}", tex):70        name = m.group(1)71        if name.startswith("uq_logo"):72            continue73        if not (ROOT / "figures" / name).exists():74            logger.error("missing figure: %s", name)75            ok = False76    logger.info("inputs check: %s", "OK" if ok else "FAILURES")77    return ok787980def compile_pdf() -> bool:81    """Compile the paper with latexmk (pdflatex + bibtex)."""82    res = subprocess.run(83        ["latexmk", "-pdf", "-interaction=nonstopmode", "main.tex"],84        cwd=ROOT / "paper", capture_output=True, text=True, timeout=600,85    )86    if res.returncode != 0:87        logger.error("latexmk failed:\n%s", res.stdout[-3000:])88        return False89    logger.info("PDF compiled: paper/main.pdf")90    return True919293def main() -> None:94    """Run all pre-flight checks, then compile."""95    ap = argparse.ArgumentParser()96    ap.add_argument("--no-compile", action="store_true")97    args = ap.parse_args()98    config.setup_logging()99    ok = check_headers() & check_inputs()100    if not args.no_compile:101        ok &= compile_pdf()102    sys.exit(0 if ok else 1)103104105if __name__ == "__main__":106    main()107