Skip to content

Commit 82b6437

Browse files
authored
Merge pull request #86 from sweickge/master
Edit Floorplan Screen Enhancements and Bug Fixes
2 parents 91cabd5 + 896011e commit 82b6437

File tree

9 files changed

+781
-51
lines changed

9 files changed

+781
-51
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ Originally Forked from https://github.com/tbotnz/netbox_floorplan
77
## Demo
88
![demo](/media/demo.gif)
99

10+
## New Demo Showing New Plugin Behavior with Advanced Rack Behavior
11+
12+
![new-enhancements](/media/new-floorplan-demo.gif)
13+
1014
## Summary
1115
A netbox plugin providing floorplan mapping capability for locations and sites
1216

media/new-floorplan-demo.gif

14.3 MB
Loading

netbox_floorplan/models.py

Lines changed: 114 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ def mapped_devices(self):
187187
return drawn_devices
188188

189189
def resync_canvas(self):
190+
"""
191+
Synchronize canvas objects with current NetBox data.
192+
Handles both advanced mode (role/tenant/status) and original mode (status only) rack displays.
193+
"""
190194
changed = False
191195
if self.canvas:
192196
if self.canvas.get("objects"):
@@ -202,19 +206,93 @@ def resync_canvas(self):
202206
else:
203207
rack = rack_qs.first()
204208
self.canvas["objects"][index]["custom_meta"]["object_name"] = rack.name
209+
210+
# Update rack fill color based on role (only if not manually set
211+
# and for advanced racks only)
212+
# Check if color was manually set by looking for manual_color flag
213+
color_manually_set = obj["custom_meta"].get("manual_color", False)
214+
215+
# Detect if this is an advanced rack by checking for info text type
216+
is_advanced_mode_rack = False
217+
if obj.get("objects"):
218+
for subobj in obj["objects"]:
219+
if (subobj.get("type") in ["i-text", "textbox"] and
220+
subobj.get("custom_meta", {}).get("text_type") == "info"):
221+
is_advanced_mode_rack = True
222+
break
223+
224+
# Only apply automatic color updates to advanced racks
225+
if not color_manually_set and is_advanced_mode_rack:
226+
expected_color = None
227+
if rack.role and hasattr(rack.role, 'color'):
228+
expected_color = f"#{rack.role.color}"
229+
else:
230+
# Default color if no role or color is set
231+
expected_color = "#000000"
232+
233+
# Check if the rack rectangle color needs updating
234+
if obj.get("objects") and len(obj["objects"]) > 0:
235+
rack_rect = obj["objects"][0] # First object is typically the rack rectangle
236+
if rack_rect.get("fill") != expected_color:
237+
self.canvas["objects"][index]["objects"][0]["fill"] = expected_color
238+
changed = True
239+
# End of rack fill color update
240+
205241
if obj.get("objects"):
206242
for subcounter, subobj in enumerate(obj["objects"]):
207-
if subobj.get("type") == "i-text":
243+
# Check if the subobject is a rectangle and has custom_meta for rack
244+
# Update the custom_meta and text fields to match the current rack data
245+
# in Netbox
246+
if subobj.get("type") == "rect":
247+
if subobj.get("custom_meta", {}).get("object_type") == "rack":
248+
# Make sure the object_name matches the actual rack name
249+
if subobj["custom_meta"]["object_name"] != f"{rack.name}":
250+
self.canvas["objects"][index]["objects"][
251+
subcounter]["custom_meta"]["object_name"] = f"{rack.name}"
252+
changed = True
253+
254+
# Check if the subobject is a textbox or i-text object. This will have both
255+
# the rack name and the info text (status, role, tenant for advanced racks
256+
# or just status for simple racks).
257+
if subobj.get("type") == "i-text" or subobj.get("type") == "textbox":
258+
# Update the name text box with the current rack name if it exists
208259
if subobj.get("custom_meta", {}).get("text_type") == "name":
209260
if subobj["text"] != f"{rack.name}":
210261
self.canvas["objects"][index]["objects"][
211262
subcounter]["text"] = f"{rack.name}"
212263
changed = True
213-
if subobj.get("custom_meta", {}).get("text_type") == "status":
264+
# Handle advanced racks combined info text box
265+
elif subobj.get("custom_meta", {}).get("text_type") == "info":
266+
# Handle combined info text box (advanced mode)
267+
rack_role_text = rack.role.name if rack.role else ""
268+
rack_tenant_text = f"{rack.tenant}" if rack.tenant else ""
269+
270+
# Update stored values in custom_meta
271+
subobj["custom_meta"]["status"] = f"{rack.status}"
272+
subobj["custom_meta"]["role"] = rack_role_text
273+
subobj["custom_meta"]["tenant"] = rack_tenant_text
274+
275+
# Rebuild the combined text based on visibility settings
276+
info_lines = []
277+
if subobj["custom_meta"].get("show_status", True):
278+
info_lines.append(f"{rack.status}")
279+
if subobj["custom_meta"].get("show_role", True) and rack_role_text:
280+
info_lines.append(rack_role_text)
281+
if subobj["custom_meta"].get("show_tenant", True) and rack_tenant_text:
282+
info_lines.append(rack_tenant_text)
283+
284+
new_text = '\n'.join(info_lines)
285+
if subobj["text"] != new_text:
286+
self.canvas["objects"][index]["objects"][subcounter]["text"] = new_text
287+
changed = True
288+
# Handle simple racks status text box, which only shows status
289+
elif subobj.get("custom_meta", {}).get("text_type") == "status":
214290
if subobj["text"] != f"{rack.status}":
215291
self.canvas["objects"][index]["objects"][
216292
subcounter]["text"] = f"{rack.status}"
217293
changed = True
294+
295+
# Handle device objects on the canvas
218296
if obj["custom_meta"].get("object_type") == "device":
219297
device_id = int(obj["custom_meta"]["object_id"])
220298
# if device is not in the database, remove it from the canvas
@@ -227,13 +305,45 @@ def resync_canvas(self):
227305
self.canvas["objects"][index]["custom_meta"]["object_name"] = device.name
228306
if obj.get("objects"):
229307
for subcounter, subobj in enumerate(obj["objects"]):
230-
if subobj.get("type") == "i-text":
308+
# Update device rectangle metadata
309+
if subobj.get("type") == "rect":
310+
if subobj.get("custom_meta", {}).get("object_type") == "device":
311+
# Make sure the object_name matches the actual device name
312+
if subobj["custom_meta"]["object_name"] != f"{device.name}":
313+
self.canvas["objects"][index]["objects"][
314+
subcounter]["custom_meta"]["object_name"] = f"{device.name}"
315+
changed = True
316+
# Update device text elements (supports both advanced and simple devices)
317+
if subobj.get("type") == "i-text" or subobj.get("type") == "textbox":
318+
319+
# Update device name text
231320
if subobj.get("custom_meta", {}).get("text_type") == "name":
232321
if subobj["text"] != f"{device.name}":
233322
self.canvas["objects"][index]["objects"][
234323
subcounter]["text"] = f"{device.name}"
235324
changed = True
236-
if subobj.get("custom_meta", {}).get("text_type") == "status":
325+
# Handle advanced devices combined info text box for devices
326+
elif subobj.get("custom_meta", {}).get("text_type") == "info":
327+
# Handle combined info text box for devices (advanced mode)
328+
device_tenant_text = f"{device.tenant}" if device.tenant else ""
329+
330+
# Update stored values in custom_meta
331+
subobj["custom_meta"]["status"] = f"{device.status}"
332+
subobj["custom_meta"]["tenant"] = device_tenant_text
333+
334+
# Rebuild the combined text based on visibility settings
335+
info_lines = []
336+
if subobj["custom_meta"].get("show_status", True):
337+
info_lines.append(f"{device.status}")
338+
if subobj["custom_meta"].get("show_tenant", True) and device_tenant_text:
339+
info_lines.append(device_tenant_text)
340+
341+
new_text = '\n'.join(info_lines)
342+
if subobj["text"] != new_text:
343+
self.canvas["objects"][index]["objects"][subcounter]["text"] = new_text
344+
changed = True
345+
# Handle 'simple' devices status text box
346+
elif subobj.get("custom_meta", {}).get("text_type") == "status":
237347
if subobj["text"] != f"{device.status}":
238348
self.canvas["objects"][index]["objects"][
239349
subcounter]["text"] = f"{device.status}"

0 commit comments

Comments
 (0)