@@ -50,4 +50,4 @@ class PowerFeedCoordinateSerializer(NetBoxModelSerializer):
|
|||||||
class IndividualOptionsSerializer(NetBoxModelSerializer):
|
class IndividualOptionsSerializer(NetBoxModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = IndividualOptions
|
model = IndividualOptions
|
||||||
fields = ("ignore_cable_type", "save_coords", "show_unconnected", "show_cables", "show_logical_connections", "show_single_cable_logical_conns", "show_neighbors", "show_circuit", "show_power", "show_wireless", "draw_default_layout")
|
fields = ("ignore_cable_type", "save_coords", "show_unconnected", "show_cables", "show_logical_connections", "show_single_cable_logical_conns", "show_neighbors", "show_circuit", "show_power", "show_wireless", "group_sites", "group_locations", "group_racks", "draw_default_layout")
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ class DeviceFilterForm(
|
|||||||
(None, ('q', 'filter_id', 'tag')),
|
(None, ('q', 'filter_id', 'tag')),
|
||||||
(_('Options'), (
|
(_('Options'), (
|
||||||
'group', 'save_coords', 'show_unconnected', 'show_cables', 'show_logical_connections',
|
'group', 'save_coords', 'show_unconnected', 'show_cables', 'show_logical_connections',
|
||||||
'show_single_cable_logical_conns', 'show_neighbors', 'show_circuit', 'show_power', 'show_wireless',
|
'show_single_cable_logical_conns', 'show_neighbors', 'show_circuit', 'show_power', 'show_wireless',
|
||||||
|
'group_sites', 'group_locations', 'group_racks'
|
||||||
)),
|
)),
|
||||||
(_('Device'), ('id',)),
|
(_('Device'), ('id',)),
|
||||||
(_('Location'), ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id')),
|
(_('Location'), ('region_id', 'site_group_id', 'site_id', 'location_id', 'rack_id')),
|
||||||
@@ -258,6 +259,15 @@ class DeviceFilterForm(
|
|||||||
show_wireless = forms.BooleanField(
|
show_wireless = forms.BooleanField(
|
||||||
label =_('Show Wireless Links'), required=False, initial=False
|
label =_('Show Wireless Links'), required=False, initial=False
|
||||||
)
|
)
|
||||||
|
group_sites = forms.BooleanField(
|
||||||
|
label =_('Group Sites'), required=False, initial=False
|
||||||
|
)
|
||||||
|
group_locations = forms.BooleanField(
|
||||||
|
label =_('Group Locations'), required=False, initial=False
|
||||||
|
)
|
||||||
|
group_racks = forms.BooleanField(
|
||||||
|
label =_('Group Racks'), required=False, initial=False
|
||||||
|
)
|
||||||
|
|
||||||
class CoordinateGroupsForm(NetBoxModelForm):
|
class CoordinateGroupsForm(NetBoxModelForm):
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
@@ -447,6 +457,9 @@ class IndividualOptionsForm(NetBoxModelForm):
|
|||||||
'show_circuit',
|
'show_circuit',
|
||||||
'show_power',
|
'show_power',
|
||||||
'show_wireless',
|
'show_wireless',
|
||||||
|
'group_sites',
|
||||||
|
'group_locations',
|
||||||
|
'group_racks',
|
||||||
'draw_default_layout',
|
'draw_default_layout',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -548,6 +561,27 @@ class IndividualOptionsForm(NetBoxModelForm):
|
|||||||
help_text=_('Displays wireless connections. These connections are '
|
help_text=_('Displays wireless connections. These connections are '
|
||||||
'displayed as blue dotted lines.')
|
'displayed as blue dotted lines.')
|
||||||
)
|
)
|
||||||
|
group_sites = forms.BooleanField(
|
||||||
|
label =_('Group Sites'),
|
||||||
|
required=False,
|
||||||
|
initial=False,
|
||||||
|
help_text=_('Draws a rectangle around Devices that belong to the '
|
||||||
|
'same site.')
|
||||||
|
)
|
||||||
|
group_locations = forms.BooleanField(
|
||||||
|
label =_('Group Locations'),
|
||||||
|
required=False,
|
||||||
|
initial=False,
|
||||||
|
help_text=_('Draws a rectangle around Devices that belong to the '
|
||||||
|
'same location.')
|
||||||
|
)
|
||||||
|
group_racks = forms.BooleanField(
|
||||||
|
label =_('Group Racks'),
|
||||||
|
required=False,
|
||||||
|
initial=False,
|
||||||
|
help_text=_('Draws a rectangle around Devices that belong to the '
|
||||||
|
'same rack.')
|
||||||
|
)
|
||||||
draw_default_layout = forms.BooleanField(
|
draw_default_layout = forms.BooleanField(
|
||||||
label = ('Draw Default Layout'),
|
label = ('Draw Default Layout'),
|
||||||
required=False,
|
required=False,
|
||||||
@@ -559,5 +593,8 @@ class IndividualOptionsForm(NetBoxModelForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = IndividualOptions
|
model = IndividualOptions
|
||||||
fields = [
|
fields = [
|
||||||
'user_id', 'ignore_cable_type', 'preselected_device_roles', 'preselected_tags', 'save_coords', 'show_unconnected', 'show_cables', 'show_logical_connections', 'show_single_cable_logical_conns', 'show_neighbors', 'show_circuit', 'show_power', 'show_wireless', 'draw_default_layout'
|
'user_id', 'ignore_cable_type', 'preselected_device_roles', 'preselected_tags',
|
||||||
|
'save_coords', 'show_unconnected', 'show_cables', 'show_logical_connections',
|
||||||
|
'show_single_cable_logical_conns', 'show_neighbors', 'show_circuit', 'show_power',
|
||||||
|
'show_wireless', 'group_sites', 'group_locations', 'group_racks', 'draw_default_layout'
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Generated by Django 4.2.10 on 2024-03-02 16:26
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('netbox_topology_views', '0006_powerpanelcoordinate_powerfeedcoordinate_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='individualoptions',
|
||||||
|
name='group_locations',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='individualoptions',
|
||||||
|
name='group_racks',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='individualoptions',
|
||||||
|
name='group_sites',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -374,6 +374,15 @@ class IndividualOptions(NetBoxModel):
|
|||||||
show_wireless = models.BooleanField(
|
show_wireless = models.BooleanField(
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
|
group_sites = models.BooleanField(
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
group_locations = models.BooleanField(
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
group_racks = models.BooleanField(
|
||||||
|
default=False
|
||||||
|
)
|
||||||
draw_default_layout = models.BooleanField(
|
draw_default_layout = models.BooleanField(
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -42,6 +42,11 @@ const csrftoken = getCookie('csrftoken')
|
|||||||
// Render vis graph
|
// Render vis graph
|
||||||
let graph = null // vis graph instance
|
let graph = null // vis graph instance
|
||||||
|
|
||||||
|
let searchParams = new URLSearchParams(window.location.search)
|
||||||
|
let group_sites = searchParams.get('group_sites')
|
||||||
|
let group_locations = searchParams.get('group_locations')
|
||||||
|
let group_racks = searchParams.get('group_racks')
|
||||||
|
|
||||||
const container = document.querySelector('#visgraph')
|
const container = document.querySelector('#visgraph')
|
||||||
const coordSaveCheckbox = document.querySelector('#id_save_coords')
|
const coordSaveCheckbox = document.querySelector('#id_save_coords')
|
||||||
;(function handleLoadData() {
|
;(function handleLoadData() {
|
||||||
@@ -133,6 +138,189 @@ const coordSaveCheckbox = document.querySelector('#id_save_coords')
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
graph.on('afterDrawing', (canvascontext) => {
|
||||||
|
allRectangles = [];
|
||||||
|
if(group_sites != null && group_sites == 'on') { drawGroupRectangles(canvascontext, groupedNodeSites, siteRectParams); }
|
||||||
|
if(group_locations != null && group_locations == 'on') { drawGroupRectangles(canvascontext, groupedNodeLocations, locationRectParams); }
|
||||||
|
if(group_racks != null && group_racks == 'on') { drawGroupRectangles(canvascontext, groupedNodeRacks, rackRectParams); }
|
||||||
|
})
|
||||||
|
|
||||||
|
graph.on('click', (canvascontext) => {
|
||||||
|
allRectangles.forEach(key => {
|
||||||
|
// Is the mouse pointer inside of the current rectangle?
|
||||||
|
if(canvascontext.pointer.canvas.x > (key.x1 - key.border / 2 - 1) && canvascontext.pointer.canvas.x < (key.x2 + key.border / 2 + 1)
|
||||||
|
&& canvascontext.pointer.canvas.y > (key.y1 - key.border / 2 - 1) && canvascontext.pointer.canvas.y < (key.y2 + key.border / 2 + 1)) {
|
||||||
|
// We just want to react when the border has been clicked, not the whole rectangle
|
||||||
|
if (canvascontext.pointer.canvas.x < (key.x1 + key.border / 2 + 1) || canvascontext.pointer.canvas.x > (key.x2 - key.border / 2 - 1)
|
||||||
|
|| canvascontext.pointer.canvas.y < (key.y1 + key.border / 2 + 1) || canvascontext.pointer.canvas.y > (key.y2 - key.border / 2 - 1)) {
|
||||||
|
// Generate an array of affected nodes in order to pass it to the select.Nodes() function
|
||||||
|
let arr = [];
|
||||||
|
if(key.category == "Site") {
|
||||||
|
groupedNodeSites.forEach(subArray => {
|
||||||
|
subArray.forEach(element => {
|
||||||
|
if (element[1] == key.id) {
|
||||||
|
arr.push(element[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(key.category == "Location") {
|
||||||
|
groupedNodeLocations.forEach(subArray => {
|
||||||
|
subArray.forEach(element => {
|
||||||
|
if (element[1] === key.id) {
|
||||||
|
arr.push(element[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(key.category == "Rack") {
|
||||||
|
groupedNodeRacks.forEach(subArray => {
|
||||||
|
subArray.forEach(element => {
|
||||||
|
if (element[1] === key.id) {
|
||||||
|
arr.push(element[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
graph.selectNodes(arr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add information on which node belongs to which group (site/location/rack).
|
||||||
|
// Create an array for each group in order to loop through that arrays later
|
||||||
|
function combineNodeInfo(typeId, type) {
|
||||||
|
let nodesArray = [];
|
||||||
|
// Extract node ids and node type ids from all nodes
|
||||||
|
for (let [key, value] of nodes._data) {
|
||||||
|
if (value[typeId] != undefined) {
|
||||||
|
nodesArray.push([value.id, value[typeId], value[type]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Split single array above into arrays grouped by node id
|
||||||
|
let groupedNodeArray = nodesArray.reduce((acc, value) => {
|
||||||
|
let key = value[1]; // node id
|
||||||
|
acc[key] = acc[key] || [];
|
||||||
|
acc[key].push(value);
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return Object.values(groupedNodeArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
var allRectangles = [];
|
||||||
|
/* Draw a single rectangle with given parameters
|
||||||
|
rectangle expects an object that consists of the following keys:
|
||||||
|
ctx: canvas context on which the rectangle should be drawn
|
||||||
|
x: x-coordinate of top left point of the rectangle
|
||||||
|
y: y-coordinate of top left point of the rectangle
|
||||||
|
width: width of rectangle
|
||||||
|
height: height of rectangle
|
||||||
|
lineWidth: border width
|
||||||
|
color: border color
|
||||||
|
text: a string to be placed where you want it to be
|
||||||
|
textPaddingX: x-position of the text
|
||||||
|
textPaddingY: y-position of the text
|
||||||
|
font: text font */
|
||||||
|
function drawGroupRectangle(rectangle) {
|
||||||
|
// Draw rectangle
|
||||||
|
rectangle.ctx.beginPath();
|
||||||
|
rectangle.ctx.lineWidth = rectangle.lineWidth;
|
||||||
|
rectangle.ctx.strokeStyle = rectangle.color;
|
||||||
|
rectangle.ctx.rect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
|
||||||
|
rectangle.ctx.stroke();
|
||||||
|
// Draw text
|
||||||
|
rectangle.ctx.font = rectangle.font;
|
||||||
|
rectangle.ctx.fillStyle = rectangle.color;
|
||||||
|
rectangle.ctx.fillText(rectangle.text, rectangle.x + rectangle.textPaddingX, rectangle.y + rectangle.textPaddingY);
|
||||||
|
|
||||||
|
allRectangles.push({category: rectangle.category, id: rectangle.id, x1: rectangle.x, y1: rectangle.y, x2: rectangle.x + rectangle.width, y2: rectangle.y + rectangle.height, border: rectangle.lineWidth})
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Draw all rectangles of a given group (site/location/rack)
|
||||||
|
rectParams expects an object that consists of the following keys:
|
||||||
|
lineWidth: border width (string)
|
||||||
|
color: border color (string)
|
||||||
|
paddingX: rectangle x-padding, calculated from the center of a node (int)
|
||||||
|
paddingY: rectangle y-padding, calculated from the center of a node (int)
|
||||||
|
textPaddingX: text x-padding, calculated from the lower left point of the text (int)
|
||||||
|
textPaddingY: text y-padding, calculated from the lower left point of the text (int)
|
||||||
|
font: css-like font size and font (string) */
|
||||||
|
function drawGroupRectangles(canvascontext, groupedNodes, rectParams) {
|
||||||
|
for(let value of Object.entries(groupedNodes)) {
|
||||||
|
const rectangles = [];
|
||||||
|
const xValues = [];
|
||||||
|
const yValues = [];
|
||||||
|
|
||||||
|
for(let val of value[1]) {
|
||||||
|
xValues.push(graph.getPosition(val[0]).x);
|
||||||
|
yValues.push(graph.getPosition(val[0]).y);
|
||||||
|
}
|
||||||
|
|
||||||
|
const rectX = Math.min(...xValues) - rectParams.paddingX;
|
||||||
|
const rectY = Math.min(...yValues) - rectParams.paddingY;
|
||||||
|
const rectSizeX = Math.max(...xValues) - Math.min(...xValues) + 2*rectParams.paddingX;
|
||||||
|
const rectSizeY = Math.max(...yValues) - Math.min(...yValues) + 2*rectParams.paddingY;
|
||||||
|
|
||||||
|
rectangles.push({
|
||||||
|
ctx: canvascontext,
|
||||||
|
x: rectX,
|
||||||
|
y: rectY,
|
||||||
|
width: rectSizeX,
|
||||||
|
height: rectSizeY,
|
||||||
|
lineWidth: rectParams.lineWidth,
|
||||||
|
color: rectParams.color,
|
||||||
|
text: value[1][0][2],
|
||||||
|
textPaddingX: rectParams.textPaddingX,
|
||||||
|
textPaddingY: rectParams.textPaddingY,
|
||||||
|
font: rectParams.font,
|
||||||
|
id: value[1][0][1],
|
||||||
|
category: rectParams.category
|
||||||
|
});
|
||||||
|
|
||||||
|
rectangles.forEach(function(rectangle) {
|
||||||
|
drawGroupRectangle(rectangle);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let groupedNodeSites = combineNodeInfo('site_id', 'site');
|
||||||
|
let siteRectParams = {
|
||||||
|
lineWidth: "5",
|
||||||
|
color: "red",
|
||||||
|
paddingX: 84,
|
||||||
|
paddingY: 84,
|
||||||
|
textPaddingX: 8,
|
||||||
|
textPaddingY: -8,
|
||||||
|
font: "14px helvetica",
|
||||||
|
category: "Site"
|
||||||
|
}
|
||||||
|
|
||||||
|
let groupedNodeLocations = combineNodeInfo('location_id', 'location');
|
||||||
|
let locationRectParams = {
|
||||||
|
lineWidth: "5",
|
||||||
|
color: "yellow",
|
||||||
|
paddingX: 77,
|
||||||
|
paddingY: 77,
|
||||||
|
textPaddingX: 12,
|
||||||
|
textPaddingY: 22,
|
||||||
|
font: "14px helvetica",
|
||||||
|
category: "Location"
|
||||||
|
}
|
||||||
|
|
||||||
|
let groupedNodeRacks = combineNodeInfo('rack_id', 'rack');
|
||||||
|
let rackRectParams = {
|
||||||
|
lineWidth: "5",
|
||||||
|
color: "green",
|
||||||
|
paddingX: 70,
|
||||||
|
paddingY: 70,
|
||||||
|
textPaddingX: 8,
|
||||||
|
textPaddingY: 30,
|
||||||
|
font: "14px helvetica",
|
||||||
|
category: "Rack"
|
||||||
|
}
|
||||||
})()
|
})()
|
||||||
|
|
||||||
// Download Graph
|
// Download Graph
|
||||||
|
|||||||
@@ -154,12 +154,27 @@ def get_query_settings(request):
|
|||||||
if request.GET["show_wireless"] == "on" :
|
if request.GET["show_wireless"] == "on" :
|
||||||
show_wireless = True
|
show_wireless = True
|
||||||
|
|
||||||
|
group_sites = False
|
||||||
|
if "group_sites" in request.GET:
|
||||||
|
if request.GET["group_sites"] == "on" :
|
||||||
|
group_sites = True
|
||||||
|
|
||||||
|
group_locations = False
|
||||||
|
if "group_locations" in request.GET:
|
||||||
|
if request.GET["group_locations"] == "on" :
|
||||||
|
group_locations = True
|
||||||
|
|
||||||
|
group_racks = False
|
||||||
|
if "group_racks" in request.GET:
|
||||||
|
if request.GET["group_racks"] == "on" :
|
||||||
|
group_racks = True
|
||||||
|
|
||||||
show_neighbors = False
|
show_neighbors = False
|
||||||
if "show_neighbors" in request.GET:
|
if "show_neighbors" in request.GET:
|
||||||
if request.GET["show_neighbors"] == "on" :
|
if request.GET["show_neighbors"] == "on" :
|
||||||
show_neighbors = True
|
show_neighbors = True
|
||||||
|
|
||||||
return save_coords, show_unconnected, show_power, show_circuit, show_logical_connections, show_single_cable_logical_conns, show_cables, show_wireless, show_neighbors
|
return save_coords, show_unconnected, show_power, show_circuit, show_logical_connections, show_single_cable_logical_conns, show_cables, show_wireless, group_sites, group_locations, group_racks, show_neighbors
|
||||||
|
|
||||||
class LinePattern():
|
class LinePattern():
|
||||||
wireless = [2, 10, 2, 10]
|
wireless = [2, 10, 2, 10]
|
||||||
|
|||||||
@@ -220,6 +220,15 @@ def create_node(
|
|||||||
node["shape"] = "image"
|
node["shape"] = "image"
|
||||||
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 device.site is not None:
|
||||||
|
node["site"] = device.site.name
|
||||||
|
node["site_id"] = device.site_id
|
||||||
|
if device.location is not None:
|
||||||
|
node["location"] = device.location.name
|
||||||
|
node["location_id"] = device.location_id
|
||||||
|
if device.rack is not None:
|
||||||
|
node["rack"] = device.rack.name
|
||||||
|
node["rack_id"] = device.rack_id
|
||||||
|
|
||||||
return node
|
return node
|
||||||
|
|
||||||
@@ -325,6 +334,9 @@ def get_topology_data(
|
|||||||
show_neighbors: bool,
|
show_neighbors: bool,
|
||||||
show_power: bool,
|
show_power: bool,
|
||||||
show_wireless: bool,
|
show_wireless: bool,
|
||||||
|
group_sites: bool,
|
||||||
|
group_locations: bool,
|
||||||
|
group_racks: bool,
|
||||||
group_id,
|
group_id,
|
||||||
):
|
):
|
||||||
|
|
||||||
@@ -688,7 +700,7 @@ class TopologyHomeView(PermissionRequiredMixin, View):
|
|||||||
|
|
||||||
if request.GET:
|
if request.GET:
|
||||||
|
|
||||||
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, group_sites, group_locations, group_racks, show_neighbors = get_query_settings(request)
|
||||||
|
|
||||||
if "group" not in request.GET:
|
if "group" not in request.GET:
|
||||||
group_id = "default"
|
group_id = "default"
|
||||||
@@ -708,6 +720,9 @@ 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_sites=group_sites,
|
||||||
|
group_locations=group_locations,
|
||||||
|
group_racks=group_racks,
|
||||||
group_id=group_id,
|
group_id=group_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -729,6 +744,9 @@ class TopologyHomeView(PermissionRequiredMixin, View):
|
|||||||
if individualOptions.show_circuit: q['show_circuit'] = "on"
|
if individualOptions.show_circuit: q['show_circuit'] = "on"
|
||||||
if individualOptions.show_power: q['show_power'] = "on"
|
if individualOptions.show_power: q['show_power'] = "on"
|
||||||
if individualOptions.show_wireless: q['show_wireless'] = "on"
|
if individualOptions.show_wireless: q['show_wireless'] = "on"
|
||||||
|
if individualOptions.group_sites: q['group_sites'] = "on"
|
||||||
|
if individualOptions.group_locations: q['group_locations'] = "on"
|
||||||
|
if individualOptions.group_racks: q['group_racks'] = "on"
|
||||||
if individualOptions.draw_default_layout:
|
if individualOptions.draw_default_layout:
|
||||||
q['draw_init'] = "true"
|
q['draw_init'] = "true"
|
||||||
else:
|
else:
|
||||||
@@ -1083,6 +1101,9 @@ class TopologyIndividualOptionsView(PermissionRequiredMixin, View):
|
|||||||
'show_circuit': queryset.show_circuit,
|
'show_circuit': queryset.show_circuit,
|
||||||
'show_power': queryset.show_power,
|
'show_power': queryset.show_power,
|
||||||
'show_wireless': queryset.show_wireless,
|
'show_wireless': queryset.show_wireless,
|
||||||
|
'group_sites': queryset.group_sites,
|
||||||
|
'group_locations': queryset.group_locations,
|
||||||
|
'group_racks': queryset.group_racks,
|
||||||
'draw_default_layout': queryset.draw_default_layout,
|
'draw_default_layout': queryset.draw_default_layout,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user