fix: update rack width controls and images

This commit is contained in:
2026-08-12 15:57:22 +02:00
parent c663069825
commit 4ee78ae4fe
6 changed files with 56 additions and 5 deletions
+7 -1
View File
@@ -36,7 +36,7 @@ Release-Tag oder ein bestimmter Commit verwendet werden:
```bash
/opt/netbox/venv/bin/pip install --upgrade --force-reinstall \
"git+https://git.mrblake.cc/MrBlake/Netbox-Utilities.git@v0.8.6"
"git+https://git.mrblake.cc/MrBlake/Netbox-Utilities.git@v0.8.7"
```
Alternativ kann hinter dem `@` die vollständige Commit-ID stehen.
@@ -150,6 +150,12 @@ sowohl die Gerätehöhe als auch die Breite und verhindert horizontale oder
vertikale Überschneidungen. Mehrere HE hohe Geräte werden ebenfalls
unterstützt.
Nach einer Änderung der Rackbreite wird die **Breitenposition** unmittelbar
aktualisiert; ein Zwischenspeichern oder Neuladen ist nicht erforderlich.
Hinterlegte Vorder- und Rückseitenbilder des Gerätetyps werden bei
Teilbreitengeräten proportional in die verfügbare Breite eingepasst, ohne das
Bild seitlich abzuschneiden.
Ohne Breitenangabe belegt ein Gerät wie bisher die volle Rackbreite. Das gilt
automatisch für sämtliche vorhandenen Geräte; es findet keine Änderung oder
Migration bestehender Platzierungen statt. Teilbreite und Position werden auf
+1 -1
View File
@@ -1,6 +1,6 @@
from netbox.plugins import PluginConfig, get_plugin_config
__version__ = "0.8.6"
__version__ = "0.8.7"
class NetBoxUtilitiesConfig(PluginConfig):
+14
View File
@@ -465,6 +465,7 @@ def _install_rack_svg():
elevation.draw_device_front(device, coords, size)
else:
elevation.draw_device_rear(device, coords, size)
_fit_latest_elevation_image(elevation.drawing)
else:
from svgwrite.shapes import Rect
@@ -496,6 +497,19 @@ def _partial_width_elevation_devices(rack, face):
)
def _fit_latest_elevation_image(drawing):
"""Fit the latest device image into a partial-width slot without cropping it."""
from svgwrite.image import Image
if not getattr(drawing, "elements", None):
return
container = drawing.elements[-1]
for element in reversed(getattr(container, "elements", ())):
if isinstance(element, Image):
element.fit(scale="meet")
return
def _validate_before_save(sender, instance, raw=False, **kwargs):
if not raw:
using = kwargs.get("using")
@@ -103,11 +103,18 @@
const position = Number.parseInt(option.value || '1', 10);
option.disabled = position > width;
});
if (width === 1 || Number.parseInt(positionField.value || '1', 10) > width) {
const resetPosition = width === 1 || Number.parseInt(positionField.value || '1', 10) > width;
if (resetPosition) {
positionField.value = '1';
}
positionField.disabled = width === 1;
if (positionField.tomselect) positionField.tomselect.sync();
if (resetPosition) {
positionField.dispatchEvent(new Event('change', {bubbles: true}));
}
positionField.disabled = width === 1;
};
widthField.addEventListener('change', updatePositions);
+24
View File
@@ -1,4 +1,5 @@
from importlib import import_module
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import MagicMock, patch
@@ -12,6 +13,7 @@ from django.urls import resolve, reverse
from netbox_utilities.rack_width import (
_cleanup_unracked_placement,
_fit_latest_elevation_image,
_partial_width_elevation_devices,
available_units_for_device,
horizontal_interval,
@@ -25,6 +27,28 @@ from netbox_utilities.rack_width import (
class RackWidthTest(SimpleTestCase):
def test_rack_width_javascript_synchronizes_netbox_tom_select(self):
script = (Path(__file__).parents[1] / "static" / "netbox_utilities" / "forms.js").read_text(encoding="utf-8")
self.assertIn("positionField.disabled = width === 1", script)
self.assertIn("positionField.tomselect.sync()", script)
def test_partial_width_device_image_is_fitted_without_cropping(self):
from svgwrite import Drawing
from svgwrite.container import Hyperlink
from svgwrite.image import Image
drawing = Drawing()
link = Hyperlink("/dcim/devices/1/")
device_image = Image("/media/device.png", size=(100, 20))
device_image.fit(scale="slice")
link.add(device_image)
drawing.add(link)
_fit_latest_elevation_image(drawing)
self.assertEqual(device_image.attribs["preserveAspectRatio"], "xMidYMid meet")
def test_partial_width_svg_devices_include_core_device_bay_annotation(self):
from dcim.choices import DeviceFaceChoices
from dcim.models import Rack
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project]
name = "netbox-utilities"
version = "0.8.6"
version = "0.8.7"
description = "Navigation, tenant utilities, partial-width rack devices, bulk uploads, and rack reordering for NetBox 4.6"
readme = "README.md"
requires-python = ">=3.12"