193 lines
8.7 KiB
Python
193 lines
8.7 KiB
Python
#!/usr/bin/env python3
|
||
"""Folie 7 – Integrationsserver / Event-Sourcing-Anbindung (DEUTSCH, adesso-Stil).
|
||
Eingaenge (Order Entry/CLAB, Probeneingang [Atras+PVT], Geraete, Middleware, MiBi/Hybase,
|
||
Stammdaten) -> Adapter-Ebene -> Event Store -> unten Event-Prozessoren, Projektionen,
|
||
Konsumenten (SinaBI, Archivierung). Hintergrund: solides dunkles Blau (BG)."""
|
||
|
||
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, MSO_CONNECTOR
|
||
from pptx.oxml.ns import qn
|
||
from adesso_style import (WHITE, INK, BLUE, TEAL, GREEN, CORAL, LIGHT, MUTE, RECF,
|
||
F, FC, chrome)
|
||
|
||
BG = RGBColor(0x0A, 0x3D, 0x6B)
|
||
GRAY = RGBColor(0xAE, 0xB7, 0xC2)
|
||
ADC = RGBColor(0xCF, 0xE2, 0xF7)
|
||
SUBC = RGBColor(0x3C, 0x4A, 0x5A) # zweite Zeile – dunkel & gut lesbar
|
||
|
||
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), adj=None):
|
||
sh = s.shapes.add_shape(kind, Inches(x), Inches(y), Inches(w), Inches(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 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=11, color=WHITE, bold=False, italic=False,
|
||
align=PP_ALIGN.LEFT, font=F, anchor=MSO_ANCHOR.MIDDLE):
|
||
tb = s.shapes.add_textbox(Inches(x), Inches(y), Inches(w), Inches(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 conn(x1, y1, x2, y2, color=LIGHT, w=1.6, both=False):
|
||
c = s.shapes.add_connector(MSO_CONNECTOR.STRAIGHT, Inches(x1), Inches(y1),
|
||
Inches(x2), Inches(y2))
|
||
c.line.color.rgb = color; c.line.width = Pt(w); c.shadow.inherit = False
|
||
ln = c.line._get_or_add_ln()
|
||
if both:
|
||
ln.append(ln.makeelement(qn('a:headEnd'), {'type': 'triangle'}))
|
||
ln.append(ln.makeelement(qn('a:tailEnd'), {'type': 'triangle'}))
|
||
return c
|
||
|
||
|
||
def group(x, y, w, h, title, tsize=11.5):
|
||
shp(MSO_SHAPE.ROUNDED_RECTANGLE, x, y, w, h, fill=None, line=LIGHT, lw=Pt(1.25), adj=0.04)
|
||
txt(x, y + 0.05, w, 0.26, title, size=tsize, color=WHITE, bold=True,
|
||
align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.TOP)
|
||
|
||
|
||
def box(x, y, w, h, title, sub=None, fill=WHITE, tcol=INK, tsize=10.5, line=None, lw=Pt(1)):
|
||
shp(MSO_SHAPE.ROUNDED_RECTANGLE, x, y, w, h, fill=fill, line=line, lw=lw, adj=0.16)
|
||
tb = s.shapes.add_textbox(Inches(x + 0.05), Inches(y), Inches(w - 0.10), Inches(h))
|
||
tf = tb.text_frame; tf.word_wrap = True; tf.vertical_anchor = MSO_ANCHOR.MIDDLE
|
||
tf.margin_left = tf.margin_right = tf.margin_top = tf.margin_bottom = Pt(0)
|
||
p = tf.paragraphs[0]; p.alignment = PP_ALIGN.CENTER; p.line_spacing = 0.98
|
||
r = p.add_run(); r.text = title; r.font.size = Pt(tsize); r.font.bold = True
|
||
r.font.name = F; r.font.color.rgb = tcol
|
||
if sub is not None:
|
||
p.space_after = Pt(2)
|
||
p2 = tf.add_paragraph(); p2.alignment = PP_ALIGN.CENTER; p2.line_spacing = 0.98
|
||
r2 = p2.add_run(); r2.text = sub; r2.font.size = Pt(tsize - 0.5)
|
||
r2.font.name = F; r2.font.color.rgb = SUBC
|
||
return tb
|
||
|
||
|
||
AD_Y, AD_H = 3.50, 0.48
|
||
def adapter(x, w, title):
|
||
box(x, AD_Y, w, AD_H, title, fill=ADC, tcol=INK, tsize=9, line=BLUE, lw=Pt(1.1))
|
||
|
||
|
||
# ---- Hintergrund + Header --------------------------------------------------
|
||
shp(MSO_SHAPE.RECTANGLE, 0, 0, SW.inches, SH.inches, fill=BG)
|
||
chrome(s, "Event Sourcing — Integrationsserver", 8)
|
||
|
||
# ---- Legende ---------------------------------------------------------------
|
||
ly = 0.95
|
||
def legsq(x, color, label, lw_txt):
|
||
shp(MSO_SHAPE.RECTANGLE, x, ly + 0.02, 0.15, 0.15, fill=color)
|
||
txt(x + 0.21, ly - 0.03, lw_txt, 0.26, label, size=10, color=WHITE, anchor=MSO_ANCHOR.MIDDLE)
|
||
legsq(0.40, CORAL, "Event Store", 1.2)
|
||
legsq(1.76, GREEN, "Prop-Limbach", 1.3)
|
||
legsq(3.28, ADC, "Adapter (own)", 1.4)
|
||
legsq(4.90, WHITE, "System", 0.9)
|
||
txt(6.30, 0.92, 6.63, 0.3,
|
||
"↕ publizieren & abonnieren ↓ publizieren grau = Kommunikation",
|
||
size=10, color=LIGHT, italic=True, anchor=MSO_ANCHOR.MIDDLE, align=PP_ALIGN.RIGHT)
|
||
|
||
# ---- EVENT STORE -----------------------------------------------------------
|
||
ST_X, ST_Y, ST_W, ST_H = 0.40, 4.18, 12.53, 0.60
|
||
shp(MSO_SHAPE.ROUNDED_RECTANGLE, ST_X, ST_Y, ST_W, ST_H, fill=CORAL, line=INK, lw=Pt(3.5), adj=0.10)
|
||
txt(ST_X, ST_Y + 0.06, ST_W, 0.30, "Event Store — append-only Event-Log",
|
||
size=15, color=INK, bold=True, align=PP_ALIGN.CENTER)
|
||
txt(ST_X, ST_Y + 0.35, ST_W, 0.22, "kanonische Wahrheit · Replay-fähig · (Kafka)",
|
||
size=10.5, color=INK, align=PP_ALIGN.CENTER)
|
||
|
||
# ===========================================================================
|
||
# OBEN – Systeme + Adapter-Ebene
|
||
# ===========================================================================
|
||
TGY, TGH = 1.28, 2.78
|
||
|
||
# --- 1: Order Entry ueber CLAB ---------------------------------------------
|
||
group(0.40, TGY, 2.70, TGH, "Order Entry über CLAB")
|
||
box(0.52, 1.62, 1.16, 0.42, "LabAccess")
|
||
box(1.82, 1.62, 1.16, 0.42, "MDN")
|
||
box(1.18, 2.22, 1.14, 0.44, "AB+M")
|
||
box(1.05, 2.84, 1.40, 0.42, "CLAB (LIS)", fill=GREEN)
|
||
adapter(0.78, 1.94, "CLAB-Adapter")
|
||
conn(1.10, 2.04, 1.55, 2.22); conn(2.40, 2.04, 1.95, 2.22)
|
||
conn(1.75, 2.66, 1.75, 2.84); conn(1.75, 3.26, 1.75, AD_Y)
|
||
conn(1.75, AD_Y + AD_H, 1.75, ST_Y, both=True)
|
||
|
||
# --- 2: Probeneingang (Atras SL + PVT) -------------------------------------
|
||
group(3.26, TGY, 1.95, TGH, "Probeneingang")
|
||
box(3.34, 2.30, 0.86, 0.52, "Atras SL")
|
||
box(4.27, 2.30, 0.86, 0.52, "PVT")
|
||
adapter(3.34, 1.79, "Adapter (Probe → Event)")
|
||
conn(2.32, 2.50, 3.34, 2.56, color=GRAY, both=True) # Praeanalytik-Kommunikation
|
||
txt(2.28, 2.04, 1.30, 0.34, "Präanalytik-\nKomm.", size=8, color=GRAY, italic=True,
|
||
align=PP_ALIGN.CENTER)
|
||
conn(3.77, 2.82, 3.95, AD_Y); conn(4.70, 2.82, 4.55, AD_Y)
|
||
conn(4.235, AD_Y + AD_H, 4.235, ST_Y)
|
||
|
||
# --- 3: Geraete -------------------------------------------------------------
|
||
group(5.37, TGY, 1.78, TGH, "Geräte-Adapter")
|
||
box(5.49, 2.10, 1.54, 0.70, "Geräte", "Sysmex · Analyzer")
|
||
adapter(5.45, 1.62, "Adapter (Device → Event)")
|
||
conn(6.26, 2.80, 6.26, AD_Y); conn(6.26, AD_Y + AD_H, 6.26, ST_Y, both=True)
|
||
|
||
# --- 4: Middleware ----------------------------------------------------------
|
||
group(7.31, TGY, 1.78, TGH, "Middleware")
|
||
box(7.43, 2.10, 1.54, 0.70, "Middleware-Systeme", "Myla/Maestria · LabOps · Liason")
|
||
adapter(7.39, 1.62, "Adapter (Middleware → Event)")
|
||
conn(8.20, 2.80, 8.20, AD_Y); conn(8.20, AD_Y + AD_H, 8.20, ST_Y, both=True)
|
||
|
||
# --- 5: MiBi (Hybase) -------------------------------------------------------
|
||
group(9.25, TGY, 1.70, TGH, "MiBi")
|
||
box(9.37, 2.10, 1.46, 0.70, "Hybase", "Resistenz / ID-Daten")
|
||
adapter(9.33, 1.54, "Adapter (MiBi → Event)")
|
||
conn(10.10, 2.80, 10.10, AD_Y); conn(10.10, AD_Y + AD_H, 10.10, ST_Y, both=True)
|
||
|
||
# --- 6: Stammdaten ----------------------------------------------------------
|
||
group(11.11, TGY, 1.82, TGH, "Stammdaten")
|
||
box(11.23, 2.10, 1.58, 0.70, "Stammdaten-Quellen", "SinaCRM · OTS/Znooni")
|
||
adapter(11.19, 1.66, "Adapter (MasterData → Event)")
|
||
conn(12.02, 2.80, 12.02, AD_Y); conn(12.02, AD_Y + AD_H, 12.02, ST_Y)
|
||
|
||
# ===========================================================================
|
||
# UNTEN – Event-Prozessoren · Projektionen · Konsumenten
|
||
# ===========================================================================
|
||
BGY, BGH = 4.96, 1.96
|
||
|
||
group(0.40, BGY, 2.95, BGH, "Event-Prozessoren")
|
||
box(0.56, 5.42, 2.63, 0.55, "Mapping / Transformation")
|
||
box(0.56, 6.12, 2.63, 0.55, "Routing Engine")
|
||
conn(1.85, ST_Y + ST_H, 1.85, BGY, both=True)
|
||
|
||
group(3.50, BGY, 5.85, BGH, "Projektionen")
|
||
box(3.66, 5.46, 1.78, 1.02, "Auftragsstatus")
|
||
box(5.56, 5.46, 1.78, 1.02, "Audit Trail")
|
||
box(7.46, 5.46, 1.73, 1.02, "Reporting")
|
||
conn(6.40, ST_Y + ST_H, 6.40, BGY)
|
||
|
||
group(9.50, BGY, 3.43, BGH, "Konsumenten")
|
||
box(9.66, 5.38, 3.11, 0.68, "SinaBI (Power BI)", "Reporting / BI")
|
||
box(9.66, 6.12, 3.11, 0.68, "Archivierung · Vaultbox", "Roche Navify · LDT2")
|
||
conn(11.21, ST_Y + ST_H, 11.21, BGY)
|
||
|
||
out = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Folie7.pptx")
|
||
prs.save(out)
|
||
print("saved", out)
|