178 lines
7.7 KiB
Python
178 lines
7.7 KiB
Python
#!/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)
|