rework load/save of coords; add group coords save

This commit is contained in:
dreng
2023-05-06 23:01:08 +02:00
parent 9cc1607811
commit dbf2d23905
7 changed files with 103 additions and 82 deletions
+43 -36
View File
@@ -33,6 +33,7 @@ class SaveCoordsViewSet(ReadOnlyModelViewSet):
device_id: str = request.data.get("node_id", None) device_id: str = request.data.get("node_id", None)
x_coord = request.data.get("x", None) x_coord = request.data.get("x", None)
y_coord = request.data.get("y", None) y_coord = request.data.get("y", None)
group_id = request.data.get("group", "None")
actual_device = None actual_device = None
if device_id.startswith("c"): if device_id.startswith("c"):
@@ -50,53 +51,59 @@ class SaveCoordsViewSet(ReadOnlyModelViewSet):
if not actual_device: if not actual_device:
return Response({"status": "invalid node_id in body"}, status=400) return Response({"status": "invalid node_id in body"}, status=400)
# Storing coordinates in custom field is deprecated now. if group_id is None or group_id == "default":
# We preserve this for backwards compatibility. # Storing coordinates in custom field is deprecated now.
try: # We preserve this for backwards compatibility.
actual_device.custom_field_data["coordinates"] = "%s;%s" % (
x_coord,
y_coord,
)
actual_device.save()
except:
return Response(
{"status": "coords custom field could not be saved"}, status=500
)
# Default group named "default" must always exist in order to make sure
# that coordinate values can be stored even if no coordinate group has been
# selected. The default group will be added automatically if it does not exist.
try:
group = CoordinateGroup.objects.get(name="default")
except CoordinateGroup.DoesNotExist:
try: try:
group = CoordinateGroup( actual_device.custom_field_data["coordinates"] = "%s;%s" % (
name="default", x_coord,
description="Automatically generated default group. If you delete " y_coord,
"this group, all default coordinates are gone for good but "
"the group itself will be re-created."
) )
group.save() actual_device.save()
except:
return Response(
{"status": "coords custom field could not be saved"}, status=500
)
# Default group named "default" must exist in order to make sure that
# coordinate values can be stored even if no coordinate group has been
# selected. The default group will be added automatically if it does not exist.
try:
if CoordinateGroup.objects.filter(name="default"):
group = CoordinateGroup.objects.get(name="default")
group_id = group.pk
else:
group = CoordinateGroup(
name="default",
description="Automatically generated default group. If you delete "
"this group, all default coordinates are gone for good but "
"the group itself will be re-created."
)
group.save()
group_id = group.pk
except: except:
return Response( return Response(
{"status": "Error while creating default group."}, status=500 {"status": "Error while creating default group."}, status=500
) )
try: try:
# Hen-and-egg-problem. Thanks, Django! By default, Django updates records that if CoordinateGroup.objects.filter(pk=group_id):
# already exist and inserts otherwise. This does not work with our group = CoordinateGroup.objects.get(pk=group_id)
# unique_together key if no pk is given. But: No record, no pk. # Hen-and-egg-problem. Thanks, Django! By default, Django updates records that
if not Coordinate.objects.filter(group=group, device=actual_device): # already exist and inserts otherwise. This does not work with our
# Unique group/device pair does not exist. Prepare new data set # unique_together key if no pk is given. But: No record, no pk.
coords = Coordinate(group=group, device=actual_device, x=x_coord, y=y_coord) if not Coordinate.objects.filter(group=group, device=actual_device):
else: # Unique group/device pair does not exist. Prepare new data set
# Unique group/device pair already exists. Update data coords = Coordinate(group=group, device=actual_device, x=x_coord, y=y_coord)
coords = Coordinate(pk=Coordinate.objects.get(group=group, device=actual_device).pk, group=group, device=actual_device, x=x_coord, y=y_coord) else:
coords.save() # Unique group/device pair already exists. Update data
coords = Coordinate(pk=Coordinate.objects.get(group=group, device=actual_device).pk, group=group, device=actual_device, x=x_coord, y=y_coord)
coords.save()
except: except:
return Response( return Response(
{"status": "Coordinates could not be saved."}, status=500 {"status": "Coordinates could not be saved."}, status=500
) )
return Response({"status": "saved coords"}) return Response({"status": "saved coords"})
class ExportTopoToXML(PermissionRequiredMixin, ViewSet): class ExportTopoToXML(PermissionRequiredMixin, ViewSet):
+5 -1
View File
@@ -31,6 +31,7 @@ class DeviceFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
( (
"q", "q",
"filter_id", "filter_id",
"group",
"save_coords", "save_coords",
"show_unconnected", "show_unconnected",
"show_cables", "show_cables",
@@ -60,7 +61,10 @@ class DeviceFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
), ),
(None, ("tag",)), (None, ("tag",)),
) )
group = forms.ModelChoiceField(
queryset=CoordinateGroup.objects.all(),
required=False
)
region_id = DynamicModelMultipleChoiceField( region_id = DynamicModelMultipleChoiceField(
queryset=Region.objects.all(), required=False, label=_("Region") queryset=Region.objects.all(), required=False, label=_("Region")
) )
File diff suppressed because one or more lines are too long
+2 -1
View File
@@ -88,7 +88,8 @@ const coordSaveCheckbox = document.querySelector('#id_save_coords')
body: JSON.stringify({ body: JSON.stringify({
node_id: nodeId, node_id: nodeId,
x: nodePosition.x, x: nodePosition.x,
y: nodePosition.y y: nodePosition.y,
group: topologyData.group
}) })
} }
) )
+9 -13
View File
@@ -8,7 +8,6 @@
"version": "2.0.17", "version": "2.0.17",
"resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz", "resolved": "https://registry.npmjs.org/@egjs/hammerjs/-/hammerjs-2.0.17.tgz",
"integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==", "integrity": "sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==",
"dev": true,
"requires": { "requires": {
"@types/hammerjs": "^2.0.36" "@types/hammerjs": "^2.0.36"
} }
@@ -23,8 +22,7 @@
"@types/hammerjs": { "@types/hammerjs": {
"version": "2.0.41", "version": "2.0.41",
"resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.41.tgz", "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.41.tgz",
"integrity": "sha512-ewXv/ceBaJprikMcxCmWU1FKyMAQ2X7a9Gtmzw8fcg2kIePI1crERDM818W+XYrxqdBBOdlf2rm137bU+BltCA==", "integrity": "sha512-ewXv/ceBaJprikMcxCmWU1FKyMAQ2X7a9Gtmzw8fcg2kIePI1crERDM818W+XYrxqdBBOdlf2rm137bU+BltCA=="
"dev": true
}, },
"anymatch": { "anymatch": {
"version": "3.1.3", "version": "3.1.3",
@@ -70,8 +68,7 @@
"component-emitter": { "component-emitter": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
"dev": true
}, },
"esbuild": { "esbuild": {
"version": "0.12.29", "version": "0.12.29",
@@ -350,8 +347,7 @@
"keycharm": { "keycharm": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.4.0.tgz", "resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.4.0.tgz",
"integrity": "sha512-TyQTtsabOVv3MeOpR92sIKk/br9wxS+zGj4BG7CR8YbK4jM3tyIBaF0zhzeBUMx36/Q/iQLOKKOT+3jOQtemRQ==", "integrity": "sha512-TyQTtsabOVv3MeOpR92sIKk/br9wxS+zGj4BG7CR8YbK4jM3tyIBaF0zhzeBUMx36/Q/iQLOKKOT+3jOQtemRQ=="
"dev": true
}, },
"normalize-path": { "normalize-path": {
"version": "3.0.0", "version": "3.0.0",
@@ -417,8 +413,7 @@
"timsort": { "timsort": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
"integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==", "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A=="
"dev": true
}, },
"to-regex-range": { "to-regex-range": {
"version": "5.0.1", "version": "5.0.1",
@@ -432,18 +427,19 @@
"uuid": { "uuid": {
"version": "8.3.2", "version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
"dev": true
}, },
"vis-data": { "vis-data": {
"version": "7.1.6", "version": "7.1.6",
"resolved": "https://registry.npmjs.org/vis-data/-/vis-data-7.1.6.tgz", "resolved": "https://registry.npmjs.org/vis-data/-/vis-data-7.1.6.tgz",
"integrity": "sha512-lG7LJdkawlKSXsdcEkxe/zRDyW29a4r7N7PMwxCPxK12/QIdqxJwcMxwjVj9ozdisRhP5TyWDHZwsgjmj0g6Dg==" "integrity": "sha512-lG7LJdkawlKSXsdcEkxe/zRDyW29a4r7N7PMwxCPxK12/QIdqxJwcMxwjVj9ozdisRhP5TyWDHZwsgjmj0g6Dg==",
"requires": {}
}, },
"vis-network": { "vis-network": {
"version": "9.1.6", "version": "9.1.6",
"resolved": "https://registry.npmjs.org/vis-network/-/vis-network-9.1.6.tgz", "resolved": "https://registry.npmjs.org/vis-network/-/vis-network-9.1.6.tgz",
"integrity": "sha512-Eiwx1JleAsUqfy4pzcsFngCVlCEdjAtRPB/OwCV7PHBm+o2jtE4IZPcPITAEGUlxvL4Fdw7/lZsfD32dL+IL6g==" "integrity": "sha512-Eiwx1JleAsUqfy4pzcsFngCVlCEdjAtRPB/OwCV7PHBm+o2jtE4IZPcPITAEGUlxvL4Fdw7/lZsfD32dL+IL6g==",
"requires": {}
}, },
"vis-util": { "vis-util": {
"version": "5.0.3", "version": "5.0.3",
+11 -11
View File
@@ -291,7 +291,7 @@
"resolved" "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" "resolved" "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz"
"version" "7.0.0" "version" "7.0.0"
"keycharm@^0.4.0": "keycharm@^0.2.0 || ^0.3.0 || ^0.4.0", "keycharm@^0.4.0":
"integrity" "sha512-TyQTtsabOVv3MeOpR92sIKk/br9wxS+zGj4BG7CR8YbK4jM3tyIBaF0zhzeBUMx36/Q/iQLOKKOT+3jOQtemRQ==" "integrity" "sha512-TyQTtsabOVv3MeOpR92sIKk/br9wxS+zGj4BG7CR8YbK4jM3tyIBaF0zhzeBUMx36/Q/iQLOKKOT+3jOQtemRQ=="
"resolved" "https://registry.npmjs.org/keycharm/-/keycharm-0.4.0.tgz" "resolved" "https://registry.npmjs.org/keycharm/-/keycharm-0.4.0.tgz"
"version" "0.4.0" "version" "0.4.0"
@@ -358,22 +358,22 @@
dependencies: dependencies:
"is-number" "^7.0.0" "is-number" "^7.0.0"
"uuid@^8.0.0": "uuid@^3.4.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", "uuid@^8.0.0":
"integrity" "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" "integrity" "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
"resolved" "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" "resolved" "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz"
"version" "8.3.2" "version" "8.3.2"
"vis-data@^7.1.4": "vis-data@^6.3.0 || ^7.0.0", "vis-data@^7.1.6":
"integrity" "sha512-usy+ePX1XnArNvJ5BavQod7YRuGQE1pjFl+pu7IS6rCom2EBoG0o1ZzCqf3l5US6MW51kYkLR+efxRbnjxNl7w==" "integrity" "sha512-lG7LJdkawlKSXsdcEkxe/zRDyW29a4r7N7PMwxCPxK12/QIdqxJwcMxwjVj9ozdisRhP5TyWDHZwsgjmj0g6Dg=="
"resolved" "https://registry.npmjs.org/vis-data/-/vis-data-7.1.4.tgz" "resolved" "https://registry.npmjs.org/vis-data/-/vis-data-7.1.6.tgz"
"version" "7.1.4" "version" "7.1.6"
"vis-network@^9.1.2": "vis-network@^9.1.6":
"integrity" "sha512-BdapguKg7sk3NvdZaDsM7T6rNhOBFz0/F4ZScxctK4klRzQPLQPTEcmbioXaZhMkkgWymzBR3lFCxL1q+eYyAw==" "integrity" "sha512-Eiwx1JleAsUqfy4pzcsFngCVlCEdjAtRPB/OwCV7PHBm+o2jtE4IZPcPITAEGUlxvL4Fdw7/lZsfD32dL+IL6g=="
"resolved" "https://registry.npmjs.org/vis-network/-/vis-network-9.1.2.tgz" "resolved" "https://registry.npmjs.org/vis-network/-/vis-network-9.1.6.tgz"
"version" "9.1.2" "version" "9.1.6"
"vis-util@^5.0.3": "vis-util@^5.0.1", "vis-util@^5.0.3":
"integrity" "sha512-Wf9STUcFrDzK4/Zr7B6epW2Kvm3ORNWF+WiwEz2dpf5RdWkLUXFSbLcuB88n1W6tCdFwVN+v3V4/Xmn9PeL39g==" "integrity" "sha512-Wf9STUcFrDzK4/Zr7B6epW2Kvm3ORNWF+WiwEz2dpf5RdWkLUXFSbLcuB88n1W6tCdFwVN+v3V4/Xmn9PeL39g=="
"resolved" "https://registry.npmjs.org/vis-util/-/vis-util-5.0.3.tgz" "resolved" "https://registry.npmjs.org/vis-util/-/vis-util-5.0.3.tgz"
"version" "5.0.3" "version" "5.0.3"
+32 -19
View File
@@ -25,7 +25,7 @@ from django.contrib.contenttypes.models import ContentType
from django.db.models import Q, QuerySet from django.db.models import Q, QuerySet
from django.db.models.functions import Lower from django.db.models.functions import Lower
from django.http import HttpRequest, HttpResponseRedirect, QueryDict from django.http import HttpRequest, HttpResponseRedirect, QueryDict
from django.shortcuts import render from django.shortcuts import render, get_object_or_404
from django.views.generic import View from django.views.generic import View
from extras.models import Tag from extras.models import Tag
from wireless.models import WirelessLink from wireless.models import WirelessLink
@@ -65,7 +65,7 @@ def get_image_for_entity(entity: Union[Device, Circuit, PowerPanel, PowerFeed]):
def create_node( def create_node(
device: Union[Device, Circuit, PowerPanel, PowerFeed], save_coords: bool device: Union[Device, Circuit, PowerPanel, PowerFeed], save_coords: bool, group_id="default"
): ):
node = {} node = {}
node_content = "" node_content = ""
@@ -156,22 +156,27 @@ def create_node(
node["href"] = device.get_absolute_url() node["href"] = device.get_absolute_url()
node["image"] = get_image_for_entity(device) node["image"] = get_image_for_entity(device)
# Default group named "default" must always exist in order to make sure if group_id is None or group_id == "default":
# that coordinate values can be stored even if no coordinate group has been # Default group named "default" must always exist in order to make sure
# selected. The default group will be added automatically if it does not exist. # that coordinate values can be stored even if no coordinate group has been
try: # selected. The default group will be added automatically if it does not exist.
group = CoordinateGroup.objects.get(name="default")
except CoordinateGroup.DoesNotExist:
try: try:
group = CoordinateGroup( if CoordinateGroup.objects.filter(name="default"):
name="default", group = CoordinateGroup.objects.get(name="default")
description="Automatically generated default group. If you delete " group_id = group.pk
"this group, all default coordinates are gone for good but " else:
"the group itself will be re-created." group = CoordinateGroup(
) name="default",
group.save() description="Automatically generated default group. If you delete "
"this group, all default coordinates are gone for good but "
"the group itself will be re-created."
)
group.save()
group_id = group.pk
except: except:
pass pass
group = get_object_or_404(CoordinateGroup, pk=group_id)
node["physics"] = True node["physics"] = True
if Coordinate.objects.filter(group=group, device=device.pk).values('x') and Coordinate.objects.filter(group=group, device=device.pk).values('y'): if Coordinate.objects.filter(group=group, device=device.pk).values('x') and Coordinate.objects.filter(group=group, device=device.pk).values('y'):
@@ -296,6 +301,7 @@ def get_topology_data(
show_neighbors: bool, show_neighbors: bool,
show_power: bool, show_power: bool,
show_wireless: bool, show_wireless: bool,
group_id,
): ):
supported_termination_types = [] supported_termination_types = []
@@ -417,7 +423,7 @@ def get_topology_data(
] = circuit_termination.circuit ] = circuit_termination.circuit
for d in nodes_circuits.values(): for d in nodes_circuits.values():
nodes.append(create_node(d, save_coords)) nodes.append(create_node(d, save_coords, group_id))
if show_power: if show_power:
power_panels_ids = PowerPanel.objects.filter( power_panels_ids = PowerPanel.objects.filter(
@@ -467,10 +473,10 @@ def get_topology_data(
cable_ids[power_feed.cable_id][power_feed.cable_end] = termination_b cable_ids[power_feed.cable_id][power_feed.cable_end] = termination_b
for d in nodes_powerfeed.values(): for d in nodes_powerfeed.values():
nodes.append(create_node(d, save_coords)) nodes.append(create_node(d, save_coords, group_id))
for d in nodes_powerpanel.values(): for d in nodes_powerpanel.values():
nodes.append(create_node(d, save_coords)) nodes.append(create_node(d, save_coords, group_id))
if show_logical_connections: if show_logical_connections:
interfaces = Interface.objects.filter( interfaces = Interface.objects.filter(
@@ -627,10 +633,11 @@ def get_topology_data(
results = {} results = {}
for d in nodes_devices.values(): for d in nodes_devices.values():
nodes.append(create_node(d, save_coords)) nodes.append(create_node(d, save_coords, group_id))
results["nodes"] = nodes results["nodes"] = nodes
results["edges"] = edges results["edges"] = edges
results["group"] = group_id
return results return results
@@ -658,6 +665,11 @@ class TopologyHomeView(PermissionRequiredMixin, View):
save_coords, show_unconnected, show_power, show_circuit, show_logical_connections, show_single_cable_logical_conns, show_cables, show_wireless, show_neighbors = get_query_settings(request) save_coords, show_unconnected, show_power, show_circuit, show_logical_connections, show_single_cable_logical_conns, show_cables, show_wireless, show_neighbors = get_query_settings(request)
if "group" not in request.GET:
group_id = "default"
else:
group_id = request.GET["group"]
if not "draw_init" in request.GET or "draw_init" in request.GET and request.GET["draw_init"].lower() == "true": if not "draw_init" in request.GET or "draw_init" in request.GET and request.GET["draw_init"].lower() == "true":
topo_data = get_topology_data( topo_data = get_topology_data(
queryset=self.queryset, queryset=self.queryset,
@@ -671,6 +683,7 @@ class TopologyHomeView(PermissionRequiredMixin, View):
show_circuit=show_circuit, show_circuit=show_circuit,
show_power=show_power, show_power=show_power,
show_wireless=show_wireless, show_wireless=show_wireless,
group_id=group_id,
) )
else: else: