#!/usr/bin/env python3
"""Generiert die Board-Layout-Skizze (SVG) fuer den SLC-Workshop.
Lineares Phasen-Swimlane-Layout: jede Phase eine Zeile, Pucks links->rechts.
Exakt 40 Pucks (37 Aktivitaeten + 3 Gate-Pucks). Reproduzierbar: bei Aenderungen
einfach erneut ausfuehren -> board-layout.svg.
"""
import math
# (id, kurzname, is_gate)
PHASES = [
("DESIGN", "#2F80C9", [
("ds_01", "Eigenschaften definieren", False),
("ds_02", "Komponenten designen", False),
("ds_03", "Vorgehen beschreiben", False),
("ds_04", "Implementierung vorbereiten", False),
]),
("TRANSITION", "#E8893B", [
("tr_01", "Entw. / Konfig.?", True),
("tr_02", "Entwicklung koordinieren", False),
("tr_03", "Anwendungen entwickeln", False),
("tr_04", "Komponenten annehmen", False),
("tr_05", "Komponenten konfigurieren", False),
("tr_06", "Betriebsdoku erstellen", False),
("tr_07", "Komponenten testen", False),
("tr_08", "Formale Uebergabe", False),
("tr_09", "Entry-Pruefung", True),
("tr_10", "Ausrollen", False),
("tr_11", "Aktivierung vorbereiten", False),
("tr_12", "Go-Live-Freigabe", True),
]),
("OPERATION", "#5BAE5B", [
("op_01", "Early Life Support", False),
("op_02", "Betriebs-Leitlinien", False),
("op_03", "Laufender Betrieb", False),
("op_04", "Ressourcen & Budget", False),
("op_05", "Services ueberwachen", False),
("op_06", "Qualitaetsbericht", False),
("op_07", "Proaktive Problemerkennung", False),
]),
("SUPPORT", "#3FB5B5", [
("sp_01", "Support-Leitlinien", False),
("sp_02", "Wissensdatenbank", False),
("sp_03", "Incidents/Requests verteilen", False),
("sp_04", "Requests bearbeiten", False),
("sp_05", "Incident 1st Level", False),
("sp_06", "Incident 2nd Level", False),
("sp_07", "Record geloest", False),
("sp_08", "Schliessen", False),
("sp_09", "Problem Record anlegen", False),
("sp_10", "Wiederk. Incidents -> Problem", False),
("sp_11", "RCA & Workaround", False),
]),
("REVIEW", "#8E63B5", [
("rv_01", "Service-Reviews durchf.", False),
("rv_02", "Bewertung d. Ergebnisse", False),
("rv_03", "Aenderungen definieren", False),
("rv_04", "Aenderungen starten", False),
("rv_05", "Aenderungen umsetzen", False),
]), # Arbeitsstand Frank (Change-Enablement); nicht im YAML/Konzept
]
# Layout-Parameter
TILE_W, TILE_H = 86, 86 # Zelle je Puck (rund, inscribed)
GAP_X, GAP_Y = 12, 40
PUCK_R = 35 # Puck-Radius in px (= Ø100 mm)
LABEL_W = 150
X0 = 30 + LABEL_W + 20
Y0 = 96
MAX_TILES = max(len(t) for _, _, t in PHASES)
WIDTH = X0 + MAX_TILES * (TILE_W + GAP_X) + 200
HEIGHT = Y0 + len(PHASES) * (TILE_H + GAP_Y) + 120
TILE_MM = 100 # ein Puck = Ø100 mm
def esc(s):
return s.replace("&", "&").replace("<", "<").replace(">", ">")
def lighten(hexcol, f=0.85):
h = hexcol.lstrip("#")
r, g, b = int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16)
r = int(r + (255 - r) * f)
g = int(g + (255 - g) * f)
b = int(b + (255 - b) * f)
return f"#{r:02x}{g:02x}{b:02x}"
def tile_svg(x, y, tid, name, color, is_gate):
"""Zeichnet einen runden Puck: Aussenring, 7 Figurenmulden, zentrales Etikett."""
cx, cy = x + TILE_W / 2.0, y + TILE_H / 2.0
fill = color if is_gate else lighten(color, 0.90)
stroke = color
sw = 3 if is_gate else 2
parts = []
# Puck-Koerper
parts.append(f'')
# 7 Figurenmulden im Ring
for k in range(7):
a = math.radians(360.0 / 7 * k - 90)
wx = cx + (PUCK_R - 8) * math.cos(a)
wy = cy + (PUCK_R - 8) * math.sin(a)
parts.append(f'')
# zentrales Etikett-Feld
parts.append(f'')
parts.append(f'{esc(tid)}')
# Name unter dem Puck
parts.append(f'{esc(name)}')
if is_gate:
parts.append(f'GATE')
return "\n".join(parts)
def arrow(x1, y1, x2, y2, color="#666", w=2.2):
return (f'')
svg = []
svg.append(f'')
out = "board-layout.svg"
with open(out, "w", encoding="utf-8") as f:
f.write("\n".join(svg))
total = sum(len(t) for _, _, t in PHASES)
gates = sum(1 for _, _, t in PHASES for _, _, g in t if g)
print(f"geschrieben: {out}")
print(f"Pucks gesamt: {total} (Aktivitaeten: {total-gates}, Gate-Pucks: {gates})")