Files
limbach/folie5.py
T
marcus hinz 93bb19abf0 initial
2026-06-20 14:40:33 +02:00

178 lines
7.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Folie 5 Event Sourcing: das groessere Bild (DEUTSCH).
Quellen differenziert (Publisher / Subsysteme), Event-Prozessoren als Teil des Stores,
Hot Store & Cold Store als Kernfunktion (Coral, CORE-Tag), Projektionen dazwischen,
darunter Cold Store / Monitoring / Reporting. Tech-Impls als Chips. (adesso-Styling)
Hintergrund: solides dunkles Blau (BG) Farbe frei anpassbar."""
import os
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.enum.shapes import MSO_SHAPE
from adesso_style import (WHITE, INK, BLUE, TEAL, GREEN, CORAL, LIGHT, MUTE, RECF,
F, FC, chrome)
# ---- Hintergrundfarbe (solid, keine Transparenz) frei anpassbar ----------
BG = RGBColor(0x0A, 0x3D, 0x6B) # dunkles Blau
prs = Presentation()
prs.slide_width = Inches(13.333)
prs.slide_height = Inches(7.5)
SW, SH = prs.slide_width, prs.slide_height
s = prs.slides.add_slide(prs.slide_layouts[6])
def shp(kind, x, y, w, h, fill=None, line=None, lw=Pt(1), rot=0, adj=None):
sh = s.shapes.add_shape(kind, x, y, w, h)
if fill is None: sh.fill.background()
else: sh.fill.solid(); sh.fill.fore_color.rgb = fill
if line is None: sh.line.fill.background()
else: sh.line.color.rgb = line; sh.line.width = lw
if rot: sh.rotation = rot
if adj is not None:
try: sh.adjustments[0] = adj
except Exception: pass
sh.shadow.inherit = False
return sh
def txt(x, y, w, h, t, size=16, color=WHITE, bold=False, italic=False,
align=PP_ALIGN.LEFT, font=F, anchor=MSO_ANCHOR.MIDDLE):
tb = s.shapes.add_textbox(x, y, w, h); tf = tb.text_frame
tf.word_wrap = True; tf.vertical_anchor = anchor
tf.margin_left = tf.margin_right = tf.margin_top = tf.margin_bottom = Pt(0)
first = True
for line in t.split("\n"):
p = tf.paragraphs[0] if first else tf.add_paragraph()
first = False
p.alignment = align
r = p.add_run(); r.text = line
r.font.size = Pt(size); r.font.bold = bold; r.font.italic = italic
r.font.name = font; r.font.color.rgb = color
return tb
def chip(cx, y, text, fill=INK, fg=WHITE, size=11):
"""Tech-Impl als kleine Pill, zentriert um cx."""
w = Inches(0.34 + 0.092 * len(text)); h = Inches(0.30)
x = cx - w / 2
shp(MSO_SHAPE.ROUNDED_RECTANGLE, x, y, w, h, fill=fill, adj=0.5)
txt(x, y, w, h, text, size=size, color=fg, bold=False,
align=PP_ALIGN.CENTER, font=FC)
def core_tag(x, y):
w, h = Inches(0.62), Inches(0.26)
shp(MSO_SHAPE.ROUNDED_RECTANGLE, x, y, w, h, fill=INK, adj=0.5)
txt(x, y, w, h, "CORE", size=9.5, color=TEAL, bold=True,
align=PP_ALIGN.CENTER, font=FC)
# ---- solider Hintergrund (zuerst -> liegt hinten) --------------------------
bg = shp(MSO_SHAPE.RECTANGLE, 0, 0, SW, SH, fill=BG)
chrome(s, "System Landscape", 6)
CX = Inches(6.665) # Folienmitte (x)
# ---- Quellen: differenziert ------------------------------------------------
src_y, src_h = Inches(1.36), Inches(0.92)
def source_box(x, w, title, examples):
shp(MSO_SHAPE.ROUNDED_RECTANGLE, x, src_y, w, src_h, fill=WHITE, adj=0.10)
txt(x + Inches(0.28), src_y + Inches(0.10), w - Inches(0.56), Inches(0.32),
title, size=14.5, color=INK, bold=True, anchor=MSO_ANCHOR.TOP)
txt(x + Inches(0.28), src_y + Inches(0.46), w - Inches(0.56), Inches(0.36),
examples, size=12, color=MUTE, anchor=MSO_ANCHOR.TOP)
pub_x, pub_w = Inches(0.9), Inches(5.6)
sub_x, sub_w = Inches(6.83), Inches(5.6)
pub_cx = pub_x + pub_w / 2
sub_cx = sub_x + sub_w / 2
source_box(pub_x, pub_w, "Publisher (nur publizieren)",
"z. B. AB+M · LabAccess · MDN · Stammdaten — proprietär")
source_box(sub_x, sub_w, "Subsysteme (konsumieren & publizieren)",
"z. B. LIS · Geräte / Analyzer — proprietär")
# ---- Pfeile Quellen -> Hot Store ------------------------------------------
ay, ah = src_y + src_h + Inches(0.02), Inches(0.30)
shp(MSO_SHAPE.DOWN_ARROW, pub_cx - Inches(0.16), ay, Inches(0.32), ah, fill=CORAL)
txt(pub_cx + Inches(0.28), ay - Inches(0.01), Inches(1.6), ah + Inches(0.02),
"publizieren", size=11, color=LIGHT, italic=True)
shp(MSO_SHAPE.UP_DOWN_ARROW, sub_cx - Inches(0.11), ay - Inches(0.05), Inches(0.22),
ah + Inches(0.10), fill=CORAL)
txt(sub_cx - Inches(2.85), ay - Inches(0.01), Inches(2.5), ah + Inches(0.02),
"konsumieren & publizieren", size=11, color=LIGHT, italic=True, align=PP_ALIGN.RIGHT)
# ---- HOT STORE (Kern) + Event-Prozessoren (Teil des Stores) ----------------
hx, hy, hw, hh = Inches(3.4), Inches(2.62), Inches(6.53), Inches(1.50)
shp(MSO_SHAPE.ROUNDED_RECTANGLE, hx, hy, hw, hh, fill=CORAL, line=INK, lw=Pt(3.5), adj=0.07)
core_tag(hx + Inches(0.16), hy + Inches(0.13))
txt(hx, hy + Inches(0.05), hw, Inches(0.34),
"Event Store — Hot Store", size=20, color=INK, bold=True, align=PP_ALIGN.CENTER)
txt(hx, hy + Inches(0.40), hw, Inches(0.20),
"append-only · kanonische Wahrheit", size=12.5, color=INK, align=PP_ALIGN.CENTER)
chip(CX, hy + Inches(0.62), "z. B. Kafka")
# Event-Prozessoren als eingebetteter Bestandteil des Stores
ep_x, ep_y, ep_w, ep_h = hx + Inches(0.28), hy + Inches(0.98), hw - Inches(0.56), Inches(0.44)
shp(MSO_SHAPE.ROUNDED_RECTANGLE, ep_x, ep_y, ep_w, ep_h, fill=WHITE, adj=0.22)
txt(ep_x, ep_y, ep_w, ep_h,
"Event-Prozessoren (konsumieren & publizieren) · z. B. Proben-Routing",
size=11.5, color=INK, bold=True, align=PP_ALIGN.CENTER)
# ---- Hot -> Projektionen ---------------------------------------------------
shp(MSO_SHAPE.DOWN_ARROW, CX - Inches(0.16), hy + hh + Inches(0.03),
Inches(0.32), Inches(0.28), fill=CORAL)
# ---- PROJEKTIONEN ----------------------------------------------------------
px, py, pw, ph = Inches(3.4), Inches(4.46), Inches(6.53), Inches(0.66)
shp(MSO_SHAPE.ROUNDED_RECTANGLE, px, py, pw, ph, fill=WHITE, adj=0.14)
txt(px + Inches(0.3), py, Inches(3.0), ph, "Projektionen", size=16, color=INK, bold=True)
chip(px + pw - Inches(2.0), py + ph / 2 - Inches(0.15), "Kafka Streams → PostgreSQL")
# ---- Verteiler (Projektionen -> 3 Bloecke) --------------------------------
boxes_y = Inches(5.34)
bw = Inches(3.7); bh = Inches(1.56)
cold_x = Inches(0.9); cold_cx = cold_x + bw / 2
mon_x = Inches(4.815); mon_cx = mon_x + bw / 2
rep_x = Inches(8.73); rep_cx = rep_x + bw / 2
bus_y = py + ph + Inches(0.10)
shp(MSO_SHAPE.RECTANGLE, CX - Pt(0.75), py + ph, Pt(1.5), bus_y - (py + ph), fill=LIGHT)
shp(MSO_SHAPE.RECTANGLE, cold_cx, bus_y, rep_cx - cold_cx, Pt(1.5), fill=LIGHT)
for cx in (cold_cx, mon_cx, rep_cx):
shp(MSO_SHAPE.DOWN_ARROW, cx - Inches(0.11), bus_y, Inches(0.22), boxes_y - bus_y,
fill=LIGHT)
def block(x, title, body, tech, core=False):
fill = CORAL if core else WHITE
line = INK if core else None
lw = Pt(3.5) if core else Pt(1)
shp(MSO_SHAPE.ROUNDED_RECTANGLE, x, boxes_y, bw, bh, fill=fill, line=line, lw=lw, adj=0.07)
ty = boxes_y + (Inches(0.30) if core else Inches(0.20))
if core:
core_tag(x + Inches(0.16), boxes_y + Inches(0.16))
txt(x, ty, bw, Inches(0.42), title, size=17, color=INK, bold=True, align=PP_ALIGN.CENTER)
txt(x + Inches(0.25), ty + Inches(0.46), bw - Inches(0.5), Inches(0.5), body,
size=12.5, color=(INK if core else MUTE), align=PP_ALIGN.CENTER)
chip(x + bw / 2, boxes_y + bh - Inches(0.42), tech)
block(cold_x, "Cold Store",
"Archiv · Audit-Trail · Dokumente", "z. B. PostgreSQL", core=True)
block(mon_x, "Monitoring",
"Auftragsstatus / Auslastung", "z. B. Grafana")
block(rep_x, "Reporting",
"Bericht an Gruppe · KPIs", "z. B. Power BI")
out = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Folie5.pptx")
prs.save(out)
print("saved", out)