chore: regroupement des scripts de patch dans patches/
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
patch_facilitator_c4.py — Chantier C4 (Assets & images)
|
||||
=======================================================
|
||||
Patch strict de facilitator_v9.py :
|
||||
P1. validate_freeform accepte le nouveau type de bloc « image ».
|
||||
P2. /lire inventorie aussi projets/<slug>/assets/ (images + dimensions)
|
||||
et injecte la liste dans le contexte Narrator — le Narrator peut
|
||||
alors référencer les fichiers par nom dans les layouts image_*.
|
||||
Le dossier assets/ est créé au premier /lire s'il n'existe pas.
|
||||
P3. Insère list_assets() (inventaire, dimensions via Pillow si
|
||||
disponible, sinon noms seuls).
|
||||
|
||||
Usage (dossier du pipeline, single-line) :
|
||||
python3 patch_facilitator_c4.py
|
||||
Compatible tout ordre avec C1/C2/C3. Vérifie chaque ancre, écrit
|
||||
.bak-fc4, compile, idempotent.
|
||||
"""
|
||||
|
||||
import py_compile
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
TARGET = Path("facilitator_v9.py")
|
||||
|
||||
FUNC = '''
|
||||
def list_assets(proj: "Project"):
|
||||
"""Inventaire des images de projets/<slug>/assets/ (chantier C4).
|
||||
Crée le dossier au premier appel. Retourne un bloc texte destiné au
|
||||
contexte Narrator, ou une chaîne vide si aucune image."""
|
||||
assets = proj.root / "assets"
|
||||
assets.mkdir(exist_ok=True)
|
||||
exts = {".png", ".jpg", ".jpeg", ".webp"}
|
||||
files = sorted(p for p in assets.iterdir()
|
||||
if p.suffix.lower() in exts and p.is_file())
|
||||
if not files:
|
||||
return ""
|
||||
lines = ["IMAGES DISPONIBLES DANS assets/ (utilisables dans les "
|
||||
"layouts image_split / image_full et le bloc freeform "
|
||||
"image, par leur nom de fichier) :"]
|
||||
for p in files:
|
||||
dims = ""
|
||||
try:
|
||||
from PIL import Image
|
||||
with Image.open(p) as im:
|
||||
dims = f" ({im.size[0]}×{im.size[1]})"
|
||||
except Exception:
|
||||
pass
|
||||
lines.append(f"- {p.name}{dims}")
|
||||
return "\\n".join(lines)
|
||||
|
||||
'''
|
||||
|
||||
PATCHES = [
|
||||
# P1 — type de bloc freeform « image »
|
||||
(
|
||||
'VALID_BLOCK_TYPES = {"title", "heading", "text", "stat", '
|
||||
'"circle",\n'
|
||||
' "badge", "card", "rect", "line"}\n',
|
||||
'VALID_BLOCK_TYPES = {"title", "heading", "text", "stat", '
|
||||
'"circle",\n'
|
||||
' "badge", "card", "rect", "line", "image"}\n',
|
||||
),
|
||||
# P2 — /lire : inventaire des assets
|
||||
(
|
||||
' elif cmd == "/lire":\n'
|
||||
' context, new_files = load_documents(proj, '
|
||||
'loaded_docs)\n'
|
||||
' if not new_files:\n'
|
||||
' info("Aucun nouveau document dans inputs/.")\n'
|
||||
' continue\n',
|
||||
' elif cmd == "/lire":\n'
|
||||
' context, new_files = load_documents(proj, '
|
||||
'loaded_docs)\n'
|
||||
' assets_info = list_assets(proj)\n'
|
||||
' if assets_info:\n'
|
||||
' context = ((context or "").strip()\n'
|
||||
' + "\\n\\n" + assets_info).strip()\n'
|
||||
' ok(f"{len(assets_info.splitlines()) - 1} '
|
||||
'image(s) dans assets/.")\n'
|
||||
' if not new_files and not assets_info:\n'
|
||||
' info("Aucun nouveau document dans inputs/ ni '
|
||||
'image dans assets/.")\n'
|
||||
' continue\n',
|
||||
),
|
||||
]
|
||||
|
||||
MARKER = "def list_assets"
|
||||
ANCHOR_SECTION = "# FLUX LIBRE — THE FREE DESIGNER"
|
||||
|
||||
|
||||
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é (list_assets 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 FREE DESIGNER introuvable ou non unique.")
|
||||
|
||||
shutil.copy2(TARGET, str(TARGET) + ".bak-fc4")
|
||||
print(" + Sauvegarde : %s.bak-fc4" % 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(" + 2 patchs + list_assets appliqués.")
|
||||
try:
|
||||
py_compile.compile(str(TARGET), doraise=True)
|
||||
print(" + Compilation OK.")
|
||||
except py_compile.PyCompileError as e:
|
||||
shutil.copy2(str(TARGET) + ".bak-fc4", TARGET)
|
||||
fail("Erreur de compilation — fichier restauré :\n%s" % e)
|
||||
print("\n Dépose tes images dans projets/<slug>/assets/ puis /lire "
|
||||
"dans la session.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user