173 lines
7.3 KiB
Python
173 lines
7.3 KiB
Python
#!/usr/bin/env python3
|
||
"""Folie 6 – Core / Rationale (DEUTSCH): kompakte Landscape links, Argumente als Karten rechts.
|
||
(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, sp=None):
|
||
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
|
||
if sp is not None: p.space_after = Pt(sp)
|
||
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, size=10):
|
||
w = Inches(0.30 + 0.085 * len(text)); h = Inches(0.27)
|
||
x = cx - w / 2
|
||
shp(MSO_SHAPE.ROUNDED_RECTANGLE, x, y, w, h, fill=INK, adj=0.5)
|
||
txt(x, y, w, h, text, size=size, color=WHITE, align=PP_ALIGN.CENTER, font=FC)
|
||
|
||
|
||
def core_tag(x, y):
|
||
w, h = Inches(0.54), Inches(0.23)
|
||
shp(MSO_SHAPE.ROUNDED_RECTANGLE, x, y, w, h, fill=INK, adj=0.5)
|
||
txt(x, y, w, h, "CORE", size=8.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, "Warum Event Sourcing passt", 7)
|
||
|
||
# ===========================================================================
|
||
# LINKS – kompakte Landscape
|
||
# ===========================================================================
|
||
LCX = Inches(3.35)
|
||
|
||
# Quellen
|
||
shp(MSO_SHAPE.ROUNDED_RECTANGLE, Inches(0.7), Inches(1.58), Inches(5.3), Inches(0.7),
|
||
fill=WHITE, adj=0.12)
|
||
txt(Inches(0.92), Inches(1.66), Inches(4.9), Inches(0.26), "Quellen",
|
||
size=12.5, color=INK, bold=True, anchor=MSO_ANCHOR.TOP)
|
||
txt(Inches(0.92), Inches(1.94), Inches(4.9), Inches(0.28),
|
||
"Publisher (publizieren) · Subsysteme (konsumieren & publizieren)",
|
||
size=10.5, color=MUTE, anchor=MSO_ANCHOR.TOP)
|
||
|
||
shp(MSO_SHAPE.DOWN_ARROW, LCX - Inches(0.13), Inches(2.30), Inches(0.26), Inches(0.26),
|
||
fill=CORAL)
|
||
|
||
# Hot Store (CORE) inkl. Event-Prozessoren
|
||
hy, hh = Inches(2.58), Inches(0.98)
|
||
shp(MSO_SHAPE.ROUNDED_RECTANGLE, Inches(1.35), hy, Inches(4.0), hh,
|
||
fill=CORAL, line=INK, lw=Pt(3), adj=0.08)
|
||
core_tag(Inches(1.50), hy + Inches(0.12))
|
||
txt(Inches(1.35), hy + Inches(0.08), Inches(4.0), Inches(0.34),
|
||
"Event Store — Hot Store", size=15, color=INK, bold=True, align=PP_ALIGN.CENTER)
|
||
txt(Inches(1.35), hy + Inches(0.40), Inches(4.0), Inches(0.24),
|
||
"inkl. Event-Prozessoren", size=10.5, color=INK, align=PP_ALIGN.CENTER)
|
||
chip(LCX, hy + hh - Inches(0.34), "Kafka")
|
||
|
||
shp(MSO_SHAPE.DOWN_ARROW, LCX - Inches(0.13), hy + hh + Inches(0.02),
|
||
Inches(0.26), Inches(0.24), fill=CORAL)
|
||
|
||
# Projektionen
|
||
py, ph = Inches(3.82), Inches(0.56)
|
||
shp(MSO_SHAPE.ROUNDED_RECTANGLE, Inches(1.35), py, Inches(4.0), ph, fill=WHITE, adj=0.16)
|
||
txt(Inches(1.55), py, Inches(2.0), ph, "Projektionen", size=12.5, color=INK, bold=True)
|
||
chip(Inches(4.25), py + ph / 2 - Inches(0.135), "Kafka Streams → PostgreSQL")
|
||
|
||
# Verteiler -> 3 Bloecke
|
||
boxes_y = Inches(4.72); mbh = Inches(1.28)
|
||
mb_w = Inches(1.65)
|
||
cold_x = Inches(0.7); cold_cx = cold_x + mb_w / 2
|
||
mon_x = Inches(2.55); mon_cx = mon_x + mb_w / 2
|
||
rep_x = Inches(4.40); rep_cx = rep_x + mb_w / 2
|
||
bus_y = py + ph + Inches(0.10)
|
||
shp(MSO_SHAPE.RECTANGLE, LCX - 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.10), bus_y, Inches(0.20), boxes_y - bus_y, fill=LIGHT)
|
||
|
||
|
||
def minibox(x, title, tech, core=False):
|
||
fill = CORAL if core else WHITE
|
||
line = INK if core else None
|
||
lw = Pt(3) if core else Pt(1)
|
||
shp(MSO_SHAPE.ROUNDED_RECTANGLE, x, boxes_y, mb_w, mbh, fill=fill, line=line, lw=lw, adj=0.10)
|
||
if core:
|
||
core_tag(x + Inches(0.12), boxes_y + Inches(0.12))
|
||
txt(x, boxes_y + (Inches(0.42) if core else Inches(0.26)), mb_w, Inches(0.5), title,
|
||
size=12, color=INK, bold=True, align=PP_ALIGN.CENTER)
|
||
chip(x + mb_w / 2, boxes_y + mbh - Inches(0.36), tech)
|
||
|
||
|
||
minibox(cold_x, "Cold\nStore", "PostgreSQL", core=True)
|
||
minibox(mon_x, "Monitoring", "Grafana")
|
||
minibox(rep_x, "Reporting", "Power BI")
|
||
|
||
# ===========================================================================
|
||
# RECHTS – Argumente als Karten
|
||
# ===========================================================================
|
||
cards = [
|
||
("Bewährte Technologie",
|
||
"Erprobt in regulierten Branchen mit hohem Volumen — z. B. ING setzt auf Kafka / Event-Streaming."),
|
||
("Flexibel",
|
||
"Neues System = neuer Adapter, ohne Eingriff in den Rest. Neue Event-Folgen & Projektionen jederzeit baubar."),
|
||
("Skalierbar",
|
||
"Selbst massive Lastzunahme wird durch mehr Hardware aufgefangen — horizontal, ohne Redesign."),
|
||
("Kosteneffizient",
|
||
"Wiederverwendbare Adapter statt Punkt-zu-Punkt. Open Source auf Standard-Hardware — kein Lock-in."),
|
||
("Auditierbar & revisionssicher",
|
||
"Append-only, vollständige & manipulationssichere Historie — vollständig replay-fähig."),
|
||
("Resilient & entkoppelt",
|
||
"Systeme fallen aus und holen per Replay auf; ein Log statt N² Schnittstellen."),
|
||
]
|
||
|
||
cw, ch = Inches(3.0), Inches(1.42)
|
||
col_x = [Inches(6.55), Inches(9.8)]
|
||
row_y = [Inches(1.62), Inches(3.30), Inches(4.98)]
|
||
for i, (title, body) in enumerate(cards):
|
||
x = col_x[i % 2]; y = row_y[i // 2]
|
||
shp(MSO_SHAPE.ROUNDED_RECTANGLE, x, y, cw, ch, fill=WHITE, adj=0.08)
|
||
shp(MSO_SHAPE.RECTANGLE, x + Inches(0.0), y + Inches(0.14), Inches(0.11), ch - Inches(0.28),
|
||
fill=CORAL)
|
||
txt(x + Inches(0.32), y + Inches(0.15), cw - Inches(0.5), Inches(0.36), title,
|
||
size=13.5, color=INK, bold=True, anchor=MSO_ANCHOR.TOP)
|
||
txt(x + Inches(0.32), y + Inches(0.55), cw - Inches(0.52), ch - Inches(0.68), body,
|
||
size=10.5, color=MUTE, anchor=MSO_ANCHOR.TOP)
|
||
|
||
out = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Folie6.pptx")
|
||
prs.save(out)
|
||
print("saved", out)
|