unifi-network-maps 1.4.0__py3-none-any.whl → 1.4.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,9 @@ from .mermaid_theme import DEFAULT_THEME, MermaidTheme, class_defs
9
9
 
10
10
 
11
11
  def _escape(label: str) -> str:
12
- return label.replace('"', '\\"')
12
+ normalized = label.replace("\r\n", "\n").replace("\r", "\n")
13
+ escaped = normalized.replace("\\", "\\\\").replace("\n", "\\n")
14
+ return escaped.replace('"', '\\"')
13
15
 
14
16
 
15
17
  def _slugify(value: str) -> str:
@@ -54,39 +56,45 @@ def _node_ref(name: str, node_id: str) -> str:
54
56
  return f'{node_id}["{_escape(name)}"]'
55
57
 
56
58
 
57
- def render_mermaid(
58
- edges: Iterable[Edge],
59
- direction: str = "LR",
59
+ def _group_nodes(groups: dict[str, list[str]] | None) -> list[str]:
60
+ if not groups:
61
+ return []
62
+ nodes: list[str] = []
63
+ for members in groups.values():
64
+ nodes.extend(members)
65
+ return nodes
66
+
67
+
68
+ def _render_group_sections(
69
+ lines: list[str],
70
+ groups: dict[str, list[str]],
60
71
  *,
61
- groups: dict[str, list[str]] | None = None,
62
- group_order: list[str] | None = None,
63
- node_types: dict[str, str] | None = None,
64
- theme: MermaidTheme = DEFAULT_THEME,
65
- ) -> str:
66
- edge_list = list(edges)
67
- group_nodes: list[str] = []
68
- if groups:
69
- for members in groups.values():
70
- group_nodes.extend(members)
71
- id_map = _build_id_map(edge_list, group_nodes)
72
- lines = [f"graph {direction}"]
72
+ group_order: list[str] | None,
73
+ id_map: dict[str, str],
74
+ ) -> None:
75
+ ordered = group_order or list(groups.keys())
76
+ for group_name in ordered:
77
+ members = groups.get(group_name, [])
78
+ if not members:
79
+ continue
80
+ group_id = _slugify(f"group_{group_name}")
81
+ label = group_name.replace("_", " ").title()
82
+ lines.append(f' subgraph {group_id}["{_escape(label)}"];')
83
+ for member in members:
84
+ lines.append(f" {_node_ref(member, id_map[member])};")
85
+ lines.append(" end")
86
+
87
+
88
+ def _render_edge_lines(
89
+ lines: list[str],
90
+ edges: list[Edge],
91
+ *,
92
+ id_map: dict[str, str],
93
+ use_node_labels: bool,
94
+ ) -> tuple[list[int], list[int]]:
73
95
  poe_links: list[int] = []
74
96
  wireless_links: list[int] = []
75
- link_index = 0
76
- if groups:
77
- ordered = group_order or list(groups.keys())
78
- for group_name in ordered:
79
- members = groups.get(group_name, [])
80
- if not members:
81
- continue
82
- group_id = _slugify(f"group_{group_name}")
83
- label = group_name.replace("_", " ").title()
84
- lines.append(f' subgraph {group_id}["{_escape(label)}"];')
85
- for member in members:
86
- lines.append(f" {_node_ref(member, id_map[member])};")
87
- lines.append(" end")
88
- use_node_labels = not groups
89
- for edge in edge_list:
97
+ for index, edge in enumerate(edges):
90
98
  if use_node_labels:
91
99
  left = _node_ref(edge.left, id_map[edge.left])
92
100
  right = _node_ref(edge.right, id_map[edge.right])
@@ -99,25 +107,41 @@ def render_mermaid(
99
107
  else:
100
108
  lines.append(f" {left} --- {right};")
101
109
  if edge.poe:
102
- poe_links.append(link_index)
110
+ poe_links.append(index)
103
111
  if edge.wireless:
104
- wireless_links.append(link_index)
105
- link_index += 1
106
- if node_types:
107
- class_map = {
108
- "gateway": "node_gateway",
109
- "switch": "node_switch",
110
- "ap": "node_ap",
111
- "client": "node_client",
112
- "other": "node_other",
113
- }
114
- if node_types:
115
- for name, node_type in node_types.items():
116
- class_name = class_map.get(node_type, "node_other")
117
- node_id = id_map.get(name)
118
- if node_id:
119
- lines.append(f" class {node_id} {class_name};")
120
- lines.extend(class_defs(theme))
112
+ wireless_links.append(index)
113
+ return poe_links, wireless_links
114
+
115
+
116
+ def _render_node_classes(
117
+ lines: list[str],
118
+ *,
119
+ node_types: dict[str, str],
120
+ id_map: dict[str, str],
121
+ theme: MermaidTheme,
122
+ ) -> None:
123
+ class_map = {
124
+ "gateway": "node_gateway",
125
+ "switch": "node_switch",
126
+ "ap": "node_ap",
127
+ "client": "node_client",
128
+ "other": "node_other",
129
+ }
130
+ for name, node_type in node_types.items():
131
+ class_name = class_map.get(node_type, "node_other")
132
+ node_id = id_map.get(name)
133
+ if node_id:
134
+ lines.append(f" class {node_id} {class_name};")
135
+ lines.extend(class_defs(theme))
136
+
137
+
138
+ def _render_link_styles(
139
+ lines: list[str],
140
+ *,
141
+ poe_links: list[int],
142
+ wireless_links: list[int],
143
+ theme: MermaidTheme,
144
+ ) -> None:
121
145
  for index in poe_links:
122
146
  lines.append(
123
147
  " linkStyle "
@@ -126,6 +150,29 @@ def render_mermaid(
126
150
  )
127
151
  for index in wireless_links:
128
152
  lines.append(f" linkStyle {index} stroke-dasharray: 5 4;")
153
+
154
+
155
+ def render_mermaid(
156
+ edges: Iterable[Edge],
157
+ direction: str = "LR",
158
+ *,
159
+ groups: dict[str, list[str]] | None = None,
160
+ group_order: list[str] | None = None,
161
+ node_types: dict[str, str] | None = None,
162
+ theme: MermaidTheme = DEFAULT_THEME,
163
+ ) -> str:
164
+ edge_list = list(edges)
165
+ id_map = _build_id_map(edge_list, _group_nodes(groups))
166
+ lines = [f"graph {direction}"]
167
+ if groups:
168
+ _render_group_sections(lines, groups, group_order=group_order, id_map=id_map)
169
+ use_node_labels = not groups
170
+ poe_links, wireless_links = _render_edge_lines(
171
+ lines, edge_list, id_map=id_map, use_node_labels=use_node_labels
172
+ )
173
+ if node_types:
174
+ _render_node_classes(lines, node_types=node_types, id_map=id_map, theme=theme)
175
+ _render_link_styles(lines, poe_links=poe_links, wireless_links=wireless_links, theme=theme)
129
176
  return "\n".join(lines) + "\n"
130
177
 
131
178