feat: optional topology per rack

- New topology option "Umfang": whole network (per site) and/or per
  rack, one landscape page each. Rack pages are grouped under
  "Topologie je Rack" with the rack titles one heading level lower to
  keep the table of contents short.
- Directly connected devices outside the rack are shown dimmed and
  dashed with their rack as subtitle, so uplinks stay visible.
- Cable paths are collected once per export and reused for all views.

Fix: devices from other sites were able to pull in their own neighbors
as external nodes; only direct neighbors of a group's devices are shown
now.

Bump version to 0.8.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-23 16:01:06 +02:00
co-authored by Claude Opus 5.5
parent 3ad7cc6810
commit 0cd72824d5
8 changed files with 98 additions and 45 deletions
+13 -6
View File
@@ -432,12 +432,19 @@ def topology(scope, opts):
from .topology import build_graphs, render_png
title = H(1, "Netzwerk-Topologie")
graphs = build_graphs(scope, opts)
if not graphs:
return _empty(opts, [title], "Keine verkabelten Geräte gefunden.")
scopes = opts.get("topo_scope") or ["site"]
ports = "ports" in (opts.get("topo_details") or [])
# Je Standort eine Querformat-Seite; die Abschnittsüberschrift steht mit auf der ersten
pages = [] # (Überschriften, Graph) – je eine Querformat-Seite
if "site" in scopes:
pages += [([H(2, g.title)], g) for g in build_graphs(scope, opts, per="site")]
if "rack" in scopes:
racks = build_graphs(scope, opts, per="rack")
# Rack-Seiten unter einer gemeinsamen Zwischenüberschrift, Racks selbst eine Ebene tiefer (kürzeres Inhaltsverzeichnis)
pages += [(([H(2, "Topologie je Rack")] if i == 0 else []) + [H(3, g.title)], g) for i, g in enumerate(racks)]
if not pages:
return _empty(opts, [title], "Keine verkabelten Geräte gefunden.")
# Die Abschnittsüberschrift steht mit auf der ersten Seite
return [
LANDSCAPE(([title] if i == 0 else []) + [H(2, g.title)], render_png(g, show_ports=ports))
for i, g in enumerate(graphs)
LANDSCAPE(([title] if i == 0 else []) + headings, render_png(g, show_ports=ports))
for i, (headings, g) in enumerate(pages)
]