diff --git a/.gitignore b/.gitignore index 1955f9d..3315f2e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ _gereedschap/extract.py NOTITIES-intern.md 00-presentatie/notities-begeleider.json 00-presentatie/presentatie-met-notities.pdf +00-presentatie/presentatie-workshop-ai-agents.pptx diff --git a/00-lezing/lezing-wat-is-een-ai-agent.pptx b/00-lezing/lezing-wat-is-een-ai-agent.pptx new file mode 100644 index 0000000..9511c9e Binary files /dev/null and b/00-lezing/lezing-wat-is-een-ai-agent.pptx differ diff --git a/00-sessie/sessie-agents-en-projecten.pptx b/00-sessie/sessie-agents-en-projecten.pptx new file mode 100644 index 0000000..0939ce6 Binary files /dev/null and b/00-sessie/sessie-agents-en-projecten.pptx differ diff --git a/_gereedschap/maak_pptx.py b/_gereedschap/maak_pptx.py new file mode 100644 index 0000000..8465752 --- /dev/null +++ b/_gereedschap/maak_pptx.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 +"""Maakt een PowerPoint-bestand van een gebouwde presentatie uit deze repository. + +Elke dia wordt als beeld op een 16:9-slide gezet en de sprekersnotities komen in het +notitieveld, zodat PowerPoint ze in de sprekersweergave op jouw scherm toont terwijl de zaal +alleen de dia ziet. Het beeld is een afbeelding: de opmaak is precies gelijk aan de PDF, maar +de tekst is in PowerPoint niet te bewerken. Wijzigen doe je in maak_dias.py en dan opnieuw +bouwen. + +Gebruik: python3 ../_gereedschap/maak_pptx.py +Bijvoorbeeld in 00-sessie: python3 ../_gereedschap/maak_pptx.py sessie-agents-en-projecten.pdf + +Staat er een notities-begeleider.json naast maak_dias.py, dan gebruikt het script die +uitgebreide notities en zegt het erbij; dat bestand is privé, dus deel die pptx niet. +Vereist: pdftoppm (Poppler) en python-pptx. +""" +import json +import re +import subprocess +import sys +import tempfile +from pathlib import Path + +try: + from pptx import Presentation + from pptx.util import Inches +except ImportError: + sys.exit("python-pptx ontbreekt. Bijvoorbeeld: ~/.local/venvs/uitlegvideo/bin/python " + "../_gereedschap/maak_pptx.py ") + +pdf = Path(sys.argv[1]).resolve() +if not pdf.exists(): + sys.exit(f"niet gevonden: {pdf}") +MAP = pdf.parent +P = MAP / "project" +PRIVE = (MAP / "notities-begeleider.json").exists() + +# 1. dia's en PDF opnieuw bouwen, zodat beeld en notities bij elkaar horen +subprocess.run([sys.executable, str(MAP / "maak_dias.py")] + (["--notities"] if PRIVE else []), + check=True, capture_output=True) +deck = json.loads((P / "deck.json").read_text(encoding="utf-8")) +faces = "".join(f'' for f in deck["faces"].values()) +tmp = Path(tempfile.mkdtemp(prefix="pptx-")) +css = """ +@page { size: 1920px 1080px; margin: 0; } +html, body { margin: 0; padding: 0; } +* { box-sizing: border-box; margin: 0; padding: 0; } +section { position: relative; width: 1920px; height: 1080px; overflow: hidden; page-break-after: always; break-after: page; } +section:last-child { page-break-after: auto; } +aside { display: none; } +ul { padding-left: 1.1em; } +table { border-collapse: collapse; width: 100%; } +th, td { border: 1px solid #D8D2C4; padding: 12px 18px; text-align: left; vertical-align: top; } +img { max-width: 100%; } +""" +(tmp / "dias.html").write_text( + f'{faces}' + + "".join((P / "slides" / f"{sid}.html").read_text(encoding="utf-8") for sid in deck["order"]) + + "", encoding="utf-8") +subprocess.run(["google-chrome", "--headless=new", "--disable-gpu", "--no-pdf-header-footer", + "--virtual-time-budget=8000", f"--print-to-pdf={tmp / 'dias.pdf'}", (tmp / "dias.html").as_uri()], + check=True, capture_output=True) + +# 2. elke pagina als beeld +subprocess.run(["pdftoppm", "-r", "100", "-jpeg", "-jpegopt", "quality=88", + str(tmp / "dias.pdf"), str(tmp / "dia")], check=True) +beelden = sorted(tmp.glob("dia-*.jpg")) +if len(beelden) != len(deck["order"]): + sys.exit(f"{len(beelden)} beelden voor {len(deck['order'])} dia's; bouw de dia's opnieuw") + + +def notities(sid): + tekst = re.search(r"", + (P / "slides" / f"{sid}.html").read_text(encoding="utf-8"), re.S) + tekst = (tekst.group(1).strip() if tekst else "") + tekst = re.sub(r"<[^>]+>", "", tekst) + # de privénotities staan als BEDOELING: ... | ZEGGEN: ... op één regel; leesbaar maken + return "\n\n".join(d.strip() for d in tekst.split(" | ") if d.strip()) + + +# 3. de pptx: 16:9, elk beeld schermvullend, notities in het notitieveld +prs = Presentation() +prs.slide_width, prs.slide_height = Inches(13.333), Inches(7.5) +leeg = prs.slide_layouts[6] # blank +for n, sid in enumerate(deck["order"]): + dia = prs.slides.add_slide(leeg) + dia.shapes.add_picture(str(beelden[n]), 0, 0, width=prs.slide_width, height=prs.slide_height) + dia.notes_slide.notes_text_frame.text = notities(sid) +uit = pdf.with_suffix(".pptx") +prs.save(str(uit)) + +# 4. de dia-bestanden terugzetten op de publieke versie +subprocess.run([sys.executable, str(MAP / "maak_dias.py")], check=True, capture_output=True) +soort = "uitgebreide begeleidersnotities (privé, niet delen)" if PRIVE else "de notities uit de dia's" +print(f"{uit.name}: {uit.stat().st_size // 1024} kB, {len(deck['order'])} dia's, met {soort}")