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
+11 -4
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,6 +51,7 @@ 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)
if group_id is None or group_id == "default":
# Storing coordinates in custom field is deprecated now. # Storing coordinates in custom field is deprecated now.
# We preserve this for backwards compatibility. # We preserve this for backwards compatibility.
try: try:
@@ -63,13 +65,14 @@ class SaveCoordsViewSet(ReadOnlyModelViewSet):
{"status": "coords custom field could not be saved"}, status=500 {"status": "coords custom field could not be saved"}, status=500
) )
# Default group named "default" must always exist in order to make sure # Default group named "default" must exist in order to make sure that
# that coordinate values can be stored even if no coordinate group has been # 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. # selected. The default group will be added automatically if it does not exist.
try: try:
if CoordinateGroup.objects.filter(name="default"):
group = CoordinateGroup.objects.get(name="default") group = CoordinateGroup.objects.get(name="default")
except CoordinateGroup.DoesNotExist: group_id = group.pk
try: else:
group = CoordinateGroup( group = CoordinateGroup(
name="default", name="default",
description="Automatically generated default group. If you delete " description="Automatically generated default group. If you delete "
@@ -77,12 +80,15 @@ class SaveCoordsViewSet(ReadOnlyModelViewSet):
"the group itself will be re-created." "the group itself will be re-created."
) )
group.save() 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:
if CoordinateGroup.objects.filter(pk=group_id):
group = CoordinateGroup.objects.get(pk=group_id)
# Hen-and-egg-problem. Thanks, Django! By default, Django updates records that # Hen-and-egg-problem. Thanks, Django! By default, Django updates records that
# already exist and inserts otherwise. This does not work with our # already exist and inserts otherwise. This does not work with our
# unique_together key if no pk is given. But: No record, no pk. # unique_together key if no pk is given. But: No record, no pk.
@@ -97,6 +103,7 @@ class SaveCoordsViewSet(ReadOnlyModelViewSet):
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"
+21 -8
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,13 +156,15 @@ 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)
if group_id is None or group_id == "default":
# Default group named "default" must always exist in order to make sure # 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 # 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. # selected. The default group will be added automatically if it does not exist.
try: try:
if CoordinateGroup.objects.filter(name="default"):
group = CoordinateGroup.objects.get(name="default") group = CoordinateGroup.objects.get(name="default")
except CoordinateGroup.DoesNotExist: group_id = group.pk
try: else:
group = CoordinateGroup( group = CoordinateGroup(
name="default", name="default",
description="Automatically generated default group. If you delete " description="Automatically generated default group. If you delete "
@@ -170,9 +172,12 @@ def create_node(
"the group itself will be re-created." "the group itself will be re-created."
) )
group.save() 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'):
node["x"] = Coordinate.objects.get(group=group, device=device.pk).x node["x"] = Coordinate.objects.get(group=group, device=device.pk).x
@@ -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: