#!/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)