- 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.
439 lines
20 KiB
Python
439 lines
20 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Deck de présentation MyInfoMate — audience prospect.
|
|
|
|
Même matière que le deck plateforme, mais retournée : celui-là défendait un
|
|
patrimoine devant un acquéreur (volume de code, historique, coût de
|
|
reconstruction), celui-ci défend un bénéfice devant un client. D'où trois règles
|
|
de rédaction tenues d'un bout à l'autre :
|
|
|
|
- aucun chiffre de production interne (lignes, commits, écrans, points d'API) ;
|
|
- aucun nom de dépôt ni de technologie dans le corps du propos ;
|
|
- chaque titre énonce ce que le client gagne, pas ce que le produit contient.
|
|
|
|
Les captures viennent du deck plateforme, extraites dans ../platform/captures.
|
|
"""
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent
|
|
sys.path.insert(0, str(ROOT.parent / "mnaha"))
|
|
|
|
from pptx import Presentation
|
|
from pptx.dml.color import RGBColor
|
|
from pptx.enum.text import MSO_ANCHOR, PP_ALIGN
|
|
from pptx.util import Inches, Pt
|
|
|
|
from build_decks import ( # noqa: E402
|
|
H, LOGO, MARGIN, W, Theme, bullets, footer, kicker_title, page_base, rect,
|
|
sanitize_package, text,
|
|
)
|
|
|
|
CAPTURES = ROOT.parent / "platform" / "captures"
|
|
|
|
|
|
def picture(slide, name, x, y, w=None, h=None):
|
|
"""Insère une capture si elle existe ; sinon ne pose rien."""
|
|
path = CAPTURES / name
|
|
if not path.exists():
|
|
return None
|
|
kw = {}
|
|
if w is not None:
|
|
kw["width"] = w
|
|
if h is not None:
|
|
kw["height"] = h
|
|
return slide.shapes.add_picture(str(path), x, y, **kw)
|
|
|
|
|
|
def slide_cover(prs, th):
|
|
s = page_base(prs, th, dark=True)
|
|
rect(s, 0, H - Inches(0.9), W, Inches(0.9), fill=th.accent)
|
|
text(s, MARGIN, Inches(2.05), Inches(10.4), Inches(0.4),
|
|
[("MÉDIATION NUMÉRIQUE POUR LIEUX CULTURELS", 11, True, th.accent_bright,
|
|
th.body_font, 0)])
|
|
text(s, MARGIN, Inches(2.6), Inches(10.6), Inches(1.8),
|
|
[("MyInfoMate", 52, True, RGBColor(0xFF, 0xFF, 0xFF), th.title_font, 0)],
|
|
line_spacing=1.02)
|
|
text(s, MARGIN, Inches(3.95), Inches(9.8), Inches(1.2),
|
|
[("Votre contenu, saisi une fois.\n"
|
|
"Vécu sur une borne, un téléphone et le web.", 16, False,
|
|
RGBColor(0xB8, 0xC2, 0xCE), th.body_font, 0)], line_spacing=1.4)
|
|
if LOGO.exists():
|
|
s.shapes.add_picture(str(LOGO), W - MARGIN - Inches(1.4), Inches(0.8),
|
|
height=Inches(1.2))
|
|
return s
|
|
|
|
|
|
def slide_problem(prs, th, n):
|
|
s = page_base(prs, th)
|
|
kicker_title(s, th, "Le constat",
|
|
"La médiation coûte cher, et elle vieillit vite.")
|
|
items = [
|
|
("Le panneau imprimé", "Une correction, et c'est un nouveau tirage. Une "
|
|
"langue de plus, et c'est un nouveau panneau."),
|
|
("L'audioguide loué", "Un parc de boîtiers à charger, désinfecter et "
|
|
"remplacer, pour un contenu qu'on ne peut plus modifier."),
|
|
("L'application sur devis", "Livrée une fois, jamais reprise. Le prestataire "
|
|
"part, le contenu se fige, et la refonte se repaie entièrement."),
|
|
]
|
|
cw = (W - 2 * MARGIN - Inches(0.6)) / 3
|
|
y = Inches(3.15)
|
|
for i, (head, body) in enumerate(items):
|
|
x = MARGIN + i * (cw + Inches(0.3))
|
|
rect(s, x, y, cw, Inches(2.2), fill=th.surface, line=th.rule)
|
|
rect(s, x, y, cw, Pt(3), fill=th.accent)
|
|
text(s, x + Inches(0.35), y + Inches(0.35), cw - Inches(0.7), Inches(0.5),
|
|
[(head, 15, True, th.ink, th.title_font, 0)], line_spacing=1.18)
|
|
text(s, x + Inches(0.35), y + Inches(0.95), cw - Inches(0.7), Inches(1.1),
|
|
[(body, 11.5, False, th.muted, th.body_font, 0)], line_spacing=1.35)
|
|
text(s, MARGIN, Inches(5.75), Inches(11.0), Inches(0.8),
|
|
[("Le point commun : le contenu appartient à l'objet qui le porte. "
|
|
"Changez l'objet, vous perdez le contenu.", 14, False, th.ink,
|
|
th.body_font, 0)], line_spacing=1.35)
|
|
footer(s, th, n, "Le constat")
|
|
return s
|
|
|
|
|
|
def slide_promise(prs, th, n):
|
|
s = page_base(prs, th)
|
|
kicker_title(s, th, "La proposition",
|
|
"Vous écrivez votre contenu une fois. Il vit partout, et il reste "
|
|
"à vous.")
|
|
text(s, MARGIN, Inches(3.35), Inches(11.0), Inches(1.6),
|
|
[("MyInfoMate sépare ce que vous racontez de l'endroit où on l'écoute. "
|
|
"Votre contenu vit dans un espace qui vous appartient ; la borne, "
|
|
"l'application et le site web ne sont que des façons de le consulter.",
|
|
17, False, th.ink, th.body_font, 0)], line_spacing=1.45)
|
|
rect(s, MARGIN, Inches(5.35), Inches(11.0), Pt(1), fill=th.rule)
|
|
text(s, MARGIN, Inches(5.6), Inches(11.0), Inches(0.8),
|
|
[("Une correction se fait une fois et se voit partout, le jour même. "
|
|
"Sans développeur, sans devis, sans attendre.", 13, False, th.muted,
|
|
th.body_font, 0)], line_spacing=1.35)
|
|
footer(s, th, n, "La proposition")
|
|
return s
|
|
|
|
|
|
def slide_channels(prs, th, n):
|
|
s = page_base(prs, th)
|
|
kicker_title(s, th, "Trois façons de visiter",
|
|
"Un seul contenu, trois expériences.")
|
|
hub_w, hub_h = Inches(3.6), Inches(1.4)
|
|
hub_x = (W - hub_w) / 2
|
|
hub_y = Inches(3.10)
|
|
rect(s, hub_x, hub_y, hub_w, hub_h, fill=th.ink)
|
|
text(s, hub_x + Inches(0.25), hub_y, hub_w - Inches(0.5), hub_h,
|
|
[("Votre contenu", 16, True, RGBColor(0xFF, 0xFF, 0xFF), th.title_font, 0),
|
|
("textes, images, sons,\nen dix langues", 11, False,
|
|
RGBColor(0xC5, 0xCE, 0xD8), th.body_font, 6)],
|
|
align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE, line_spacing=1.2)
|
|
|
|
cw, ch, gap = Inches(3.35), Inches(1.5), Inches(0.35)
|
|
total = 3 * cw + 2 * gap
|
|
x0 = (W - total) / 2
|
|
y = hub_y + hub_h + Inches(0.8)
|
|
apps = [
|
|
("La borne en salle", "Grand écran tactile, choix de la langue, "
|
|
"retour à l'accueil automatique."),
|
|
("L'application mobile", "Sur le téléphone du visiteur. Fonctionne "
|
|
"sans réseau."),
|
|
("La visite web", "Un QR code, rien à installer. Ouvre dans le "
|
|
"navigateur."),
|
|
]
|
|
for i, (name, body) in enumerate(apps):
|
|
x = x0 + i * (cw + gap)
|
|
rect(s, x, y, cw, ch, fill=th.surface, line=th.rule)
|
|
rect(s, x, y, cw, Pt(3), fill=th.accent)
|
|
rect(s, x + cw / 2 - Pt(1), hub_y + hub_h, Pt(2), Inches(0.8), fill=th.rule)
|
|
text(s, x + Inches(0.25), y + Inches(0.3), cw - Inches(0.5), Inches(0.3),
|
|
[(name, 13.5, True, th.ink, th.title_font, 0)], align=PP_ALIGN.CENTER)
|
|
text(s, x + Inches(0.25), y + Inches(0.72), cw - Inches(0.5), Inches(0.7),
|
|
[(body, 11, False, th.muted, th.body_font, 0)], align=PP_ALIGN.CENTER,
|
|
line_spacing=1.3)
|
|
rect(s, x0, hub_y + hub_h + Inches(0.4), total, Pt(2), fill=th.rule)
|
|
footer(s, th, n, "Trois façons de visiter")
|
|
return s
|
|
|
|
|
|
def slide_feature(prs, th, n, kicker, title, subtitle, items, capture=None,
|
|
portrait=False):
|
|
s = page_base(prs, th)
|
|
kicker_title(s, th, kicker, title, subtitle, width=Inches(6.05))
|
|
bullets(s, th, MARGIN, Inches(3.85), Inches(5.75), items, size=12.5,
|
|
gap=Inches(0.15))
|
|
if capture:
|
|
if portrait:
|
|
picture(s, capture, Inches(8.9), Inches(1.35), h=Inches(5.4))
|
|
else:
|
|
picture(s, capture, Inches(7.35), Inches(2.15), w=Inches(5.1))
|
|
footer(s, th, n, kicker)
|
|
return s
|
|
|
|
|
|
def slide_references(prs, th, n):
|
|
s = page_base(prs, th)
|
|
kicker_title(s, th, "Ils l'utilisent déjà",
|
|
"Trois sites en production, le plus ancien depuis 2021.")
|
|
cards = [
|
|
("Musée de la Fraise", "Wépion — depuis 2021",
|
|
"Une borne tactile en salle, toujours en service cinq ans après sa pose."),
|
|
("Fort de Saint-Héribert", "Namur — depuis 2023",
|
|
"Un site souterrain sans réseau : la visite mobile fonctionne entièrement "
|
|
"hors-ligne, déclenchée par des balises."),
|
|
("Visit Namur", "Office de tourisme — depuis 2024",
|
|
"Une borne grand format à l'accueil du public."),
|
|
]
|
|
cw = (W - 2 * MARGIN - Inches(0.6)) / 3
|
|
y = Inches(3.15)
|
|
for i, (head, sub, body) in enumerate(cards):
|
|
x = MARGIN + i * (cw + Inches(0.3))
|
|
rect(s, x, y, cw, Inches(2.6), fill=th.surface, line=th.rule)
|
|
rect(s, x, y, cw, Pt(3), fill=th.accent)
|
|
text(s, x + Inches(0.38), y + Inches(0.38), cw - Inches(0.76), Inches(0.5),
|
|
[(head, 15, True, th.ink, th.title_font, 0)], line_spacing=1.18)
|
|
text(s, x + Inches(0.38), y + Inches(0.92), cw - Inches(0.76), Inches(0.3),
|
|
[(sub, 11, True, th.accent, th.body_font, 0)])
|
|
text(s, x + Inches(0.38), y + Inches(1.35), cw - Inches(0.76), Inches(1.1),
|
|
[(body, 11.5, False, th.muted, th.body_font, 0)], line_spacing=1.35)
|
|
text(s, MARGIN, Inches(6.05), Inches(11.0), Inches(0.6),
|
|
[("Le premier site installé n'a jamais été refait. Il a été mis à jour.",
|
|
13, False, th.ink, th.body_font, 0)], line_spacing=1.3)
|
|
footer(s, th, n, "Ils l'utilisent déjà")
|
|
return s
|
|
|
|
|
|
def slide_pricing(prs, th, n):
|
|
s = page_base(prs, th)
|
|
kicker_title(s, th, "Nos formules",
|
|
"Un abonnement mensuel par site. Pas de frais de développement.")
|
|
plans = [
|
|
("Essentiel", "39 € / mois", ["Visite web par QR code",
|
|
"1 Go de médias",
|
|
"Statistiques de fréquentation"]),
|
|
("Pro", "99 € / mois", ["Borne et application mobile",
|
|
"15 Go de médias",
|
|
"Assistant IA en option"]),
|
|
("Premium", "179 € / mois", ["Tous les canaux",
|
|
"50 Go de médias",
|
|
"Assistant IA inclus"]),
|
|
("Sur mesure", "sur devis", ["Stockage illimité",
|
|
"Statistiques avancées",
|
|
"Accompagnement dédié"]),
|
|
]
|
|
cw = (W - 2 * MARGIN - Inches(0.75)) / 4
|
|
y = Inches(3.15)
|
|
for i, (name, price, feats) in enumerate(plans):
|
|
x = MARGIN + i * (cw + Inches(0.25))
|
|
rect(s, x, y, cw, Inches(2.75), fill=th.surface, line=th.rule)
|
|
rect(s, x, y, cw, Pt(3), fill=th.accent)
|
|
text(s, x + Inches(0.3), y + Inches(0.32), cw - Inches(0.6), Inches(0.3),
|
|
[(name, 14, True, th.ink, th.title_font, 0)])
|
|
text(s, x + Inches(0.3), y + Inches(0.72), cw - Inches(0.6), Inches(0.35),
|
|
[(price, 16, True, th.accent, th.body_font, 0)])
|
|
cy = y + Inches(1.3)
|
|
for f in feats:
|
|
text(s, x + Inches(0.3), cy, cw - Inches(0.6), Inches(0.45),
|
|
[(f, 10.5, False, th.muted, th.body_font, 0)], line_spacing=1.25)
|
|
cy += Inches(0.42)
|
|
text(s, MARGIN, Inches(6.15), Inches(11.0), Inches(0.5),
|
|
[("Application publiée à votre nom sur les magasins, mode hors-ligne, "
|
|
"balises et notifications se règlent site par site, en dehors du plan.",
|
|
11, False, th.muted, th.body_font, 0)], line_spacing=1.3)
|
|
footer(s, th, n, "Nos formules")
|
|
return s
|
|
|
|
|
|
def slide_roadmap(prs, th, n):
|
|
s = page_base(prs, th)
|
|
kicker_title(s, th, "Ce qui arrive",
|
|
"Le produit continue d'avancer, et votre abonnement suit.")
|
|
rows = [
|
|
("Guide vocal proactif", "En test",
|
|
"Le commentaire se déclenche tout seul à l'approche d'une salle. Le "
|
|
"visiteur n'a plus rien à manipuler."),
|
|
("Lunettes connectées", "En développement",
|
|
"Le visiteur regarde une œuvre, pose sa question à voix haute et obtient "
|
|
"sa réponse, sans sortir son téléphone."),
|
|
("Plan de salles interactif", "Prévu",
|
|
"Un plan d'étage cliquable, pour les sites d'intérieur. C'est la demande "
|
|
"la plus fréquente."),
|
|
("Guide incarné à l'écran", "À l'étude",
|
|
"Un personnage qui accueille et répond sur la borne."),
|
|
]
|
|
y = Inches(3.25)
|
|
for name, status, body in rows:
|
|
rect(s, MARGIN, y, W - 2 * MARGIN, Inches(0.75), fill=th.surface, line=th.rule)
|
|
rect(s, MARGIN, y, Pt(3), Inches(0.75), fill=th.accent)
|
|
text(s, MARGIN + Inches(0.28), y + Inches(0.24), Inches(2.85), Inches(0.3),
|
|
[(name, 12.5, True, th.ink, th.title_font, 0)])
|
|
text(s, MARGIN + Inches(3.25), y + Inches(0.26), Inches(2.1), Inches(0.3),
|
|
[(status.upper(), 8.5, True, th.accent, th.body_font, 0)])
|
|
text(s, MARGIN + Inches(5.55), y + Inches(0.14), Inches(5.85), Inches(0.5),
|
|
[(body, 10, False, th.muted, th.body_font, 0)], line_spacing=1.22)
|
|
y += Inches(0.85)
|
|
footer(s, th, n, "Ce qui arrive")
|
|
return s
|
|
|
|
|
|
def slide_start(prs, th, n):
|
|
s = page_base(prs, th)
|
|
kicker_title(s, th, "Démarrer",
|
|
"Une visite en ligne avant la fin du mois.")
|
|
steps = [
|
|
("1", "On ouvre votre espace",
|
|
"Vos couleurs, votre logo, vos langues. Comptez une journée."),
|
|
("2", "Vous saisissez vos contenus",
|
|
"Vos équipes de médiation, avec notre accompagnement. Pas de développeur."),
|
|
("3", "Vous publiez",
|
|
"Le QR code est prêt. La borne et l'application suivent quand vous voulez."),
|
|
]
|
|
cw = (W - 2 * MARGIN - Inches(0.6)) / 3
|
|
y = Inches(3.15)
|
|
for i, (num, head, body) in enumerate(steps):
|
|
x = MARGIN + i * (cw + Inches(0.3))
|
|
rect(s, x, y, cw, Inches(2.3), fill=th.surface, line=th.rule)
|
|
rect(s, x, y, cw, Pt(3), fill=th.accent)
|
|
text(s, x + Inches(0.38), y + Inches(0.32), cw - Inches(0.76), Inches(0.5),
|
|
[(num, 26, True, th.accent, th.title_font, 0)])
|
|
text(s, x + Inches(0.38), y + Inches(0.95), cw - Inches(0.76), Inches(0.45),
|
|
[(head, 14, True, th.ink, th.title_font, 0)], line_spacing=1.18)
|
|
text(s, x + Inches(0.38), y + Inches(1.5), cw - Inches(0.76), Inches(0.7),
|
|
[(body, 11, False, th.muted, th.body_font, 0)], line_spacing=1.3)
|
|
text(s, MARGIN, Inches(5.85), Inches(11.0), Inches(0.6),
|
|
[("Essai gratuit, sans engagement, avec vos vrais contenus.", 15, True,
|
|
th.ink, th.body_font, 0)], line_spacing=1.3)
|
|
footer(s, th, n, "Démarrer")
|
|
return s
|
|
|
|
|
|
def slide_closing(prs, th):
|
|
s = page_base(prs, th, dark=True)
|
|
rect(s, 0, H - Inches(0.9), W, Inches(0.9), fill=th.accent)
|
|
text(s, MARGIN, Inches(2.4), Inches(10.6), Inches(2.2),
|
|
[("Faisons vivre\nvotre lieu autrement.", 40, True,
|
|
RGBColor(0xFF, 0xFF, 0xFF), th.title_font, 0)], line_spacing=1.1)
|
|
text(s, MARGIN, Inches(4.75), Inches(9.8), Inches(1.0),
|
|
[("MyInfoMate — une solution Unov\n"
|
|
"myinfomate.be · fransolet.thomas@gmail.com", 15, False,
|
|
RGBColor(0xB8, 0xC2, 0xCE), th.body_font, 0)], line_spacing=1.45)
|
|
if LOGO.exists():
|
|
s.shapes.add_picture(str(LOGO), W - MARGIN - Inches(1.4), Inches(0.8),
|
|
height=Inches(1.2))
|
|
return s
|
|
|
|
|
|
def build(out_path):
|
|
prs = Presentation()
|
|
prs.slide_width, prs.slide_height = W, H
|
|
th = Theme(branded=True)
|
|
n = 2
|
|
|
|
slide_cover(prs, th)
|
|
slide_problem(prs, th, n); n += 1
|
|
slide_promise(prs, th, n); n += 1
|
|
slide_channels(prs, th, n); n += 1
|
|
|
|
slide_feature(prs, th, n, "Vous gardez la main",
|
|
"Votre équipe met à jour, seule, le jour même.",
|
|
"L'outil de saisie est fait pour des chargés de médiation, pas "
|
|
"pour des informaticiens.",
|
|
["Un texte, une photo, un son : vous corrigez et vous publiez.",
|
|
"Dix langues de visite, avec traduction proposée automatiquement.",
|
|
"Vous composez vous-même l'écran d'accueil, avec un aperçu en "
|
|
"direct de ce que verra le visiteur.",
|
|
"Plusieurs comptes, avec des droits différents selon les rôles."],
|
|
"cms.png"); n += 1
|
|
|
|
slide_feature(prs, th, n, "Vos contenus",
|
|
"Quatorze façons de raconter, à composer librement.",
|
|
"Rien à développer : un nouveau parcours se configure en quelques "
|
|
"heures.",
|
|
["Article, carte, diaporama, vidéo, audio, document, agenda.",
|
|
"Quiz, jeu, parcours, événement, page web.",
|
|
"Chaque contenu existe dans toutes vos langues.",
|
|
"Vos couleurs et votre logo s'appliquent partout, sans "
|
|
"intervention."],
|
|
"formats.png"); n += 1
|
|
|
|
slide_feature(prs, th, n, "Un guide qui répond",
|
|
"Vos visiteurs posent leurs questions. À votre contenu.",
|
|
"L'assistant ne répond qu'à partir de ce que vous avez écrit. "
|
|
"S'il ne sait pas, il le dit — il n'invente rien.",
|
|
["Disponible sur la borne, le mobile et le web.",
|
|
"Répond dans la langue du visiteur.",
|
|
"Vous rédigez vous-même les réponses de repli sur vos sujets "
|
|
"sensibles.",
|
|
"Aucune donnée nominative conservée."],
|
|
"assistant.png"); n += 1
|
|
|
|
slide_feature(prs, th, n, "Ce que vos visiteurs vous apprennent",
|
|
"Les questions restées sans réponse deviennent votre feuille de "
|
|
"route.",
|
|
"Vous voyez ce que le public cherche et ne trouve pas. C'est "
|
|
"rarement ce qu'on imagine.",
|
|
["La liste des questions auxquelles rien n'a pu répondre.",
|
|
"Fréquentation par jour, contenus les plus et les moins consultés.",
|
|
"Quiz terminés, points d'intérêt visités, QR codes scannés.",
|
|
"Un rapport exportable pour votre conseil ou votre tutelle."],
|
|
"questions.png"); n += 1
|
|
|
|
slide_feature(prs, th, n, "Parcours et jeux de piste",
|
|
"La même mécanique sert la visite guidée et l'animation scolaire.",
|
|
"Une suite d'étapes, avec ou sans carte, avec ou sans énigme à "
|
|
"résoudre pour avancer.",
|
|
["Étapes libres ou imposées, révélées au fur et à mesure.",
|
|
"Énigmes bloquantes, minuteur, écran de réussite.",
|
|
"Une étape se déclenche par la position, une balise ou un QR code.",
|
|
"Vendu comme animation famille ou groupe scolaire."],
|
|
"parcours.png"); n += 1
|
|
|
|
slide_feature(prs, th, n, "Sans réseau, sans installation",
|
|
"Une cave, un fort, un site rural : ça marche quand même.",
|
|
"Le visiteur télécharge la visite avant d'entrer. Tout reste "
|
|
"disponible ensuite, sans aucune connexion.",
|
|
["Textes, images et audio stockés sur le téléphone.",
|
|
"Des balises posées en salle proposent le bon contenu au bon "
|
|
"endroit, sans manipulation.",
|
|
"Les statistiques remontent toutes seules au retour du réseau.",
|
|
"Peu de solutions du marché le font réellement."],
|
|
"horsligne.png", portrait=True); n += 1
|
|
|
|
slide_feature(prs, th, n, "À votre nom",
|
|
"Vos visiteurs voient votre marque, pas la nôtre.",
|
|
"Votre lieu a son propre espace : ses couleurs, son icône, son "
|
|
"adresse.",
|
|
["Application publiée sous le nom de votre lieu sur les magasins.",
|
|
"Visite web sur une adresse qui vous est dédiée.",
|
|
"Écran d'accueil et de chargement à votre image.",
|
|
"Vos contenus restent les vôtres, et restent exportables."],
|
|
"marqueblanche_2.png", portrait=True); n += 1
|
|
|
|
slide_references(prs, th, n); n += 1
|
|
slide_pricing(prs, th, n); n += 1
|
|
slide_roadmap(prs, th, n); n += 1
|
|
slide_start(prs, th, n); n += 1
|
|
slide_closing(prs, th)
|
|
|
|
cp = prs.core_properties
|
|
cp.author = "MyInfoMate"
|
|
cp.last_modified_by = "MyInfoMate"
|
|
cp.title = "MyInfoMate — la visite augmentée de votre lieu"
|
|
cp.subject = ""
|
|
cp.comments = ""
|
|
cp.keywords = ""
|
|
cp.category = ""
|
|
cp.content_status = ""
|
|
cp.identifier = ""
|
|
cp.language = "fr-BE"
|
|
cp.revision = 1
|
|
|
|
out_path.parent.mkdir(parents=True, exist_ok=True)
|
|
count = len(prs.slides._sldIdLst)
|
|
prs.save(str(out_path))
|
|
sanitize_package(out_path)
|
|
return out_path, count
|
|
|
|
|
|
if __name__ == "__main__":
|
|
path, count = build(ROOT / "MyInfoMate_prospect.pptx")
|
|
print("%s (%d slides)" % (path, count))
|