- 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.
142 lines
4.9 KiB
Python
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()
|