Presentation pipeline
Folien, Folien, Folien
In meiner Lehrveranstaltung zu “Forschungsmethoden und Statistik” habe ich meine Folien mithilfe von quarto
und revealjs
erstellt. Das war natürlich nicht immer so.
Früher, in der guten alten Zeit, habe ich wie jeder vernünftige Mensch meine Folien mit LaTeX beamer
erstellt. 2023 sprach allerdings kaum mehr etwas dafür beamer
zu verwenden, denn alle Präsentation mit revealjs
sahen einfach gut aus – und das quasi “Out of the box”. Wer will nicht dass seine Folien geil aussehen? Also musste ich einen neuen Workflow aufbauen. Die quarto
Dokumente schreibe ich in RStudio und zur schnellen Ansicht rendere ich auch direkt in RStudio. Das Ergebnis sind .html files. Ich brauche aber ebenso Handouts für meine Studierenden und hätte selbst gerne noch eine .pdf Version meiner Präsentation falls alle Browser streiken und Gott bewahre, etwas ähnliches ist mir bereits passiert. Üblicherweise trage ich meine files auf einem USB Stick herum. USB-Sticks bleiben gerne mal wo liegen oder stecken und daher wäre es gut, ein vernünftiges Backup in der Cloud zu haben, um im Falle des Falles auch so auf die Präsentation zugreifen zu können.
Dieser Post wird sich damit beschäftigen, einen für mich funktionierenden automatisierten Ablauf für meine quarto
Präsentationen zu erstellen. Folgender Ablauf ist geplant:
- Render meine
quarto
Präsentationen - Erstelle pdfs aus diesen
- Erstelle pdf Handouts
- Erstelle ein Backup auf google drive
Bisher hatte ich für solche Aufgaben Makefiles1. Damit bin ich nicht so schlecht gefahren – allerdings musste ich die Makefiles immer wieder anpassen bzw. in einen neuen Ordner kopieren wenn eine neue Präsentation erstellt wird. Außerdem sagt mir jeder ich muss mehr in Python machen. Ok, wenn ich das Ganze also in Python machen soll, dann will ich auch folgende Wohlfühl-Features haben:
- Multiple
.qmd
Files sollen parallel gerendert etc. werden2 - Das Herumkopieren find ich nicht so prickelnd – daher wäre es toll wenn ich den Prozess direkt aus der bash triggern könnte.
- Naja und es soll in
Python
passieren.
Rendern
Das ist vermutlich der einfachste Part.
Man muss nur sicherstellen quarto
installiert zu haben. Am besten man holt sich das neueste RStudio, dann hat man quarto
bereits an Board, oder man installiert es sich separat3.
Man kann nun:
- RStudio verwenden um das
.qmd
zu schreiben und auf den “render” Button drücken - Was viel cooler ist: in der bash4 rendern.
In der bash rendern ist nicht nur cooler, sondern auch der erste Schritt um das ganze Prozedere zu automatisieren. So far so good. Die pdfs zu erstellen wird ein wenig schwieriger.
pdfs erstellen
Ich hab viel Zeit mit Google verbracht um dieses Problem zu lösen. Ziel ist es .html slides automatisch zu einem .pdf zu konvertieren. Decktape ist dafür ein wirklich ausgezeichnetes tool. Für decktape gibt es auch ein Dockerimage5 \(\rightarrow\) man muss sich keine Gedanken darüber machen, welche obskure andere Software man noch installieren muss, weil alles schon mit dem Dockercontainer verschifft wird und zwar genau in den sicheren Hafen unseres Computers.
Also folgende To-Dos:
- Docker holen
- Docker image pullen
- Docker image verwenden \(\rightarrow\) das ist genau was wir jetzt machen werden
Um jetzt unsere .html Präsentation in .pdf Folien zu verwandeln übergebe ich den Folder in dem die Dateien liegen an den Docker-Container. Das Ganze ist wieder nur eine einzige Zeile und funktioniert folgendermaßen:
Terminal
Was passiert hier?
docker run
: okay, wir wollen irgendwas in docker machen, also soll das Image das später genannt wird (astefanutti/decktape
) laufen--rm
: es wird saubergemacht nachdem docker fertig ist-t
: damit sehen wir was passiert (stdin and stdout werden attached)-v
: wir wollen was mounten und zwar:ordner/auf/meinem/computer:/container
. Decktape rechtnet damit, dass das .html file im Ordner “slides” liegt, daher wird der Ordner vom eigenen Rechner dort reingemountet.--size='2048x1536'
: so funktioniert es bei mir. Ich habe einige Einstellungen probiert – es läuft.reveal
offensichtlich arbeiten wir mit reveal-js.FILE.html FILE.pdf
hier dürfen wir noch einen Namen vergeben für das .pdf!
Das Ergebnis ist FILE.pdf
, eine pdf Verison der html Präsentation.
pdf handout
Wir wollen Handouts erstellen, die 4 Folien auf einer Seite darstellen, verwenden wir pdfjam. Das geht ratz-fatz. Kaum hat man auf Enter gedrückt ist das file auch schon da. Wir brauchen wieder nur eine Zeile um ein Handout zu erhalten.
Ein Skript um alles zu builden
Jetzt haben wir alle einzelnen Schritte beisammen (3 Zeilen) um unsere Präsentation zu erstellen, eine .pdf Version und ein Handout. Das ist gut, allerdings wollen wir das natürlich nicht jedesmal händisch ausführen, sondern ein Skript anfertigen, das die Schritte automatisiert macht. Ich habe manchmal auch mehrere quarto
Präsentationsfiles in einem Ordner liegen und möchte nicht jedes Mal darüber nachdenken welches ich nun rendern will. Das Skript sollte also folgendes können:
- render .qmd files – decktape – pdfjam
- für alle
quarto
files im Ordner - parallel (da die Files z. T. längere Laufzeiten haben und mein Laptop > 10 Kerne hat) – es ist mir kurz gesagt egal ob er tw. umsonst rendert, ich will nur keine Zeit damit verschwenden über irgendwelche Files nachzudenken.
Das hier gezeigte Python Skript definiert eine Funktion process_file
die im wesentlichen 3 Befehle ausführt: render – decktape – pdfjam. Die Dateinamen werden von den quarto
Dokumenten übernommen und die Endungen angepasst. Um alles parallel ablaufen zu lassen wird hier der ThreadPoolExecutor
verwendet.
Python
#| eval: FALSE
#| echo: true
from concurrent.futures import ThreadPoolExecutor
import os
import glob
import re
import sys
args = sys.argv[1:]
def process_file(val): # Function
new_file_html = re.sub("qmd$", "html", val)
new_file_pdf = re.sub("qmd$", "pdf", val)
new_file_handout_pdf = re.sub(".qmd$", "_handout.pdf", val)
run_quarto = "quarto render " + val
run_pdf = f"docker run --rm -t -v {file_path}:/slides -v :{file_path} astefanutti/decktape --size='2048x1536' reveal {new_file_html} {new_file_pdf}"
run_pdf_handout = f"pdfjam --nup 2x2 --landscape --a4paper {new_file_pdf} --outfile {new_file_handout_pdf}"
os.system(run_quarto) # render
os.system(run_pdf) # decktape
os.system(run_pdf_handout) # pdfjam
if __name__ == "__main__":
file_path = os.getcwd()
quarto_files = "*.qmd"
matching_files = glob.glob(os.path.join(file_path, quarto_files))
file_names = [os.path.basename(pth) for pth in matching_files]
with ThreadPoolExecutor(max_workers=4) as executor:
executor.map(process_file, file_names)
if args[0] == "save":
print("backup to google drive")
os.system("bash -i -c backupLV2google")
- 1
-
Argumente werden eingesammelt. Ziel ist es einen Input zu ermöglichen in der Form:
render_praes save
bzw.render_praes
. Wenn ‘save’ angegeben ist, dann soll von dem Folder auch ein backup gemacht werden (das wird jetzt nicht im Detail dargestellt – vielleicht in einem anderen Post mal). - 2
- Hier hat mir als N00b ChatGPT geholfen. Meine schön programmierte Schleife wurde zu diesem Konstrukt umformuliert.
- 3
- Wenn ‘save’ angegeben wird, wird dieser Block getriggert. Den Teil kann man natürlich weglassen wenn man es nicht braucht.
Platzierung des Python Skripts
Um dieses Skript ohne copy/paste in unterschiedlichen Projekten verwenden zu können, will ich es zentral zur Verfügung stellen und zwar so, dass es immer wenn eine Linux Terminal geöffnet wird zur Verfügung steht. Wie machen wir das?
- Wechsle in das Verzeichnis in dem das Skript liegt
cd da/is/mein/skript
- Mache das Python Skript ausführbar:
chmod +x render_praes.py
- Kopiere/verschiebe das Skript an einen zentraleren Ort6 wie z. B. den
~/bin
Ordner. Hat keinen derartigen Ordner, macht an zuerstmkdir ~/bin
und danncp render_praes.py ~/bin
um das Skript reinzukopieren. - Dann fügt man einen
alias
in die~/.bashrc
ein. Damit stellt man das Skript unter dem geannten Namen zur Verfügung – daher ist das der Moment, in dem man sich für einen Namen entscheiden muss. Hier ist der Name mitrender_praes
festgelegt.
Um die Funktion auszuprobieren startet man ein neues Terminal, wechselt in den Ordner mit zumindest einer quarto
revealjs
Präsentation und schreibt render_preas
.
Boom . Es sollte nun zuerst quarto
rendern, decktape das .pdf erzeugen und pdfjam
anschließend das Handout.
Und währenddessen kann man mal gemütlich einen Kaffee trinken gehen.
Footnotes
Das ist eine wirklich gute kurze Einführung in Makefiles https://kbroman.org/minimal_make/↩︎
Man lebt ja nicht ewig!↩︎
Unter Linux muss man sich ein .deb file downloaden und das installieren. Ich würde RStudio empfehlen.↩︎
STG + ALT + T öffnet ein Terminal in Linux.↩︎
Man muss sich wirklich nicht mit Docker auskennen um Docker verwenden zu können!↩︎
ist nicht unbedingt notwendig – aus Erfahrung kann ich aber sagen dass es sehr praktisch ist wenn man weiß wo solche Skripts liegen.↩︎