mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-25 01:48:38 -06:00
Replace CSS-based cable trace diagrams with SVG images
This commit is contained in:
parent
ce7fa95546
commit
9f615cde79
@ -2,18 +2,15 @@ import socket
|
|||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.contenttypes.models import ContentType
|
|
||||||
from django.db.models import F
|
|
||||||
from django.http import HttpResponseForbidden, HttpResponse
|
from django.http import HttpResponseForbidden, HttpResponse
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from drf_yasg import openapi
|
from drf_yasg import openapi
|
||||||
from drf_yasg.openapi import Parameter
|
from drf_yasg.openapi import Parameter
|
||||||
from drf_yasg.utils import swagger_auto_schema
|
from drf_yasg.utils import swagger_auto_schema
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
from rest_framework.mixins import ListModelMixin
|
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.routers import APIRootView
|
from rest_framework.routers import APIRootView
|
||||||
from rest_framework.viewsets import GenericViewSet, ViewSet
|
from rest_framework.viewsets import ViewSet
|
||||||
|
|
||||||
from circuits.models import Circuit
|
from circuits.models import Circuit
|
||||||
from dcim import filtersets
|
from dcim import filtersets
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
import logging
|
import logging
|
||||||
from copy import deepcopy
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
|
||||||
from django.core.paginator import EmptyPage, PageNotAnInteger
|
from django.core.paginator import EmptyPage, PageNotAnInteger
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models import F, Prefetch
|
from django.db.models import F, Prefetch
|
||||||
from django.forms import ModelMultipleChoiceField, MultipleHiddenInput, modelformset_factory
|
from django.forms import ModelMultipleChoiceField, MultipleHiddenInput, modelformset_factory
|
||||||
from django.shortcuts import get_object_or_404, redirect, render
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
|
from django.urls import reverse
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
@ -23,7 +22,7 @@ from utilities.forms import ConfirmationForm
|
|||||||
from utilities.paginator import EnhancedPaginator, get_paginate_count
|
from utilities.paginator import EnhancedPaginator, get_paginate_count
|
||||||
from utilities.permissions import get_permission_for_model
|
from utilities.permissions import get_permission_for_model
|
||||||
from utilities.tables import paginate_table
|
from utilities.tables import paginate_table
|
||||||
from utilities.utils import csv_format, count_related
|
from utilities.utils import count_related
|
||||||
from utilities.views import GetReturnURLMixin, ObjectPermissionRequiredMixin
|
from utilities.views import GetReturnURLMixin, ObjectPermissionRequiredMixin
|
||||||
from virtualization.models import VirtualMachine
|
from virtualization.models import VirtualMachine
|
||||||
from . import filtersets, forms, tables
|
from . import filtersets, forms, tables
|
||||||
@ -2423,11 +2422,16 @@ class PathTraceView(generic.ObjectView):
|
|||||||
# Get the total length of the cable and whether the length is definitive (fully defined)
|
# Get the total length of the cable and whether the length is definitive (fully defined)
|
||||||
total_length, is_definitive = path.get_total_length() if path else (None, False)
|
total_length, is_definitive = path.get_total_length() if path else (None, False)
|
||||||
|
|
||||||
|
# Determine the path to the SVG trace image
|
||||||
|
api_viewname = f"{path.origin._meta.app_label}-api:{path.origin._meta.model_name}-trace"
|
||||||
|
svg_url = f"{reverse(api_viewname, kwargs={'pk': path.origin.pk})}?render=svg"
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'path': path,
|
'path': path,
|
||||||
'related_paths': related_paths,
|
'related_paths': related_paths,
|
||||||
'total_length': total_length,
|
'total_length': total_length,
|
||||||
'is_definitive': is_definitive
|
'is_definitive': is_definitive,
|
||||||
|
'svg_url': svg_url,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -720,47 +720,6 @@ table tbody {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cable Tracing
|
|
||||||
.cable-trace {
|
|
||||||
max-width: 38rem;
|
|
||||||
margin: 1rem auto;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.cable-trace .node {
|
|
||||||
background-color: var(--nbx-cable-node-bg);
|
|
||||||
border: $border-width solid var(--nbx-cable-node-border-color);
|
|
||||||
border-radius: $border-radius;
|
|
||||||
padding: 1.5rem 1rem;
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
.cable-trace .termination {
|
|
||||||
background-color: var(--nbx-cable-termination-bg);
|
|
||||||
border: $border-width solid var(--nbx-cable-termination-border-color);
|
|
||||||
box-shadow: $box-shadow;
|
|
||||||
border-radius: $border-radius;
|
|
||||||
margin: -1rem auto;
|
|
||||||
padding: 0.5rem;
|
|
||||||
position: relative;
|
|
||||||
width: 60%;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
.cable-trace .active {
|
|
||||||
border: 0.25rem solid $success;
|
|
||||||
}
|
|
||||||
.cable-trace .cable {
|
|
||||||
border-left-style: solid;
|
|
||||||
border-left-width: 0.25rem;
|
|
||||||
margin: 1rem 0 1rem 50%;
|
|
||||||
padding: 1.5rem;
|
|
||||||
text-align: left;
|
|
||||||
width: 50%;
|
|
||||||
}
|
|
||||||
.cable-trace .trace-end {
|
|
||||||
margin-top: 2rem;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre.change-data {
|
pre.change-data {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
|
@ -1,89 +1,50 @@
|
|||||||
{% extends 'base/layout.html' %}
|
{% extends 'base/layout.html' %}
|
||||||
{% load helpers %}
|
{% load helpers %}
|
||||||
|
|
||||||
{% block header %}
|
{% block title %}Cable Trace for {{ object|meta:"verbose_name"|bettertitle }} {{ object }}{% endblock %}
|
||||||
<h1>{% block title %}Cable Trace for {{ object|meta:"verbose_name"|bettertitle }} {{ object }}{% endblock %}</h1>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col col-md-5">
|
<div class="col col-md-5">
|
||||||
|
<object data="{{ svg_url }}" class="rack_elevation"></object>
|
||||||
|
<div class="text-center mt-3">
|
||||||
|
<a class="btn btn-outline-primary btn-sm" href="{{ svg_url }}">
|
||||||
|
<i class="mdi mdi-file-download"></i> Download SVG
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
<div class="cable-trace">
|
<div class="cable-trace">
|
||||||
{% with traced_path=path.origin.trace %}
|
{% with traced_path=path.origin.trace %}
|
||||||
{% for near_end, cable, far_end in traced_path %}
|
{% if path.is_split %}
|
||||||
|
<div class="trace-end">
|
||||||
{# Near end #}
|
<h3 class="text-danger">Path split!</h3>
|
||||||
{% if near_end.device %}
|
<p>Select a node below to continue:</p>
|
||||||
{% include 'dcim/trace/device.html' with device=near_end.device %}
|
<ul class="text-start">
|
||||||
{% include 'dcim/trace/termination.html' with termination=near_end %}
|
{% for next_node in path.get_split_nodes %}
|
||||||
{% elif near_end.power_panel %}
|
{% if next_node.cable %}
|
||||||
{% include 'dcim/trace/powerpanel.html' with powerpanel=near_end.power_panel %}
|
<li>
|
||||||
{% include 'dcim/trace/termination.html' with termination=far_end%}
|
<a href="{% url 'dcim:frontport_trace' pk=next_node.pk %}">{{ next_node }}</a>
|
||||||
{% elif near_end.circuit %}
|
(Cable <a href="{{ next_node.cable.get_absolute_url }}">{{ next_node.cable }}</a>)
|
||||||
{% include 'dcim/trace/circuit.html' with circuit=near_end.circuit %}
|
</li>
|
||||||
{% include 'dcim/trace/termination.html' with termination=near_end %}
|
{% else %}
|
||||||
{% endif %}
|
<li class="text-muted">{{ next_node }}</li>
|
||||||
|
{% endif %}
|
||||||
{# Cable #}
|
{% endfor %}
|
||||||
{% if cable %}
|
</ul>
|
||||||
{% include 'dcim/trace/cable.html' %}
|
</div>
|
||||||
{% elif far_end %}
|
{% else %}
|
||||||
{% include 'dcim/trace/attachment.html' %}
|
<div class="trace-end">
|
||||||
{% endif %}
|
<h3 class="text-success">Trace Completed</h3>
|
||||||
|
<h5>Total Segments: {{ traced_path|length }}</h5>
|
||||||
{# Far end #}
|
<h5>Total Length:
|
||||||
{% if far_end.device %}
|
{% if total_length %}
|
||||||
{% include 'dcim/trace/termination.html' with termination=far_end %}
|
{{ total_length|floatformat:"-2" }}{% if not is_definitive %}+{% endif %} Meters /
|
||||||
{% if forloop.last %}
|
{{ total_length|meters_to_feet|floatformat:"-2" }} Feet
|
||||||
{% include 'dcim/trace/device.html' with device=far_end.device %}
|
{% else %}
|
||||||
{% endif %}
|
<span class="text-muted">N/A</span>
|
||||||
{% elif far_end.power_panel %}
|
{% endif %}
|
||||||
{% include 'dcim/trace/termination.html' with termination=far_end %}
|
</h5>
|
||||||
{% include 'dcim/trace/powerpanel.html' with powerpanel=far_end.power_panel %}
|
</div>
|
||||||
{% elif far_end.circuit %}
|
{% endif %}
|
||||||
{% include 'dcim/trace/termination.html' with termination=far_end %}
|
|
||||||
{% if forloop.last %}
|
|
||||||
{% include 'dcim/trace/circuit.html' with circuit=far_end.circuit %}
|
|
||||||
{% endif %}
|
|
||||||
{% elif far_end %}
|
|
||||||
{% include 'dcim/trace/object.html' with object=far_end %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if forloop.last %}
|
|
||||||
{% if path.is_split %}
|
|
||||||
<div class="trace-end">
|
|
||||||
<h3 class="text-danger">Path split!</h3>
|
|
||||||
<p>Select a node below to continue:</p>
|
|
||||||
<ul class="text-start">
|
|
||||||
{% for next_node in path.get_split_nodes %}
|
|
||||||
{% if next_node.cable %}
|
|
||||||
<li>
|
|
||||||
<a href="{% url 'dcim:frontport_trace' pk=next_node.pk %}">{{ next_node }}</a>
|
|
||||||
(Cable <a href="{{ next_node.cable.get_absolute_url }}">{{ next_node.cable }}</a>)
|
|
||||||
</li>
|
|
||||||
{% else %}
|
|
||||||
<li class="text-muted">{{ next_node }}</li>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="trace-end">
|
|
||||||
<h3{% if far_end %} class="text-success"{% endif %}>Trace Completed</h3>
|
|
||||||
<h5>Total Segments: {{ traced_path|length }}</h5>
|
|
||||||
<h5>Total Length:
|
|
||||||
{% if total_length %}
|
|
||||||
{{ total_length|floatformat:"-2" }}{% if not is_definitive %}+{% endif %} Meters /
|
|
||||||
{{ total_length|meters_to_feet|floatformat:"-2" }} Feet
|
|
||||||
{% else %}
|
|
||||||
<span class="text-muted">N/A</span>
|
|
||||||
{% endif %}
|
|
||||||
</h5>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endfor %}
|
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user