mirror of
https://github.com/netbox-community/netbox.git
synced 2025-07-23 04:22:01 -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 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.shortcuts import get_object_or_404
|
||||
from drf_yasg import openapi
|
||||
from drf_yasg.openapi import Parameter
|
||||
from drf_yasg.utils import swagger_auto_schema
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.mixins import ListModelMixin
|
||||
from rest_framework.response import Response
|
||||
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 dcim import filtersets
|
||||
|
@ -1,15 +1,14 @@
|
||||
import logging
|
||||
from copy import deepcopy
|
||||
from collections import OrderedDict
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.core.paginator import EmptyPage, PageNotAnInteger
|
||||
from django.db import transaction
|
||||
from django.db.models import F, Prefetch
|
||||
from django.forms import ModelMultipleChoiceField, MultipleHiddenInput, modelformset_factory
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.urls import reverse
|
||||
from django.utils.html import escape
|
||||
from django.utils.safestring import mark_safe
|
||||
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.permissions import get_permission_for_model
|
||||
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 virtualization.models import VirtualMachine
|
||||
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)
|
||||
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 {
|
||||
'path': path,
|
||||
'related_paths': related_paths,
|
||||
'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 {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
|
@ -1,89 +1,50 @@
|
||||
{% extends 'base/layout.html' %}
|
||||
{% load helpers %}
|
||||
|
||||
{% block header %}
|
||||
<h1>{% block title %}Cable Trace for {{ object|meta:"verbose_name"|bettertitle }} {{ object }}{% endblock %}</h1>
|
||||
{% endblock %}
|
||||
{% block title %}Cable Trace for {{ object|meta:"verbose_name"|bettertitle }} {{ object }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<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">
|
||||
{% with traced_path=path.origin.trace %}
|
||||
{% for near_end, cable, far_end in traced_path %}
|
||||
|
||||
{# Near end #}
|
||||
{% if near_end.device %}
|
||||
{% include 'dcim/trace/device.html' with device=near_end.device %}
|
||||
{% include 'dcim/trace/termination.html' with termination=near_end %}
|
||||
{% elif near_end.power_panel %}
|
||||
{% include 'dcim/trace/powerpanel.html' with powerpanel=near_end.power_panel %}
|
||||
{% include 'dcim/trace/termination.html' with termination=far_end%}
|
||||
{% elif near_end.circuit %}
|
||||
{% include 'dcim/trace/circuit.html' with circuit=near_end.circuit %}
|
||||
{% include 'dcim/trace/termination.html' with termination=near_end %}
|
||||
{% endif %}
|
||||
|
||||
{# Cable #}
|
||||
{% if cable %}
|
||||
{% include 'dcim/trace/cable.html' %}
|
||||
{% elif far_end %}
|
||||
{% include 'dcim/trace/attachment.html' %}
|
||||
{% endif %}
|
||||
|
||||
{# Far end #}
|
||||
{% if far_end.device %}
|
||||
{% include 'dcim/trace/termination.html' with termination=far_end %}
|
||||
{% if forloop.last %}
|
||||
{% include 'dcim/trace/device.html' with device=far_end.device %}
|
||||
{% endif %}
|
||||
{% elif far_end.power_panel %}
|
||||
{% include 'dcim/trace/termination.html' with termination=far_end %}
|
||||
{% include 'dcim/trace/powerpanel.html' with powerpanel=far_end.power_panel %}
|
||||
{% elif far_end.circuit %}
|
||||
{% 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 %}
|
||||
{% 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 class="text-success">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 %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user