commit 52ce98626c101368ca18b0e7f5f18f0ad75cafc5 Author: marcus hinz Date: Fri Jun 12 10:40:47 2026 +0200 initial diff --git a/limbach/.~lock.Workshop_LIS.pptx# b/limbach/.~lock.Workshop_LIS.pptx# new file mode 100644 index 0000000..93432cc --- /dev/null +++ b/limbach/.~lock.Workshop_LIS.pptx# @@ -0,0 +1 @@ +,marcuh,mach12,12.06.2026 09:55,file:///home/marcuh/.config/libreoffice/4; \ No newline at end of file diff --git a/limbach/Folie2.pptx b/limbach/Folie2.pptx new file mode 100644 index 0000000..3ffe790 Binary files /dev/null and b/limbach/Folie2.pptx differ diff --git a/limbach/Folie3.pptx b/limbach/Folie3.pptx new file mode 100644 index 0000000..cd383d8 Binary files /dev/null and b/limbach/Folie3.pptx differ diff --git a/limbach/Folie4.pptx b/limbach/Folie4.pptx new file mode 100644 index 0000000..9d3016b Binary files /dev/null and b/limbach/Folie4.pptx differ diff --git a/limbach/Vault_Impuls_Workshop.pptx b/limbach/Vault_Impuls_Workshop.pptx new file mode 100644 index 0000000..c4904a2 Binary files /dev/null and b/limbach/Vault_Impuls_Workshop.pptx differ diff --git a/limbach/Workshop_LIS.pptx b/limbach/Workshop_LIS.pptx new file mode 100644 index 0000000..4a23b99 Binary files /dev/null and b/limbach/Workshop_LIS.pptx differ diff --git a/limbach/build_deck.py b/limbach/build_deck.py new file mode 100644 index 0000000..ba10c17 --- /dev/null +++ b/limbach/build_deck.py @@ -0,0 +1,245 @@ +#!/usr/bin/env python3 +"""Workshop-Folien 'Vault – Technischer Impuls': ruhig, visuell, wenig Text.""" + +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 + +# ---- Palette --------------------------------------------------------------- +INK = RGBColor(0x2A, 0x2F, 0x3A) +NAVY = RGBColor(0x1F, 0x3A, 0x5F) +ACCENT = RGBColor(0xC0, 0x49, 0x2F) +MUTE = RGBColor(0x8A, 0x93, 0xA0) +SOFT = RGBColor(0xE9, 0xEC, 0xEF) # heller Kasten +SOFT2 = RGBColor(0xDD, 0xE5, 0xEC) # heller Kasten blau +LINEC = RGBColor(0xC3, 0xC9, 0xD1) +BG = RGBColor(0xFF, 0xFF, 0xFF) +WHITE = RGBColor(0xFF, 0xFF, 0xFF) +F = "Calibri" + +prs = Presentation() +prs.slide_width = Inches(13.333) +prs.slide_height = Inches(7.5) +SW, SH = prs.slide_width, prs.slide_height +BLANK = prs.slide_layouts[6] +MX = Inches(0.95) + + +def slide(bg=BG): + s = prs.slides.add_slide(BLANK) + r = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, SW, SH) + r.fill.solid(); r.fill.fore_color.rgb = bg; r.line.fill.background() + r.shadow.inherit = False + s.shapes._spTree.remove(r._element); s.shapes._spTree.insert(2, r._element) + return s + + +def shape(s, kind, x, y, w, h, fill=None, line=None, lw=Pt(1)): + 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 + sh.shadow.inherit = False + return sh + + +def box(s, x, y, w, h, text="", fill=SOFT, tc=INK, size=15, bold=False, + rounded=True, line=None, sub=None, sub_c=None): + kind = MSO_SHAPE.ROUNDED_RECTANGLE if rounded else MSO_SHAPE.RECTANGLE + sh = shape(s, kind, x, y, w, h, fill=fill, line=line) + tf = sh.text_frame; tf.word_wrap = True + tf.vertical_anchor = MSO_ANCHOR.MIDDLE + tf.margin_top = tf.margin_bottom = Pt(2) + p = tf.paragraphs[0]; p.alignment = PP_ALIGN.CENTER + r = p.add_run(); r.text = text + r.font.size = Pt(size); r.font.bold = bold; r.font.name = F; r.font.color.rgb = tc + if sub: + p2 = tf.add_paragraph(); p2.alignment = PP_ALIGN.CENTER; p2.space_before = Pt(2) + r2 = p2.add_run(); r2.text = sub + r2.font.size = Pt(11); r2.font.name = F; r2.font.color.rgb = sub_c or MUTE + return sh + + +def text(s, x, y, w, h, runs, align=PP_ALIGN.LEFT, anchor=MSO_ANCHOR.TOP, + leading=1.1, space=0): + 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) + p = tf.paragraphs[0]; p.alignment = align; p.line_spacing = leading; p.space_after = Pt(space) + for t, kw in runs: + r = p.add_run(); r.text = t + r.font.size = Pt(kw.get("size", 16)); r.font.bold = kw.get("bold", False) + r.font.italic = kw.get("italic", False); r.font.name = F + r.font.color.rgb = kw.get("color", INK) + return tb + + +def kicker(s, t): + tb = text(s, MX, Inches(0.62), Inches(11), Inches(0.4), + [(t.upper(), {"size": 12.5, "color": ACCENT, "bold": True})]) + tb.text_frame.paragraphs[0].runs[0].font._rPr.set('spc', '240') + + +def head(s, t): + text(s, MX, Inches(1.0), Inches(11.4), Inches(0.9), + [(t, {"size": 32, "color": NAVY, "bold": True})]) + shape(s, MSO_SHAPE.RECTANGLE, MX, Inches(1.78), Inches(0.6), Pt(4), fill=ACCENT) + + +def caption(s, t): + text(s, MX, Inches(6.62), Inches(11.4), Inches(0.6), + [(t, {"size": 16, "color": MUTE})], leading=1.15) + + +def vline(s, x, y, h, color=LINEC): + shape(s, MSO_SHAPE.RECTANGLE, x, y, Pt(1.6), h, fill=color) + + +# =========================================================================== +# TITEL +# =========================================================================== +s = slide(NAVY) +shape(s, MSO_SHAPE.RECTANGLE, 0, 0, Inches(0.24), SH, fill=ACCENT) +shape(s, MSO_SHAPE.RECTANGLE, MX, Inches(2.7), Inches(0.85), Pt(5), fill=ACCENT) +text(s, MX, Inches(1.7), Inches(10), Inches(0.5), + [("TECHNISCHER IMPULS", {"size": 14, "color": RGBColor(0xC6,0xD1,0xDE), "bold": True})] + ).text_frame.paragraphs[0].runs[0].font._rPr.set('spc', '280') +text(s, MX, Inches(3.05), Inches(11.4), Inches(2.0), + [("LIS – wohin", {"size": 50, "color": WHITE, "bold": True})]) +text(s, MX, Inches(4.05), Inches(11.4), Inches(2.0), + [("technisch denken?", {"size": 50, "color": WHITE, "bold": True})]) + +# =========================================================================== +# FOLIE 1 – Produkt oder Projekt (Balken-Bild) +# =========================================================================== +s = slide() +kicker(s, "Diagnose") +head(s, "Produkt oder Projekt?") + +base = Inches(5.45) # Grundlinie +bw, gap = Inches(0.52), Inches(0.16) + + +def bars(cx_start, heights, colors, label): + n = len(heights) + for i, (hh, col) in enumerate(zip(heights, colors)): + x = cx_start + i * (bw + gap) + h = Inches(hh) + shape(s, MSO_SHAPE.ROUNDED_RECTANGLE, x, base - h, bw, h, fill=col) + total = n * bw + (n - 1) * gap + # Grundlinie + shape(s, MSO_SHAPE.RECTANGLE, cx_start, base, total, Pt(2), fill=LINEC) + text(s, cx_start, base + Inches(0.18), total, Inches(0.4), + [(label, {"size": 18, "color": NAVY, "bold": True})], align=PP_ALIGN.CENTER) + + +bars(Inches(1.5), [0.45, 0.5, 2.7, 0.5, 0.45], + [MUTE, MUTE, ACCENT, MUTE, MUTE], "Projekt") +bars(Inches(8.0), [1.7, 2.0, 1.8, 2.1, 1.7], + [NAVY, NAVY, NAVY, NAVY, NAVY], "Produkt") + +# kleine neutrale Unterschriften +text(s, Inches(1.5), base + Inches(0.6), Inches(3.42), Inches(0.4), + [("tief in einer Dimension", {"size": 13, "color": MUTE})], align=PP_ALIGN.CENTER) +text(s, Inches(8.0), base + Inches(0.6), Inches(3.42), Inches(0.4), + [("breit über viele", {"size": 13, "color": MUTE})], align=PP_ALIGN.CENTER) + +# =========================================================================== +# FOLIE 2 – Eine Schicht dazwischen (Layer-Diagramm) +# =========================================================================== +s = slide() +kicker(s, "Architektur") +head(s, "Eine Schicht dazwischen") + +cxL, cxR = MX, Inches(12.38) +full_w = cxR - cxL + +# oben: Gruppe / Prozesse +top_y = Inches(2.2) +chips = ["Reporting", "DFÜ / Anlieferung", "Fusion"] +cw = Inches(3.2); cg = (full_w - 3 * cw) / 2 +for i, c in enumerate(chips): + box(s, cxL + i * (cw + cg), top_y, cw, Inches(0.72), c, + fill=BG, tc=NAVY, size=15, bold=True, line=LINEC) + +# Mitte: die Schicht (Hero) +mid_y = Inches(3.55) +box(s, cxL, mid_y, full_w, Inches(0.95), + "Integrations- & Abstraktionsschicht", + fill=NAVY, tc=WHITE, size=20, bold=True) + +# unten: Systeme +bot_y = Inches(5.15) +labs = [("Labor A", "LIS"), ("Labor B", "LIS"), ("Vault", "LIS"), ("Labor C", "eigenes LIS")] +lw = Inches(2.65); lg = (full_w - 4 * lw) / 3 +for i, (nm, sub) in enumerate(labs): + x = cxL + i * (lw + lg) + box(s, x, bot_y, lw, Inches(0.95), nm, fill=SOFT, tc=INK, size=16, bold=True, + sub=sub, sub_c=MUTE) + # Verbinder Schicht -> System + vline(s, x + lw / 2, mid_y + Inches(0.95), bot_y - (mid_y + Inches(0.95))) + +# Verbinder oben -> Schicht +for i in range(3): + cx = cxL + i * (cw + cg) + cw / 2 + vline(s, cx, top_y + Inches(0.72), mid_y - (top_y + Inches(0.72))) + +caption(s, "Labore docken an und behalten ihr LIS. Die Schicht verbindet.") + +# =========================================================================== +# FOLIE 3 – Zustand oder Ereignis (Event Sourcing, visuell) +# =========================================================================== +s = slide() +kicker(s, "Modell") +head(s, "Zustand oder Ereignis") + +cxL, cxR = MX, Inches(12.38) +full_w = cxR - cxL + +# --- obere Spur: Zustand --- +ay = Inches(2.35) +text(s, cxL, ay - Inches(0.05), Inches(3), Inches(0.4), + [("Zustand", {"size": 15, "color": MUTE, "bold": True})]) +zy = ay + Inches(0.42) +box(s, cxL, zy, Inches(2.4), Inches(0.95), "Probe", fill=SOFT, tc=INK, size=16, bold=True) +# Pfeil "überschreiben" +shape(s, MSO_SHAPE.RIGHT_ARROW, cxL + Inches(2.65), zy + Inches(0.27), + Inches(1.5), Inches(0.42), fill=LINEC) +text(s, cxL + Inches(2.55), zy - Inches(0.18), Inches(1.7), Inches(0.3), + [("überschreiben", {"size": 11, "color": MUTE, "italic": True})], align=PP_ALIGN.CENTER) +box(s, cxL + Inches(4.4), zy, Inches(2.7), Inches(0.95), "1 Abrechnung", + fill=BG, tc=NAVY, size=16, bold=True, line=LINEC) + +# Trennlinie +shape(s, MSO_SHAPE.RECTANGLE, cxL, Inches(4.25), full_w, Pt(1.2), fill=LINEC) + +# --- untere Spur: Ereignisse --- +by = Inches(4.55) +text(s, cxL, by - Inches(0.05), Inches(3), Inches(0.4), + [("Ereignisse", {"size": 15, "color": MUTE, "bold": True})]) +ey = by + Inches(0.42) +ev = ["Eingang", "Messung", "Abrechnung", "Abrechnung"] +ew = Inches(1.85); eg = Inches(0.32) +for i, e in enumerate(ev): + x = cxL + i * (ew + eg) + col = ACCENT if e == "Abrechnung" else NAVY + box(s, x, ey, ew, Inches(0.85), e, fill=col, tc=WHITE, size=14, bold=True) + if i < len(ev) - 1: + shape(s, MSO_SHAPE.CHEVRON, x + ew + Inches(0.02), ey + Inches(0.24), + Inches(0.28), Inches(0.38), fill=LINEC) +# Pfeil zum abgeleiteten Zustand +last_x = cxL + 4 * (ew + eg) +shape(s, MSO_SHAPE.RIGHT_ARROW, last_x - Inches(0.05), ey + Inches(0.22), + Inches(0.7), Inches(0.42), fill=LINEC) +box(s, last_x + Inches(0.75), ey - Inches(0.05), Inches(2.7), Inches(0.95), + "Zustand", fill=SOFT2, tc=NAVY, size=15, bold=True, + sub="abgeleitet", sub_c=MUTE) + +caption(s, "Der Zustand entsteht aus den Ereignissen – Mehrfaches inklusive.") + +out = "/home/marcuh/claude-projects/limbach/Vault_Impuls_Workshop.pptx" +prs.save(out) +print("saved", out) diff --git a/limbach/folie2.py b/limbach/folie2.py new file mode 100644 index 0000000..9b5b1ca --- /dev/null +++ b/limbach/folie2.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 +"""Folie 2 – Grenzen der Vault-Integration. Kette: Auftrag -> altes LIS -> +Schattenbuchhaltung/Metadaten -> Vault (am Ende). Der Layer wird zum SPOT.""" + +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 + +INK = RGBColor(0x2A, 0x2F, 0x3A) +NAVY = RGBColor(0x1F, 0x3A, 0x5F) +ACCENT = RGBColor(0xC0, 0x49, 0x2F) +MUTE = RGBColor(0x8A, 0x93, 0xA0) +LINEC = RGBColor(0xC3, 0xC9, 0xD1) +FRAME = RGBColor(0xF4, 0xF6, 0xF8) +TINT_R = RGBColor(0xF6, 0xE5, 0xDF) +BG = RGBColor(0xFF, 0xFF, 0xFF) +WHITE = RGBColor(0xFF, 0xFF, 0xFF) +F = "Calibri" + +prs = Presentation() +prs.slide_width = Inches(13.333) +prs.slide_height = Inches(7.5) +s = prs.slides.add_slide(prs.slide_layouts[6]) +bgr = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, prs.slide_width, prs.slide_height) +bgr.fill.solid(); bgr.fill.fore_color.rgb = BG; bgr.line.fill.background() +bgr.shadow.inherit = False + + +def shp(kind, x, y, w, h, fill=None, line=None, lw=Pt(1), rot=0): + 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 + sh.shadow.inherit = False + return sh + + +def boxtext(sh, t, size=14, color=INK, bold=False): + tf = sh.text_frame; tf.word_wrap = True + tf.vertical_anchor = MSO_ANCHOR.MIDDLE + tf.margin_left = tf.margin_right = Pt(4); tf.margin_top = tf.margin_bottom = Pt(1) + p = tf.paragraphs[0]; p.alignment = PP_ALIGN.CENTER + r = p.add_run(); r.text = t + r.font.size = Pt(size); r.font.bold = bold; r.font.name = F; r.font.color.rgb = color + + +def txt(x, y, w, h, t, size=14, color=INK, bold=False, italic=False, + align=PP_ALIGN.LEFT, spc=None, lead=1.1, anchor=MSO_ANCHOR.TOP): + 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) + p = tf.paragraphs[0]; p.alignment = align; p.line_spacing = lead + r = p.add_run(); r.text = t + r.font.size = Pt(size); r.font.bold = bold; r.font.italic = italic + r.font.name = F; r.font.color.rgb = color + if spc: r.font._rPr.set('spc', str(spc)) + return tb + + +# ---- Kopf ------------------------------------------------------------------ +txt(Inches(0.95), Inches(0.32), Inches(8), Inches(0.35), "INTEGRATION", + size=12.5, color=ACCENT, bold=True, spc=240) +txt(Inches(0.95), Inches(0.62), Inches(11.5), Inches(0.85), "Grenzen der Vault-Integration?", + size=31, color=NAVY, bold=True) +shp(MSO_SHAPE.RECTANGLE, Inches(0.95), Inches(1.34), Inches(0.6), Pt(4), fill=ACCENT) + +# ---- Linke Spalte ---------------------------------------------------------- +def claim(y, t, bold=False, sub=None): + shp(MSO_SHAPE.RECTANGLE, Inches(0.95), y + Inches(0.08), Inches(0.14), Inches(0.14), fill=ACCENT) + txt(Inches(1.28), y, Inches(4.5), Inches(0.4), t, size=16.5, color=INK, bold=bold) + if sub: + txt(Inches(1.28), y + Inches(0.34), Inches(4.5), Inches(0.32), sub, + size=12.5, color=MUTE, italic=True) + +claim(Inches(2.2), "Middleware kapselt den Rand – den Kern nicht", bold=True) +claim(Inches(2.95), "1:n geht nur als Schattenmodell", + sub="aufwändig · fragil · fehleranfällig") +claim(Inches(3.95), "die Fachwahrheit verlässt den Kern") + +shp(MSO_SHAPE.RECTANGLE, Inches(0.95), Inches(4.95), Inches(0.5), Pt(3.5), fill=ACCENT) +txt(Inches(0.95), Inches(5.12), Inches(4.85), Inches(1.1), + "Die Schattenbuchhaltung wird zum Single Point of Truth – nicht Vault.", + size=19, color=NAVY, bold=True, lead=1.08) +txt(Inches(0.95), Inches(6.45), Inches(4.85), Inches(0.4), + "Genau die Rolle, die Vault halten müsste.", size=13.5, color=MUTE, italic=True) + +# =========================================================================== +# RECHTS: Kette Auftrag -> altes LIS -> Layer (SPOT) -> Vault (Ende) +# =========================================================================== +fx, fy, fw, fh = Inches(6.25), Inches(2.4), Inches(5.9), Inches(4.55) +shp(MSO_SHAPE.ROUNDED_RECTANGLE, fx, fy, fw, fh, fill=FRAME, line=LINEC, lw=Pt(1.5)) +txt(fx + Inches(0.3), fy + Inches(0.14), fw - Inches(0.6), Inches(0.3), + "Auftragsweg – hin und zurück durch den Layer", size=12.5, color=NAVY, bold=True) + +cxc = fx + Inches(2.05) +bw = Inches(2.7) +bx = cxc - bw / 2 + + +def down(y, label=None): + shp(MSO_SHAPE.DOWN_ARROW, cxc - Inches(0.11), y, Inches(0.22), Inches(0.26), fill=MUTE) + if label: + txt(cxc + Inches(0.22), y - Inches(0.02), Inches(1.6), Inches(0.3), label, + size=11, color=MUTE, italic=True, anchor=MSO_ANCHOR.MIDDLE) + + +# 1) Auftrag +b = shp(MSO_SHAPE.ROUNDED_RECTANGLE, bx, fy + Inches(0.52), bw, Inches(0.48), fill=BG, line=LINEC) +boxtext(b, "Auftrag · Mischauftrag (1:n)", size=12.5, color=INK, bold=True) +down(fy + Inches(1.02)) + +# 2) Altes LIS +b = shp(MSO_SHAPE.ROUNDED_RECTANGLE, bx, fy + Inches(1.3), bw, Inches(0.5), fill=NAVY) +boxtext(b, "Altes LIS · nimmt 1:n an", size=13, color=WHITE, bold=True) +down(fy + Inches(1.82), "Magic") + +# 3) Schattenbuchhaltung = SPOT +b = shp(MSO_SHAPE.ROUNDED_RECTANGLE, bx - Inches(0.15), fy + Inches(2.1), bw + Inches(0.3), + Inches(0.78), fill=TINT_R, line=ACCENT, lw=Pt(2)) +txt(bx - Inches(0.15), fy + Inches(2.18), bw + Inches(0.3), Inches(0.32), + "Schattenbuchhaltung / Metadaten", size=13, color=ACCENT, bold=True, align=PP_ALIGN.CENTER) +txt(bx - Inches(0.15), fy + Inches(2.52), bw + Inches(0.3), Inches(0.3), "Mapping 1 ↔ 3", + size=12, color=INK, align=PP_ALIGN.CENTER) +# aktiver SPOT-Stern +shp(MSO_SHAPE.STAR_5_POINT, fx + fw - Inches(0.78), fy + Inches(2.18), Inches(0.5), Inches(0.5), + fill=ACCENT) +txt(fx + fw - Inches(2.55), fy + Inches(2.7), Inches(1.75), Inches(0.28), "Single Point of Truth", + size=10.5, color=ACCENT, bold=True, align=PP_ALIGN.RIGHT) +down(fy + Inches(2.9), "1 → 3, Vault-kompatibel") + +# 4) Vault am Ende +b = shp(MSO_SHAPE.ROUNDED_RECTANGLE, bx, fy + Inches(3.18), bw, Inches(0.82), fill=NAVY) +txt(bx, fy + Inches(3.24), bw, Inches(0.3), "Vault · 1:1 · Speicher", + size=13, color=WHITE, bold=True, align=PP_ALIGN.CENTER) +for k in range(3): + shp(MSO_SHAPE.ROUNDED_RECTANGLE, cxc - Inches(1.08) + k * Inches(0.78), + fy + Inches(3.58), Inches(0.62), Inches(0.3), fill=RGBColor(0x34, 0x4E, 0x70)) + txt(cxc - Inches(1.08) + k * Inches(0.78), fy + Inches(3.58), Inches(0.62), Inches(0.3), + "Rec", size=9.5, color=WHITE, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE) +# Geist-Stern (durchgestrichen) bei Vault +gx = fx + fw - Inches(0.74) +shp(MSO_SHAPE.STAR_5_POINT, gx, fy + Inches(3.32), Inches(0.44), Inches(0.44), + fill=BG, line=LINEC, lw=Pt(1.25)) +shp(MSO_SHAPE.RECTANGLE, gx - Inches(0.02), fy + Inches(3.52), Inches(0.48), Pt(2), + fill=ACCENT, rot=20) +txt(fx + fw - Inches(2.45), fy + Inches(3.3), Inches(1.65), Inches(0.4), + "müsste hier liegen", size=10, color=MUTE, align=PP_ALIGN.RIGHT) + +# Rückweg (Lesen) – Pfeil links nach oben +shp(MSO_SHAPE.UP_ARROW, fx + Inches(0.34), fy + Inches(1.45), Inches(0.26), Inches(2.45), + fill=RGBColor(0xD8, 0xCB, 0xC6)) +txt(fx + Inches(0.1), fy + Inches(3.95), Inches(1.55), Inches(0.5), + "Lesen: zurück, wieder LIS-kompatibel", size=9.5, color=ACCENT, align=PP_ALIGN.LEFT, lead=1.0) + +out = "/home/marcuh/claude-projects/limbach/Folie2.pptx" +prs.save(out) +print("saved", out) diff --git a/limbach/folie3.py b/limbach/folie3.py new file mode 100644 index 0000000..7dc3df9 --- /dev/null +++ b/limbach/folie3.py @@ -0,0 +1,268 @@ +#!/usr/bin/env python3 +"""Folie 3 – LIS mit Umsystemen (Adapter, links/rechts) und andockenden Modulen +(unten), unter einer Steuerungs-/Governance-Schicht; weitere LIS angedeutet.""" + +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 + +INK = RGBColor(0x2A, 0x2F, 0x3A) +NAVY = RGBColor(0x1F, 0x3A, 0x5F) +ACCENT = RGBColor(0xC0, 0x49, 0x2F) +TEAL = RGBColor(0x2F, 0x8F, 0x83) +AMBER = RGBColor(0xA9, 0x73, 0x22) +MUTE = RGBColor(0x8A, 0x93, 0xA0) +LINEC = RGBColor(0xC3, 0xC9, 0xD1) +HINT = RGBColor(0xEE, 0xF1, 0xF4) +HINTL = RGBColor(0xCB, 0xD2, 0xDA) +TINT_T = RGBColor(0xDD, 0xEC, 0xEA) +TINT_A = RGBColor(0xF3, 0xE8, 0xD2) +UMS = RGBColor(0xEC, 0xEE, 0xF1) +BG = RGBColor(0xFF, 0xFF, 0xFF) +WHITE = RGBColor(0xFF, 0xFF, 0xFF) +F = "Calibri" + +prs = Presentation() +prs.slide_width = Inches(13.333) +prs.slide_height = Inches(7.5) +s = prs.slides.add_slide(prs.slide_layouts[6]) +bgr = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, prs.slide_width, prs.slide_height) +bgr.fill.solid(); bgr.fill.fore_color.rgb = BG; bgr.line.fill.background() +bgr.shadow.inherit = False + + +def shp(kind, x, y, w, h, fill=None, line=None, lw=Pt(1), rot=0): + 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 + sh.shadow.inherit = False + return sh + + +def boxtext(sh, t, size=14, color=INK, bold=False): + tf = sh.text_frame; tf.word_wrap = True + tf.vertical_anchor = MSO_ANCHOR.MIDDLE + tf.margin_left = tf.margin_right = Pt(3); tf.margin_top = tf.margin_bottom = Pt(1) + p = tf.paragraphs[0]; p.alignment = PP_ALIGN.CENTER + r = p.add_run(); r.text = t + r.font.size = Pt(size); r.font.bold = bold; r.font.name = F; r.font.color.rgb = color + + +def txt(x, y, w, h, t, size=14, color=INK, bold=False, italic=False, + align=PP_ALIGN.LEFT, spc=None): + tb = s.shapes.add_textbox(x, y, w, 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 = align + r = p.add_run(); r.text = t + r.font.size = Pt(size); r.font.bold = bold; r.font.italic = italic + r.font.name = F; r.font.color.rgb = color + if spc: r.font._rPr.set('spc', str(spc)) + return tb + + +def hline(x, y, w, color=LINEC, weight=Pt(1.4)): + shp(MSO_SHAPE.RECTANGLE, x, y - weight / 2, w, weight, fill=color) + + +def vline(x, y, h, color=LINEC, weight=Pt(1.4)): + shp(MSO_SHAPE.RECTANGLE, x - weight / 2, y, weight, h, fill=color) + + +# ---- Kopf ------------------------------------------------------------------ +txt(Inches(0.95), Inches(0.32), Inches(8), Inches(0.35), "ARCHITEKTUR", + size=12.5, color=ACCENT, bold=True, spc=240) +txt(Inches(0.95), Inches(0.62), Inches(11), Inches(0.85), "Offen – und gesteuert", + size=31, color=NAVY, bold=True) +shp(MSO_SHAPE.RECTANGLE, Inches(0.95), Inches(1.34), Inches(0.6), Pt(4), fill=ACCENT) + +x0, x1 = Inches(0.95), Inches(12.38) + + +# =========================================================================== +# GOVERNANCE – Icon-Karten + Strich +# =========================================================================== +def card(cx, label): + cw_, ch_ = Inches(2.05), Inches(1.25) + x, y = cx - cw_ / 2, Inches(1.5) + shp(MSO_SHAPE.ROUNDED_RECTANGLE, x, y, cw_, ch_, fill=BG, line=LINEC, lw=Pt(1.25)) + txt(x, y + ch_ - Inches(0.4), cw_, Inches(0.34), label, + size=14, color=NAVY, bold=True, align=PP_ALIGN.CENTER) + return cx, y + Inches(0.42) + + +def regler(cx, cy): + tw = Inches(0.62) + left = cx - tw / 2 + for j, frac in enumerate([0.30, 0.68, 0.48]): + ty = cy - Inches(0.18) + j * Inches(0.18) + shp(MSO_SHAPE.ROUNDED_RECTANGLE, left, ty - Inches(0.022), tw, Inches(0.045), fill=LINEC) + kx = left + Inches(0.62 * frac) + shp(MSO_SHAPE.OVAL, kx - Inches(0.07), ty - Inches(0.07), + Inches(0.14), Inches(0.14), fill=NAVY) + + +cx1, cy1 = card(Inches(4.75), "Steuerung") +cx2, cy2 = card(Inches(6.95), "Reporting") +cx3, cy3 = card(Inches(9.15), "Governance") + +regler(cx1, cy1) + +# Reporting – Zahlenreihen +nb = s.shapes.add_textbox(cx2 - Inches(0.85), cy2 - Inches(0.36), Inches(1.7), Inches(0.72)) +tf = nb.text_frame; tf.word_wrap = False; tf.vertical_anchor = MSO_ANCHOR.MIDDLE +tf.margin_left = tf.margin_right = tf.margin_top = tf.margin_bottom = Pt(0) +for i, row in enumerate(["3 8 1 4 7", "6 0 9 2 5", "5 7 2 8 1"]): + p = tf.paragraphs[0] if i == 0 else tf.add_paragraph() + p.alignment = PP_ALIGN.CENTER; p.line_spacing = 0.95 + r = p.add_run(); r.text = row + r.font.size = Pt(11); r.font.name = "Consolas"; r.font.color.rgb = MUTE + +# Governance – § +gtb = s.shapes.add_textbox(cx3 - Inches(0.6), cy3 - Inches(0.42), Inches(1.2), Inches(0.8)) +gtf = gtb.text_frame; gtf.vertical_anchor = MSO_ANCHOR.MIDDLE +gp = gtf.paragraphs[0]; gp.alignment = PP_ALIGN.CENTER +gr = gp.add_run(); gr.text = "§"; gr.font.size = Pt(34); gr.font.bold = True +gr.font.name = F; gr.font.color.rgb = NAVY + +gov_y = Inches(3.05) +shp(MSO_SHAPE.ROUNDED_RECTANGLE, x0, gov_y - Inches(0.07), x1 - x0, Inches(0.14), fill=NAVY) +for cc in (cx1, cx2, cx3): + vline(cc, Inches(2.75), gov_y - Inches(0.07) - Inches(2.75), color=LINEC, weight=Pt(1)) + + +# =========================================================================== +# LIS + Umsysteme (Adapter links/rechts) + Module (andocken, unten) +# =========================================================================== +core_x, core_w = Inches(3.7), Inches(2.3) +core_y, core_h = Inches(4.3), Inches(1.2) +core_cx = core_x + core_w / 2 +core_by = core_y + core_h + + +def adapter(xc, yc): + shp(MSO_SHAPE.ROUNDED_RECTANGLE, xc - Inches(0.10), yc - Inches(0.11), + Inches(0.16), Inches(0.22), fill=ACCENT) + shp(MSO_SHAPE.RECTANGLE, xc + Inches(0.06), yc - Inches(0.055), Inches(0.1), Pt(2.4), fill=ACCENT) + shp(MSO_SHAPE.RECTANGLE, xc + Inches(0.06), yc + Inches(0.03), Inches(0.1), Pt(2.4), fill=ACCENT) + + +def dock(xc, yc): + shp(MSO_SHAPE.ROUNDED_RECTANGLE, xc - Inches(0.1), yc - Inches(0.1), + Inches(0.2), Inches(0.2), fill=NAVY) + shp(MSO_SHAPE.OVAL, xc - Inches(0.055), yc - Inches(0.055), Inches(0.11), Inches(0.11), + fill=ACCENT) + + +def icon_truck(ix, iy): # Anlieferung + shp(MSO_SHAPE.ROUNDED_RECTANGLE, ix - Inches(0.20), iy - Inches(0.10), + Inches(0.24), Inches(0.16), fill=NAVY) + shp(MSO_SHAPE.ROUNDED_RECTANGLE, ix + Inches(0.05), iy - Inches(0.035), + Inches(0.12), Inches(0.095), fill=NAVY) + for wx in (ix - Inches(0.11), ix + Inches(0.08)): + shp(MSO_SHAPE.OVAL, wx - Inches(0.038), iy + Inches(0.05), + Inches(0.076), Inches(0.076), fill=NAVY) + + +def icon_device(ix, iy): # Gerät + shp(MSO_SHAPE.ROUNDED_RECTANGLE, ix - Inches(0.16), iy - Inches(0.14), + Inches(0.32), Inches(0.28), fill=NAVY) + shp(MSO_SHAPE.RECTANGLE, ix - Inches(0.11), iy - Inches(0.10), + Inches(0.22), Inches(0.085), fill=RGBColor(0x34, 0x4E, 0x70)) + shp(MSO_SHAPE.OVAL, ix - Inches(0.085), iy + Inches(0.03), Inches(0.055), Inches(0.055), fill=TEAL) + shp(MSO_SHAPE.OVAL, ix + Inches(0.03), iy + Inches(0.03), Inches(0.055), Inches(0.055), fill=AMBER) + + +def icon_dfu(ix, iy): # DFÜ + shp(MSO_SHAPE.RIGHT_ARROW, ix - Inches(0.16), iy - Inches(0.12), Inches(0.32), Inches(0.095), + fill=NAVY) + shp(MSO_SHAPE.LEFT_ARROW, ix - Inches(0.16), iy + Inches(0.025), Inches(0.32), Inches(0.095), + fill=ACCENT) + + +# Governance -> LIS +vline(core_cx, gov_y + Inches(0.07), core_y - (gov_y + Inches(0.07)), color=LINEC, weight=Pt(1)) + +uw, uh = Inches(1.55), Inches(0.6) + + +def ums_box(bx, yc, name, icon_fn): + shp(MSO_SHAPE.ROUNDED_RECTANGLE, bx, yc - uh / 2, uw, uh, fill=UMS, line=LINEC) + icon_fn(bx + Inches(0.32), yc) + txt(bx + Inches(0.58), yc - Inches(0.16), uw - Inches(0.64), Inches(0.32), name, + size=12, color=INK, align=PP_ALIGN.LEFT) + + +# Umsysteme links +lx = Inches(0.8) +ums_box(lx, Inches(4.6), "Anlieferung", icon_truck) +ums_box(lx, Inches(5.2), "DFÜ", icon_dfu) +for yc in (Inches(4.6), Inches(5.2)): + hline(lx + uw, yc, core_x - (lx + uw)) + adapter(core_x - Inches(0.16), yc) + +# Umsystem rechts +gx = Inches(6.65) +ums_box(gx, Inches(4.9), "Geräte", icon_device) +hline(core_x + core_w, Inches(4.9), gx - (core_x + core_w)) +adapter(core_x + core_w + Inches(0.16), Inches(4.9)) + +# LIS (kompakt, solide) +shp(MSO_SHAPE.ROUNDED_RECTANGLE, core_x, core_y, core_w, core_h, fill=NAVY) +sh = shp(MSO_SHAPE.ROUNDED_RECTANGLE, core_x, core_y, core_w, core_h, fill=NAVY) +boxtext(sh, "LIS", size=22, color=WHITE, bold=True) + +# Module unten (andockend) +mw, mh = Inches(1.7), Inches(0.6) +my = Inches(6.05) +for nm, fill, tc, mcx in [("Hochdurchsatz", TINT_T, TEAL, Inches(3.85)), + ("Spezial", TINT_A, AMBER, Inches(5.85))]: + vline(mcx, core_by, my - core_by, color=LINEC, weight=Pt(1)) + dock(mcx, core_by + (my - core_by) / 2) + sh = shp(MSO_SHAPE.ROUNDED_RECTANGLE, mcx - mw / 2, my, mw, mh, fill=fill, line=tc, lw=Pt(1.25)) + boxtext(sh, nm, size=12.5, color=tc, bold=True) + +txt(Inches(2.9), my + mh + Inches(0.12), Inches(4.0), Inches(0.3), "eigene Module", + size=12, color=MUTE, bold=True, align=PP_ALIGN.CENTER) + + +# =========================================================================== +# WEITERE LIS – überlappend, nur angedeutet +# =========================================================================== +hw_, hh_ = Inches(1.8), Inches(2.0) + +def mini_lis(hx, hy, faded=False): + fill = RGBColor(0xF4, 0xF6, 0xF8) if faded else HINT + shp(MSO_SHAPE.ROUNDED_RECTANGLE, hx, hy, hw_, hh_, fill=fill, line=HINTL, lw=Pt(1.25)) + hdc = shp(MSO_SHAPE.ROUNDED_RECTANGLE, hx, hy, hw_, Inches(0.46), + fill=HINTL if not faded else RGBColor(0xDF, 0xE4, 0xEA)) + boxtext(hdc, "LIS", size=14, color=NAVY, bold=True) + for k in range(3): + yy = hy + Inches(0.66) + k * Inches(0.4) + shp(MSO_SHAPE.ROUNDED_RECTANGLE, hx + Inches(0.2), yy, hw_ - Inches(0.4), + Inches(0.28), fill=WHITE, line=HINTL, lw=Pt(0.75)) + +mini_lis(Inches(10.5), Inches(4.35), faded=True) +mini_lis(Inches(10.0), Inches(4.75), faded=False) +vline(Inches(10.0) + hw_ / 2, gov_y + Inches(0.07), Inches(4.75) - (gov_y + Inches(0.07)), + color=LINEC, weight=Pt(1)) +txt(Inches(9.9), Inches(4.35) + hh_ + Inches(0.2), Inches(2.4), Inches(0.35), "weitere LIS", + size=13, color=MUTE, align=PP_ALIGN.CENTER) + +# ---- Mini-Legende ---------------------------------------------------------- +ly = Inches(7.05) +adapter(Inches(7.35), ly) +txt(Inches(7.55), ly - Inches(0.16), Inches(2.1), Inches(0.32), "Adapter (Umsysteme)", + size=11.5, color=MUTE) +dock(Inches(9.65), ly) +txt(Inches(9.85), ly - Inches(0.16), Inches(2.1), Inches(0.32), "Andocken (Module)", + size=11.5, color=MUTE) + +out = "/home/marcuh/claude-projects/limbach/Folie3.pptx" +prs.save(out) +print("saved", out) diff --git a/limbach/folie4.py b/limbach/folie4.py new file mode 100644 index 0000000..35998ea --- /dev/null +++ b/limbach/folie4.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python3 +"""Folie 4 – Eventstream als Quelle; Aussagen links, Lupen rechts, Strecke unten.""" + +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 + +INK = RGBColor(0x2A, 0x2F, 0x3A) +NAVY = RGBColor(0x1F, 0x3A, 0x5F) +ACCENT = RGBColor(0xC0, 0x49, 0x2F) +TEAL = RGBColor(0x2F, 0x8F, 0x83) +AMBER = RGBColor(0xC8, 0x8A, 0x3A) +MUTE = RGBColor(0x8A, 0x93, 0xA0) +GLASS = RGBColor(0xF2, 0xF5, 0xF8) +BELT = RGBColor(0xD7, 0xDC, 0xE2) +DARK = RGBColor(0x33, 0x39, 0x44) +LINEC = RGBColor(0xC3, 0xC9, 0xD1) +THIN = RGBColor(0xDD, 0xE2, 0xE8) +STREAM = RGBColor(0x24, 0x37, 0x52) +BG = RGBColor(0xFF, 0xFF, 0xFF) +WHITE = RGBColor(0xFF, 0xFF, 0xFF) +F = "Calibri" + +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]) +bgr = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, SW, SH) +bgr.fill.solid(); bgr.fill.fore_color.rgb = BG; bgr.line.fill.background() +bgr.shadow.inherit = False + + +def shp(kind, x, y, w, h, fill=None, line=None, lw=Pt(1), rot=0): + 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 + sh.shadow.inherit = False + return sh + + +def txt(x, y, w, h, t, size=16, color=INK, bold=False, italic=False, + align=PP_ALIGN.LEFT, spc=None): + tb = s.shapes.add_textbox(x, y, w, 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 = align + r = p.add_run(); r.text = t + r.font.size = Pt(size); r.font.bold = bold; r.font.italic = italic + r.font.name = F; r.font.color.rgb = color + if spc: r.font._rPr.set('spc', str(spc)) + return tb + + +# ---- Kopf (ganz oben) ------------------------------------------------------ +txt(Inches(0.95), Inches(0.32), Inches(8), Inches(0.35), "MODELL", + size=12.5, color=ACCENT, bold=True, spc=240) +txt(Inches(0.95), Inches(0.62), Inches(11), Inches(0.85), "Zustand oder Ereignis", + size=31, color=NAVY, bold=True) +shp(MSO_SHAPE.RECTANGLE, Inches(0.95), Inches(1.34), Inches(0.6), Pt(4), fill=ACCENT) + +x0, x1 = Inches(1.0), Inches(12.33) +beltw = x1 - x0 + +# ---- Aussagen links -------------------------------------------------------- +claims = [ + "Eventstream = einzige Quelle der Wahrheit", + "kann nicht überschrieben werden", + "vollständige 3D-Sicht auf Maschine / Strang", +] +cy = Inches(1.95) +for i, c in enumerate(claims): + y = cy + Inches(0.6) * i + shp(MSO_SHAPE.RECTANGLE, Inches(0.95), y + Inches(0.1), Inches(0.14), Inches(0.14), fill=ACCENT) + bold = (i == 0) + txt(Inches(1.28), y, Inches(4.6), Inches(0.45), c, + size=16.5, color=INK, bold=bold) + +# =========================================================================== +# LUPEN (Projektionen) – nach rechts gerückt +# =========================================================================== +def lupe(cx, label, label_w): + cy = Inches(2.45) + d = Inches(0.9) + shp(MSO_SHAPE.OVAL, cx - d / 2, cy - d / 2, d, d, fill=WHITE, line=NAVY, lw=Pt(4)) + shp(MSO_SHAPE.OVAL, cx - d / 2 + Inches(0.13), cy - d / 2 + Inches(0.12), + Inches(0.17), Inches(0.13), fill=RGBColor(0xED,0xF1,0xF5)) + hw, hh = Inches(0.48), Inches(0.15) + hcx, hcy = cx + d * 0.52, cy + d * 0.52 + shp(MSO_SHAPE.ROUNDED_RECTANGLE, hcx - hw / 2, hcy - hh / 2, hw, hh, fill=NAVY, rot=45) + txt(cx - label_w / 2, cy + d / 2 + Inches(0.16), label_w, Inches(0.6), + label, size=14, color=NAVY, bold=True, align=PP_ALIGN.CENTER) + return cy + d / 2 + + +lupes = [ + (Inches(7.05), "Gruppen-Reporting", Inches(2.3)), + (Inches(9.45), "Optimierung der Auslastung", Inches(2.3)), + (Inches(11.55), "Abrechnungen", Inches(1.9)), +] +lens_bottoms = [lupe(cx, lb, lw) for cx, lb, lw in lupes] + +# =========================================================================== +# EVENTSTREAM (Pfeil) +# =========================================================================== +ay, ah = Inches(4.45), Inches(0.46) +shp(MSO_SHAPE.RIGHT_ARROW, x0, ay, beltw, ah, fill=STREAM) +acx = ay + ah / 2 +txt(Inches(5.65), ay + Inches(0.04), Inches(2.0), ah - Inches(0.08), + "Eventstream", size=14, color=WHITE, bold=True, align=PP_ALIGN.CENTER) + +# Verbinder Lupe -> Strom +for (cx, _, _), lb_y in zip(lupes, lens_bottoms): + shp(MSO_SHAPE.RECTANGLE, cx - Pt(0.5), lb_y, Pt(1), ay - lb_y, fill=LINEC) + +# =========================================================================== +# RÖHRCHEN-STRECKE durch Maschine – unten +# =========================================================================== +belt_top = Inches(6.1) +belt_h = Inches(0.30) + +for rx in (x0 - Inches(0.18), x1 - Inches(0.32)): + shp(MSO_SHAPE.OVAL, rx, belt_top - Inches(0.10), Inches(0.5), Inches(0.5), + fill=BELT, line=LINEC) +shp(MSO_SHAPE.ROUNDED_RECTANGLE, x0, belt_top, beltw, belt_h, fill=BELT) + +mx, mw = Inches(5.45), Inches(2.45) +my, mh = Inches(5.2), Inches(1.5) +shp(MSO_SHAPE.ROUNDED_RECTANGLE, mx, my, mw, mh, fill=NAVY) +shp(MSO_SHAPE.ROUNDED_RECTANGLE, mx + Inches(0.3), my + Inches(0.22), + mw - Inches(0.6), Inches(0.32), fill=RGBColor(0x34,0x4E,0x70)) +shp(MSO_SHAPE.OVAL, mx + Inches(0.35), my + Inches(0.74), Inches(0.15), Inches(0.15), fill=TEAL) +shp(MSO_SHAPE.OVAL, mx + Inches(0.6), my + Inches(0.74), Inches(0.15), Inches(0.15), fill=AMBER) +shp(MSO_SHAPE.RECTANGLE, mx - Inches(0.02), belt_top - Inches(0.02), + Inches(0.14), belt_h + Inches(0.04), fill=DARK) +shp(MSO_SHAPE.RECTANGLE, mx + mw - Inches(0.12), belt_top - Inches(0.02), + Inches(0.14), belt_h + Inches(0.04), fill=DARK) + + +def tube(cx, liquid): + bw, bh = Inches(0.22), Inches(0.6) + bx, by = cx - bw / 2, belt_top - bh + shp(MSO_SHAPE.ROUNDED_RECTANGLE, bx, by, bw, bh, fill=GLASS, line=LINEC, lw=Pt(0.75)) + lh = Inches(0.25) + shp(MSO_SHAPE.ROUNDED_RECTANGLE, bx + Pt(1), belt_top - lh, bw - Pt(2), lh, fill=liquid) + cw, ch = Inches(0.26), Inches(0.12) + shp(MSO_SHAPE.ROUNDED_RECTANGLE, cx - cw / 2, by - Inches(0.06), cw, ch, fill=DARK) + + +cols = [ACCENT, TEAL, AMBER] +pre_x = [1.55, 2.1, 2.65, 3.2, 3.75, 4.3, 4.85] +post_x = [8.25, 8.8, 9.35, 9.9, 10.45, 11.0] +for i, xv in enumerate(pre_x + post_x): + tube(Inches(xv), cols[i % 3]) + +tube_top = belt_top - Inches(0.6) +for xv in pre_x + post_x: + shp(MSO_SHAPE.RECTANGLE, Inches(xv) - Pt(0.4), ay + ah, + Pt(0.8), tube_top - (ay + ah), fill=THIN) + shp(MSO_SHAPE.OVAL, Inches(xv) - Inches(0.05), acx - Inches(0.05), + Inches(0.1), Inches(0.1), fill=AMBER) + +out = "/home/marcuh/claude-projects/limbach/Folie4.pptx" +prs.save(out) +print("saved", out) diff --git a/limbach/memory/MEMORY.md b/limbach/memory/MEMORY.md new file mode 100644 index 0000000..b2cb63e --- /dev/null +++ b/limbach/memory/MEMORY.md @@ -0,0 +1 @@ +- [Limbach Workshop / Outline](limbach-workshop-outline.md) — Wissensaufbau in Outline für Workshop 15./16.06.2026 diff --git a/limbach/memory/limbach-workshop-outline.md b/limbach/memory/limbach-workshop-outline.md new file mode 100644 index 0000000..e3f2020 --- /dev/null +++ b/limbach/memory/limbach-workshop-outline.md @@ -0,0 +1,21 @@ +--- +name: limbach-workshop-outline +description: Limbach-Projekt — Wissensaufbau in Outline für Workshop 15./16.06.2026 +metadata: + node_type: memory + type: project + originSessionId: 9d8b39c1-0e24-4510-91b6-eba4f524af7a +--- + +Marcus bereitet einen Workshop zur **Limbach Gruppe** (15./16.06.2026) vor. Die Arbeit läuft nicht im Repo, sondern in **Outline** (Collection „Limbach", id `90cccbbb-a882-48c0-bf26-c26a0395d9dc`). + +Struktur (Stand 2026-06-07): +- **01 - Unternehmen**: 01 Limbach Gruppe (Holding/Inhaber), 02 H&S, 03 VAULT (inkl. Kapitel „CE-Zertifizierung / Fragen"). +- **02 - Regulatorik und Markt** (Ordner-Doc id `c43dbcd4-1b99-44e7-8b48-a5083aac557a`): 01 Was es gibt, 02 Trennungsprinzip, 03 Nachweispflichten, 04 Prüfung/Zertifizierung, 05 Drittanbieter, 06 Abgrenzung IVD/CE, 07 USP/Produktchancen, 08 Marktvorgaben. +- „Einschätzung" liegt unsortiert auf oberster Ebene. + +Thematischer Kern: LIS/LIMS im Laborumfeld — H&S (Legacy) vs. VAULT (.NET/Azure, Holding-nah), Regulatorik (RiliBÄK, ISO 15189, IVDR), IVD-Abgrenzung von LIS, und die strategische CE-/Integrations-Frage an VAULT. + +Stilvorgaben des Nutzers (gelten hier stark): sachlich, kurz, **nur tun was ausdrücklich verlangt wird**, keine Vorschläge fürs weitere Vorgehen. Doc-Inhalte mit Disclaimer „keine Rechtsberatung". + +Offener Punkt: Outline-Collection sortiert manuell (`index`) — Nummern-Präfixe greifen nur, wenn die Collection in Outline auf „Alphabetical" gestellt wird (über MCP nicht setzbar). diff --git a/limbach/merge.py b/limbach/merge.py new file mode 100644 index 0000000..dda889a --- /dev/null +++ b/limbach/merge.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +"""Fügt Folie2, Folie3, Folie4 zu einer Datei zusammen (Reihenfolge 2, 3, 4).""" + +import copy +from pptx import Presentation + +base = "/home/marcuh/claude-projects/limbach/" +dst = Presentation(base + "Folie2.pptx") # Seite 1 = Folie 2 +blank = dst.slide_layouts[6] + +for fname in ("Folie3.pptx", "Folie4.pptx"): # Seiten 2 und 3 + src = Presentation(base + fname) + new_slide = dst.slides.add_slide(blank) + for shape in src.slides[0].shapes: + new_slide.shapes._spTree.append(copy.deepcopy(shape._element)) + +out = base + "Workshop_LIS.pptx" +dst.save(out) +print("saved", out) diff --git a/robotunits/memory/MEMORY.md b/robotunits/memory/MEMORY.md new file mode 100644 index 0000000..2536876 --- /dev/null +++ b/robotunits/memory/MEMORY.md @@ -0,0 +1,3 @@ +# Memory Index + +- [Würth plato-backend ist strikt read-only](wuerth-plato-backend-read-only.md) — niemals Änderungen in /home/marcuh/git/wuerth/fahrzeugeinrichtung-plato-backend diff --git a/robotunits/memory/wuerth-plato-backend-read-only.md b/robotunits/memory/wuerth-plato-backend-read-only.md new file mode 100644 index 0000000..f8a1aaa --- /dev/null +++ b/robotunits/memory/wuerth-plato-backend-read-only.md @@ -0,0 +1,14 @@ +--- +name: wuerth-plato-backend-read-only +description: /home/marcuh/git/wuerth/fahrzeugeinrichtung-plato-backend ist strikt read-only — niemals Änderungen dort machen +metadata: + node_type: memory + type: feedback + originSessionId: e3bfa548-0bcc-4bf3-80f4-4360920c38ac +--- + +Das Verzeichnis `/home/marcuh/git/wuerth/fahrzeugeinrichtung-plato-backend` darf ausschließlich gelesen werden. Keine Edits, Writes, git-Operationen oder sonstige Änderungen dort — unter keinen Umständen. + +**Why:** Ausdrückliche, nachdrückliche Anweisung des Users (2026-06-10): "dort KEINE ÄNDERUNGEN MACHEN!!!!!!!! - HART NUR LESEN". Vermutlich Kunden-/Fremdrepo (Würth). + +**How to apply:** In diesem Verzeichnis nur Read/Grep/Glob bzw. lesende Bash-Befehle verwenden. Erkenntnisse daraus dürfen genutzt werden, um Code in [[adesso-cpq]] oder geko-core zu schreiben.