16547 Add distance to Circuit

This commit is contained in:
Arthur Hanson 2024-09-26 15:14:45 -07:00
parent 85396866bc
commit 79e51084bf
20 changed files with 185 additions and 123 deletions

View File

@ -0,0 +1,28 @@
# Generated by Django 5.0.9 on 2024-09-26 22:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('circuits', '0044_circuit_groups'),
]
operations = [
migrations.AddField(
model_name='circuit',
name='_abs_distance',
field=models.DecimalField(blank=True, decimal_places=4, max_digits=10, null=True),
),
migrations.AddField(
model_name='circuit',
name='distance',
field=models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True),
),
migrations.AddField(
model_name='circuit',
name='distance_unit',
field=models.CharField(blank=True, max_length=50),
),
]

View File

@ -6,6 +6,7 @@ from django.utils.translation import gettext_lazy as _
from circuits.choices import *
from dcim.models import CabledObjectModel
from netbox.models import ChangeLoggedModel, OrganizationalModel, PrimaryModel
from netbox.models.mixins import DistanceMixin
from netbox.models.features import ContactsMixin, CustomFieldsMixin, CustomLinksMixin, ExportTemplatesMixin, ImageAttachmentsMixin, TagsMixin
from utilities.fields import ColorField
@ -37,7 +38,7 @@ class CircuitType(OrganizationalModel):
verbose_name_plural = _('circuit types')
class Circuit(ContactsMixin, ImageAttachmentsMixin, PrimaryModel):
class Circuit(ContactsMixin, ImageAttachmentsMixin, DistanceMixin, PrimaryModel):
"""
A communications circuit connects two points. Each Circuit belongs to a Provider; Providers may have multiple
circuits. Each circuit is also assigned a CircuitType and a Site, and may optionally be assigned to a particular

View File

@ -7,6 +7,7 @@ from dcim.choices import *
from dcim.models import DeviceType, ModuleType
from netbox.api.fields import ChoiceField, RelatedObjectCountField
from netbox.api.serializers import NetBoxModelSerializer
from netbox.choices import *
from .manufacturers import ManufacturerSerializer
from .platforms import PlatformSerializer

View File

@ -6,6 +6,7 @@ from dcim.constants import *
from dcim.models import Rack, RackReservation, RackRole, RackType
from netbox.api.fields import ChoiceField, RelatedObjectCountField
from netbox.api.serializers import NetBoxModelSerializer
from netbox.choices import *
from netbox.config import ConfigItem
from tenancy.api.serializers_.tenants import TenantSerializer
from users.api.serializers_.users import UserSerializer

View File

@ -1546,24 +1546,6 @@ class CableLengthUnitChoices(ChoiceSet):
)
class WeightUnitChoices(ChoiceSet):
# Metric
UNIT_KILOGRAM = 'kg'
UNIT_GRAM = 'g'
# Imperial
UNIT_POUND = 'lb'
UNIT_OUNCE = 'oz'
CHOICES = (
(UNIT_KILOGRAM, _('Kilograms')),
(UNIT_GRAM, _('Grams')),
(UNIT_POUND, _('Pounds')),
(UNIT_OUNCE, _('Ounces')),
)
#
# CableTerminations
#

View File

@ -8,6 +8,7 @@ from dcim.constants import *
from dcim.models import *
from extras.models import ConfigTemplate
from ipam.models import ASN, VLAN, VLANGroup, VRF
from netbox.choices import *
from netbox.forms import NetBoxModelBulkEditForm
from tenancy.models import Tenant
from users.models import User

View File

@ -10,6 +10,7 @@ from dcim.constants import *
from dcim.models import *
from extras.models import ConfigTemplate
from ipam.models import VRF, IPAddress
from netbox.choices import *
from netbox.forms import NetBoxModelImportForm
from tenancy.models import Tenant
from utilities.forms.fields import (

View File

@ -7,6 +7,7 @@ from dcim.models import *
from extras.forms import LocalConfigContextFilterForm
from extras.models import ConfigTemplate
from ipam.models import ASN, VRF
from netbox.choices import *
from netbox.forms import NetBoxModelFilterSetForm
from tenancy.forms import ContactModelFilterForm, TenancyFilterForm
from users.models import User

View File

@ -21,11 +21,12 @@ from extras.querysets import ConfigContextModelQuerySet
from netbox.choices import ColorChoices
from netbox.config import ConfigItem
from netbox.models import OrganizationalModel, PrimaryModel
from netbox.models.mixins import WeightMixin
from netbox.models.features import ContactsMixin, ImageAttachmentsMixin
from utilities.fields import ColorField, CounterCacheField, NaturalOrderingField
from utilities.tracking import TrackingModelMixin
from .device_components import *
from .mixins import RenderConfigMixin, WeightMixin
from .mixins import RenderConfigMixin
__all__ = (

View File

@ -1,56 +1,12 @@
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import gettext_lazy as _
from dcim.choices import *
from utilities.conversion import to_grams
__all__ = (
'RenderConfigMixin',
'WeightMixin',
)
class WeightMixin(models.Model):
weight = models.DecimalField(
verbose_name=_('weight'),
max_digits=8,
decimal_places=2,
blank=True,
null=True
)
weight_unit = models.CharField(
verbose_name=_('weight unit'),
max_length=50,
choices=WeightUnitChoices,
blank=True,
)
# Stores the normalized weight (in grams) for database ordering
_abs_weight = models.PositiveBigIntegerField(
blank=True,
null=True
)
class Meta:
abstract = True
def save(self, *args, **kwargs):
# Store the given weight (if any) in grams for use in database ordering
if self.weight and self.weight_unit:
self._abs_weight = to_grams(self.weight, self.weight_unit)
else:
self._abs_weight = None
super().save(*args, **kwargs)
def clean(self):
super().clean()
# Validate weight and weight_unit
if self.weight and not self.weight_unit:
raise ValidationError(_("Must specify a unit when setting a weight"))
class RenderConfigMixin(models.Model):
config_template = models.ForeignKey(
to='extras.ConfigTemplate',

View File

@ -16,13 +16,13 @@ from dcim.constants import *
from dcim.svg import RackElevationSVG
from netbox.choices import ColorChoices
from netbox.models import OrganizationalModel, PrimaryModel
from netbox.models.mixins import WeightMixin
from netbox.models.features import ContactsMixin, ImageAttachmentsMixin
from utilities.conversion import to_grams
from utilities.data import array_to_string, drange
from utilities.fields import ColorField, NaturalOrderingField
from .device_components import PowerPort
from .devices import Device, Module
from .mixins import WeightMixin
from .power import PowerFeed
__all__ = (

View File

@ -7,8 +7,10 @@ __all__ = (
'ButtonColorChoices',
'ColorChoices',
'CSVDelimiterChoices',
'DistanceUnitChoices',
'ImportFormatChoices',
'ImportMethodChoices',
'WeightUnitChoices',
)
@ -157,3 +159,39 @@ class CSVDelimiterChoices(ChoiceSet):
(SEMICOLON, _('Semicolon')),
(TAB, _('Tab')),
]
class DistanceUnitChoices(ChoiceSet):
# Metric
UNIT_KILOMETER = 'km'
UNIT_METER = 'm'
# Imperial
UNIT_MILE = 'mi'
UNIT_FOOT = 'ft'
CHOICES = (
(UNIT_KILOMETER, _('Kilometers')),
(UNIT_METER, _('Meters')),
(UNIT_MILE, _('Miles')),
(UNIT_FOOT, _('Feet')),
)
class WeightUnitChoices(ChoiceSet):
# Metric
UNIT_KILOGRAM = 'kg'
UNIT_GRAM = 'g'
# Imperial
UNIT_POUND = 'lb'
UNIT_OUNCE = 'oz'
CHOICES = (
(UNIT_KILOGRAM, _('Kilograms')),
(UNIT_GRAM, _('Grams')),
(UNIT_POUND, _('Pounds')),
(UNIT_OUNCE, _('Ounces')),
)

View File

@ -0,0 +1,97 @@
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import gettext_lazy as _
from netbox.choices import *
from utilities.conversion import to_grams
__all__ = (
'DistanceMixin',
'WeightMixin',
)
class WeightMixin(models.Model):
weight = models.DecimalField(
verbose_name=_('weight'),
max_digits=8,
decimal_places=2,
blank=True,
null=True
)
weight_unit = models.CharField(
verbose_name=_('weight unit'),
max_length=50,
choices=WeightUnitChoices,
blank=True,
)
# Stores the normalized weight (in grams) for database ordering
_abs_weight = models.PositiveBigIntegerField(
blank=True,
null=True
)
class Meta:
abstract = True
def save(self, *args, **kwargs):
# Store the given weight (if any) in grams for use in database ordering
if self.weight and self.weight_unit:
self._abs_weight = to_grams(self.weight, self.weight_unit)
else:
self._abs_weight = None
super().save(*args, **kwargs)
def clean(self):
super().clean()
# Validate weight and weight_unit
if self.weight and not self.weight_unit:
raise ValidationError(_("Must specify a unit when setting a weight"))
class DistanceMixin(models.Model):
distance = models.DecimalField(
verbose_name=_('distance'),
max_digits=8,
decimal_places=2,
blank=True,
null=True
)
distance_unit = models.CharField(
verbose_name=_('distance unit'),
max_length=50,
choices=DistanceUnitChoices,
blank=True,
)
# Stores the normalized distance (in meters) for database ordering
_abs_distance = models.DecimalField(
max_digits=10,
decimal_places=4,
blank=True,
null=True
)
class Meta:
abstract = True
def save(self, *args, **kwargs):
# Store the given distance (if any) in meters for use in database ordering
if self.distance is not None and self.distance_unit:
self._abs_distance = to_meters(self.distance, self.distance_unit)
else:
self._abs_distance = None
# Clear distance_unit if no distance is defined
if self.distance is None:
self.distance_unit = ''
super().save(*args, **kwargs)
def clean(self):
super().clean()
# Validate distance and distance_unit
if self.distance and not self.distance_unit:
raise ValidationError(_("Must specify a unit when setting a distance"))

View File

@ -2,7 +2,8 @@ from decimal import Decimal
from django.utils.translation import gettext as _
from dcim.choices import CableLengthUnitChoices, WeightUnitChoices
from dcim.choices import CableLengthUnitChoices
from netbox.choices import WeightUnitChoices
__all__ = (
'to_grams',

View File

@ -4,6 +4,7 @@ from dcim.api.serializers_.device_components import InterfaceSerializer
from dcim.choices import LinkStatusChoices
from netbox.api.fields import ChoiceField
from netbox.api.serializers import NetBoxModelSerializer
from netbox.choices import *
from tenancy.api.serializers_.tenants import TenantSerializer
from wireless.choices import *
from wireless.models import WirelessLink
@ -20,7 +21,7 @@ class WirelessLinkSerializer(NetBoxModelSerializer):
tenant = TenantSerializer(nested=True, required=False, allow_null=True)
auth_type = ChoiceField(choices=WirelessAuthTypeChoices, required=False, allow_blank=True)
auth_cipher = ChoiceField(choices=WirelessAuthCipherChoices, required=False, allow_blank=True)
distance_unit = ChoiceField(choices=WirelessLinkDistanceUnitChoices, allow_blank=True, required=False, allow_null=True)
distance_unit = ChoiceField(choices=DistanceUnitChoices, allow_blank=True, required=False, allow_null=True)
class Meta:
model = WirelessLink

View File

@ -481,21 +481,3 @@ class WirelessAuthCipherChoices(ChoiceSet):
(CIPHER_TKIP, 'TKIP'),
(CIPHER_AES, 'AES'),
)
class WirelessLinkDistanceUnitChoices(ChoiceSet):
# Metric
UNIT_KILOMETER = 'km'
UNIT_METER = 'm'
# Imperial
UNIT_MILE = 'mi'
UNIT_FOOT = 'ft'
CHOICES = (
(UNIT_KILOMETER, _('Kilometers')),
(UNIT_METER, _('Meters')),
(UNIT_MILE, _('Miles')),
(UNIT_FOOT, _('Feet')),
)

View File

@ -3,6 +3,7 @@ from django.utils.translation import gettext_lazy as _
from dcim.choices import LinkStatusChoices
from ipam.models import VLAN
from netbox.choices import *
from netbox.forms import NetBoxModelBulkEditForm
from tenancy.models import Tenant
from utilities.forms import add_blank_choice
@ -132,7 +133,7 @@ class WirelessLinkBulkEditForm(NetBoxModelBulkEditForm):
)
distance_unit = forms.ChoiceField(
label=_('Distance unit'),
choices=add_blank_choice(WirelessLinkDistanceUnitChoices),
choices=add_blank_choice(DistanceUnitChoices),
required=False,
initial=''
)

View File

@ -3,6 +3,7 @@ from django.utils.translation import gettext_lazy as _
from dcim.choices import LinkStatusChoices
from dcim.models import Interface
from ipam.models import VLAN
from netbox.choices import *
from netbox.forms import NetBoxModelImportForm
from tenancy.models import Tenant
from utilities.forms.fields import CSVChoiceField, CSVModelChoiceField, SlugField
@ -114,7 +115,7 @@ class WirelessLinkImportForm(NetBoxModelImportForm):
)
distance_unit = CSVChoiceField(
label=_('Distance unit'),
choices=WirelessLinkDistanceUnitChoices,
choices=DistanceUnitChoices,
required=False,
help_text=_('Distance unit')
)

View File

@ -2,6 +2,7 @@ from django import forms
from django.utils.translation import gettext_lazy as _
from dcim.choices import LinkStatusChoices
from netbox.choices import *
from netbox.forms import NetBoxModelFilterSetForm
from tenancy.forms import TenancyFilterForm
from utilities.forms import add_blank_choice
@ -104,7 +105,7 @@ class WirelessLinkFilterForm(TenancyFilterForm, NetBoxModelFilterSetForm):
)
distance_unit = forms.ChoiceField(
label=_('Distance unit'),
choices=add_blank_choice(WirelessLinkDistanceUnitChoices),
choices=add_blank_choice(DistanceUnitChoices),
required=False
)
tag = TagFilterField(model)

View File

@ -6,6 +6,7 @@ from django.utils.translation import gettext_lazy as _
from dcim.choices import LinkStatusChoices
from dcim.constants import WIRELESS_IFACE_TYPES
from netbox.models import NestedGroupModel, PrimaryModel
from netbox.models.mixins import DistanceMixin
from utilities.conversion import to_meters
from .choices import *
from .constants import *
@ -132,7 +133,7 @@ def get_wireless_interface_types():
return {'type__in': WIRELESS_IFACE_TYPES}
class WirelessLink(WirelessAuthenticationBase, PrimaryModel):
class WirelessLink(WirelessAuthenticationBase, DistanceMixin, PrimaryModel):
"""
A point-to-point connection between two wireless Interfaces.
"""
@ -161,26 +162,6 @@ class WirelessLink(WirelessAuthenticationBase, PrimaryModel):
choices=LinkStatusChoices,
default=LinkStatusChoices.STATUS_CONNECTED
)
distance = models.DecimalField(
verbose_name=_('distance'),
max_digits=8,
decimal_places=2,
blank=True,
null=True
)
distance_unit = models.CharField(
verbose_name=_('distance unit'),
max_length=50,
choices=WirelessLinkDistanceUnitChoices,
blank=True,
)
# Stores the normalized distance (in meters) for database ordering
_abs_distance = models.DecimalField(
max_digits=10,
decimal_places=4,
blank=True,
null=True
)
tenant = models.ForeignKey(
to='tenancy.Tenant',
on_delete=models.PROTECT,
@ -231,10 +212,6 @@ class WirelessLink(WirelessAuthenticationBase, PrimaryModel):
def clean(self):
super().clean()
# Validate distance and distance_unit
if self.distance is not None and not self.distance_unit:
raise ValidationError(_("Must specify a unit when setting a wireless distance"))
# Validate interface types
if self.interface_a.type not in WIRELESS_IFACE_TYPES:
raise ValidationError({
@ -250,16 +227,6 @@ class WirelessLink(WirelessAuthenticationBase, PrimaryModel):
})
def save(self, *args, **kwargs):
# Store the given distance (if any) in meters for use in database ordering
if self.distance is not None and self.distance_unit:
self._abs_distance = to_meters(self.distance, self.distance_unit)
else:
self._abs_distance = None
# Clear distance_unit if no distance is defined
if self.distance is None:
self.distance_unit = ''
# Store the parent Device for the A and B interfaces
self._interface_a_device = self.interface_a.device
self._interface_b_device = self.interface_b.device