DOCS/outputs/mnaha/seed/step7_images.py
Thomas Fransolet d14f151517 Livrables de presentation, seed de demo MNAHA et wireframes
- outputs/platform, outputs/prospect : scripts de construction des decks
  commerciaux + captures sources et .pptx generes.
- outputs/mnaha : seed complet de l'instance de demo luxembourgeoise
  (scripts par etape, contenus, images generees), le deck en version
  marque blanche et en version brandee, le PDF de presentation.
- outputs/Wireframes : maquettes app mobile, app web et manager.
- interview clients : notes de l'entretien Fourneau St Michel.

state.json et les __pycache__ sont ignores : le premier porte les cles
d'API de l'instance seedee, les scripts le regenerent.
2026-09-11 15:47:57 +02:00

142 lines
4.9 KiB
Python

# -*- coding: utf-8 -*-
"""Étape 7 — visuels de démonstration, créés en ressources et rattachés.
`POST /api/Resource/upload` ne stocke aucun octet : il lit le fichier, l'encode
en base64, jette le résultat, et n'enregistre que le libellé, le chemin de
bucket et la taille. Le blob est déposé dans Firebase Storage par `manager-app`
depuis le navigateur — un script qui parle à l'API ne peut pas l'y écrire.
Les ressources sont donc créées en type `ImageUrl`, servies par
`serve_images.py`. **Ce serveur doit tourner pendant les captures.**
Idempotent : une ressource déjà créée sous le même libellé est réutilisée.
"""
import json
from pathlib import Path
from api import Api
STATE = Path(__file__).resolve().parent / "state.json"
IMAGES = Path(__file__).resolve().parent / "images"
HOST = "http://localhost:8099"
IMAGE_URL = 2 # ResourceType.ImageUrl — hors bucket, donc hors quota
SECTION_IMAGES = {
"Mosaïque de Vichten": "art-vichten",
"Goeblange-Nospelt": "art-goeblange",
"Villeroy & Boch Art déco": "art-villeroy",
"Cabinet des médailles": "art-medailles",
"Fort Thüngen": "art-thungen",
"Démantèlement 1867": "art-demantelement",
"Maquettes et plans-reliefs": "art-maquettes",
"La villa romaine": "art-villa",
"Thermes et hypocauste": "art-thermes",
"Parcours extérieur": "parcours-echternach",
"Sur les pas du maître": "parcours-echternach",
"Le secret de la villa": "parcours-escape",
"Collections": "site-feschmaart",
"Quiz archéologie": "art-vichten",
"Expositions et rendez-vous": "site-feschmaart",
}
CONFIG_IMAGES = {
"feschmaart": "site-feschmaart",
"draieechelen": "site-draieechelen",
"echternach": "site-echternach",
}
POI_IMAGES = {
"Entrée du site": "poi-entree",
"Cour intérieure": "poi-cour",
"Les thermes": "poi-thermes",
"Les bassins": "poi-bassins",
"Point de vue sur la Sûre": "poi-panorama",
}
def sync_resources(api, instance_id):
"""Une ressource ImageUrl par PNG.
Les lignes laissées sans `url` par `upload` sont corrigées sur place plutôt
que supprimées : elles gardent leur identifiant, donc les rattachements déjà
posés restent valides — et la suppression d'une ressource échoue de toute
façon tant que le correctif de `ResourceController` n'est pas déployé.
"""
existing = {r["label"]: r for r in
(api.get(f"/api/Resource?instanceId={instance_id}") or [])}
for label, resource in existing.items():
if resource.get("url") or not (IMAGES / f"{label}.png").exists():
continue
resource["type"] = IMAGE_URL
resource["url"] = f"{HOST}/{label}.png"
resource["sizeBytes"] = (IMAGES / f"{label}.png").stat().st_size
api.put("/api/Resource", resource)
print(f" ~ {label} (URL ajoutée)")
existing = {r["label"]: r for r in
(api.get(f"/api/Resource?instanceId={instance_id}") or [])}
resources = {}
for path in sorted(IMAGES.glob("*.png")):
if path.name.startswith("_"):
continue
label = path.stem
if label in existing:
resources[label] = existing[label]
print(f" = {label}")
continue
created = api.post("/api/Resource", {
"instanceId": instance_id,
"label": label,
"type": IMAGE_URL,
"url": f"{HOST}/{path.name}",
"sizeBytes": path.stat().st_size,
})
resources[label] = created
print(f" + {label}")
return resources
def main():
api = Api()
state = json.loads(STATE.read_text(encoding="utf-8"))
instance_id = state["instanceId"]
print("ressources")
resources = sync_resources(api, instance_id)
print("\nsites")
for key, image in CONFIG_IMAGES.items():
resource = resources[image]
config = api.get(f"/api/Configuration/{state['configs'][key]}")
config["imageId"] = resource["id"]
config["imageSource"] = resource["url"]
api.put("/api/Configuration", config)
print(f" {key} <- {image}")
print("\nsections")
for key, config_id in state["configs"].items():
for section in api.get(f"/api/Section/configuration/{config_id}") or []:
image = SECTION_IMAGES.get(section["label"])
if not image:
continue
resource = resources[image]
detail = api.get(f"/api/Section/{section['id']}")
if detail.get("imageSource") == resource["url"]:
print(f" = {section['label']}")
continue
detail["imageId"] = resource["id"]
detail["imageSource"] = resource["url"]
api.put("/api/Section", detail)
print(f" + {section['label']} <- {image}")
print("\nétape 7 terminée — points d'intérêt et applications : step7b_finish.py")
if __name__ == "__main__":
main()