initial
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
,marcuh,mach12,12.06.2026 09:55,file:///home/marcuh/.config/libreoffice/4;
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
- [Limbach Workshop / Outline](limbach-workshop-outline.md) — Wissensaufbau in Outline für Workshop 15./16.06.2026
|
||||||
@@ -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).
|
||||||
@@ -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)
|
||||||
@@ -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
|
||||||
@@ -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.
|
||||||
Reference in New Issue
Block a user