Files
sliding-automation/patches/patch_facilitator_c2.py
T

200 lines
7.4 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
patch_facilitator_c2.py — Chantier C2 (Preview PNG)
===================================================
Patch strict de facilitator_v9.py :
P1. Config : PREVIEW_SCRIPT (.env, défaut ./preview.sh).
P2. Insère run_preview() + maybe_preview() avant la section
ARCHIVAGE TRILIUM (asynchrone par défaut, log dédié).
P3-P6. Propose les aperçus aux 4 sorties PPTX : flux standard,
flux libre, révision ciblée, révision complète.
P7. CLI : --preview <pptx> (mode bloquant, pour usage direct).
Usage (dossier du pipeline, single-line) :
python3 patch_facilitator_c2.py
Compatible avant/après le patch C1 (ancres indépendantes). Vérifie
chaque ancre, écrit .bak, compile, idempotent.
"""
import py_compile
import shutil
import sys
from pathlib import Path
TARGET = Path("facilitator_v9.py")
FUNC = '''
def run_preview(pptx_path: Path, wait: bool = False) -> bool:
"""Aperçus PNG par slide via preview.sh (chantier C2).
wait=False : lancement en arrière-plan (le DS218 est lent), sortie
consignée dans <pptx>_preview.log. wait=True : bloquant (CLI)."""
script = Path(PREVIEW_SCRIPT)
if not script.exists():
warn(f"{PREVIEW_SCRIPT} introuvable — aperçus indisponibles.")
return False
out_dir = pptx_path.parent / f"{pptx_path.stem}_previews"
if wait:
info("Génération des aperçus (quelques minutes sur le NAS)...")
r = subprocess.run([str(script), str(pptx_path)],
capture_output=True, text=True)
if r.returncode == 0:
ok(f"Aperçus : {out_dir}")
ok(f"Galerie : {out_dir / 'index.html'}")
return True
warn("Échec de la génération des aperçus :")
print(textwrap.indent((r.stderr or r.stdout or "?").strip(),
" "))
return False
log = pptx_path.parent / f"{pptx_path.stem}_preview.log"
with open(log, "w", encoding="utf-8") as lf:
subprocess.Popen([str(script), str(pptx_path)],
stdout=lf, stderr=subprocess.STDOUT)
info(f"Aperçus en arrière-plan → {out_dir}")
info(f"Suivi : {log}")
return True
def maybe_preview(pptx_path) -> None:
"""Propose la génération des aperçus après une sortie PPTX."""
if not pptx_path or not Path(PREVIEW_SCRIPT).exists():
return
if ask("Générer les aperçus PNG ? (o/N) :").lower() in (
"o", "oui", "y", "yes"):
run_preview(pptx_path, wait=False)
'''
PATCHES = [
# P1 — config
(
'LAYOUTS_PATH = os.getenv("LAYOUTS_PATH", "layouts_v2.yaml")\n',
'LAYOUTS_PATH = os.getenv("LAYOUTS_PATH", "layouts_v2.yaml")\n'
'PREVIEW_SCRIPT = os.getenv("PREVIEW_SCRIPT", "./preview.sh")'
' # C2\n',
),
# P3 — flux standard (indentation 12)
(
' ok(f"Fichier PPTX : {pptx_path}")\n'
' ok(f"Taille : {pptx_path.stat().st_size/1024:.1f}'
' Ko")\n'
' else:\n'
' warn("Le PPTX n\'a pas pu être généré.")\n'
' info(f"Le YAML est dans : {proj.outputs}")\n',
' ok(f"Fichier PPTX : {pptx_path}")\n'
' ok(f"Taille : {pptx_path.stat().st_size/1024:.1f}'
' Ko")\n'
' maybe_preview(pptx_path)\n'
' else:\n'
' warn("Le PPTX n\'a pas pu être généré.")\n'
' info(f"Le YAML est dans : {proj.outputs}")\n',
),
# P4 — flux libre (indentation 20)
(
' ok(f"Fichier PPTX : {pptx_path}")\n'
' ok(f"Taille : '
'{pptx_path.stat().st_size/1024:.1f} Ko")\n',
' ok(f"Fichier PPTX : {pptx_path}")\n'
' ok(f"Taille : '
'{pptx_path.stat().st_size/1024:.1f} Ko")\n'
' maybe_preview(pptx_path)\n',
),
# P5 — révision ciblée
(
' ok(f"PPTX des slides révisées : {pptx_path}")\n'
' info("Ouvre ce fichier et copie-colle les slides dans '
'ton deck maître.")\n',
' ok(f"PPTX des slides révisées : {pptx_path}")\n'
' info("Ouvre ce fichier et copie-colle les slides dans '
'ton deck maître.")\n'
' maybe_preview(pptx_path)\n',
),
# P6 — révision complète
(
' section("DECK COMPLET RÉGÉNÉRÉ")\n'
' ok(f"Nouveau PPTX complet : {pptx_path}")\n',
' section("DECK COMPLET RÉGÉNÉRÉ")\n'
' ok(f"Nouveau PPTX complet : {pptx_path}")\n'
' maybe_preview(pptx_path)\n',
),
# P7a — argument CLI
(
' parser.add_argument("--render", metavar="YAML",\n'
' help="Rendu direct d\'un YAML existant, '
'sans agents")\n',
' parser.add_argument("--render", metavar="YAML",\n'
' help="Rendu direct d\'un YAML existant, '
'sans agents")\n'
' parser.add_argument("--preview", metavar="PPTX",\n'
' help="Aperçus PNG d\'un PPTX existant '
'(C2)")\n',
),
# P7b — traitement CLI
(
' if args.render:\n',
' if args.preview:\n'
' p = Path(args.preview)\n'
' if not p.exists():\n'
' warn(f"Fichier introuvable : {p}")\n'
' sys.exit(1)\n'
' sys.exit(0 if run_preview(p, wait=True) else 1)\n'
'\n'
' if args.render:\n',
),
]
MARKER = "def run_preview"
ANCHOR_SECTION = "# ARCHIVAGE TRILIUM"
def fail(msg):
print(" ! %s" % msg)
sys.exit(1)
def main():
if not TARGET.exists():
fail("%s introuvable — lancer depuis le dossier du pipeline."
% TARGET)
content = TARGET.read_text(encoding="utf-8")
if MARKER in content:
fail("Déjà patché (run_preview présent) — rien à faire.")
for i, (old, _) in enumerate(PATCHES, 1):
n = content.count(old)
if n == 0:
fail("Ancre du patch %d introuvable — facilitator inattendu."
% i)
if n > 1:
fail("Ancre du patch %d non unique (%d occurrences)." % (i, n))
if content.count(ANCHOR_SECTION) != 1:
fail("Ancre de section ARCHIVAGE TRILIUM introuvable ou non unique.")
shutil.copy2(TARGET, str(TARGET) + ".bak-c2")
print(" + Sauvegarde : %s.bak-c2" % TARGET)
for old, new in PATCHES:
content = content.replace(old, new)
lines = content.split("\n")
idx = next(i for i, l in enumerate(lines) if ANCHOR_SECTION in l)
ins = idx - 1 if lines[idx - 1].startswith("# ───") else idx
lines[ins:ins] = FUNC.split("\n")
content = "\n".join(lines)
TARGET.write_text(content, encoding="utf-8")
print(" + 7 patchs appliqués.")
try:
py_compile.compile(str(TARGET), doraise=True)
print(" + Compilation OK.")
except py_compile.PyCompileError as e:
shutil.copy2(str(TARGET) + ".bak-c2", TARGET)
fail("Erreur de compilation — fichier restauré :\n%s" % e)
print("\n Test direct : python3 facilitator_v9.py --preview "
"<deck.pptx>")
print(" Config .env optionnelle : PREVIEW_SCRIPT=./preview.sh")
if __name__ == "__main__":
main()