mirror of
https://github.com/netbox-community/netbox.git
synced 2026-04-06 00:32:08 +02:00
Merge pull request #21837 from netbox-community/21795-update-humanize_speed-to-support-decimal-gbpstbps-output
Closes #21795: Improve humanize_speed formatting for decimal Gbps/Tbps values
This commit is contained in:
@@ -186,26 +186,52 @@ def action_url(parser, token):
|
||||
return ActionURLNode(model, action, kwargs, asvar)
|
||||
|
||||
|
||||
def _format_speed(speed, divisor, unit):
|
||||
"""
|
||||
Format a speed value with a given divisor and unit.
|
||||
|
||||
Handles decimal values and strips trailing zeros for clean output.
|
||||
"""
|
||||
whole, remainder = divmod(speed, divisor)
|
||||
if remainder == 0:
|
||||
return f'{whole} {unit}'
|
||||
|
||||
# Divisors are powers of 10, so len(str(divisor)) - 1 matches the decimal precision.
|
||||
precision = len(str(divisor)) - 1
|
||||
fraction = f'{remainder:0{precision}d}'.rstrip('0')
|
||||
return f'{whole}.{fraction} {unit}'
|
||||
|
||||
|
||||
@register.filter()
|
||||
def humanize_speed(speed):
|
||||
"""
|
||||
Humanize speeds given in Kbps. Examples:
|
||||
Humanize speeds given in Kbps, always using the largest appropriate unit.
|
||||
|
||||
1544 => "1.544 Mbps"
|
||||
100000 => "100 Mbps"
|
||||
10000000 => "10 Gbps"
|
||||
Decimal values are displayed when the result is not a whole number;
|
||||
trailing zeros after the decimal point are stripped for clean output.
|
||||
|
||||
Examples:
|
||||
|
||||
1_544 => "1.544 Mbps"
|
||||
100_000 => "100 Mbps"
|
||||
1_000_000 => "1 Gbps"
|
||||
2_500_000 => "2.5 Gbps"
|
||||
10_000_000 => "10 Gbps"
|
||||
800_000_000 => "800 Gbps"
|
||||
1_600_000_000 => "1.6 Tbps"
|
||||
"""
|
||||
if not speed:
|
||||
return ''
|
||||
if speed >= 1000000000 and speed % 1000000000 == 0:
|
||||
return '{} Tbps'.format(int(speed / 1000000000))
|
||||
if speed >= 1000000 and speed % 1000000 == 0:
|
||||
return '{} Gbps'.format(int(speed / 1000000))
|
||||
if speed >= 1000 and speed % 1000 == 0:
|
||||
return '{} Mbps'.format(int(speed / 1000))
|
||||
if speed >= 1000:
|
||||
return '{} Mbps'.format(float(speed) / 1000)
|
||||
return '{} Kbps'.format(speed)
|
||||
|
||||
speed = int(speed)
|
||||
|
||||
if speed >= 1_000_000_000:
|
||||
return _format_speed(speed, 1_000_000_000, 'Tbps')
|
||||
if speed >= 1_000_000:
|
||||
return _format_speed(speed, 1_000_000, 'Gbps')
|
||||
if speed >= 1_000:
|
||||
return _format_speed(speed, 1_000, 'Mbps')
|
||||
return f'{speed} Kbps'
|
||||
|
||||
|
||||
def _humanize_capacity(value, divisor=1000):
|
||||
|
||||
@@ -3,7 +3,7 @@ from unittest.mock import patch
|
||||
from django.test import TestCase, override_settings
|
||||
|
||||
from utilities.templatetags.builtins.tags import static_with_params
|
||||
from utilities.templatetags.helpers import _humanize_capacity
|
||||
from utilities.templatetags.helpers import _humanize_capacity, humanize_speed
|
||||
|
||||
|
||||
class StaticWithParamsTest(TestCase):
|
||||
@@ -90,3 +90,87 @@ class HumanizeCapacityTest(TestCase):
|
||||
|
||||
def test_default_divisor_is_1000(self):
|
||||
self.assertEqual(_humanize_capacity(2000), '2.00 GB')
|
||||
|
||||
|
||||
class HumanizeSpeedTest(TestCase):
|
||||
"""
|
||||
Test the humanize_speed filter for correct unit selection and decimal formatting.
|
||||
"""
|
||||
|
||||
# Falsy / empty inputs
|
||||
|
||||
def test_none(self):
|
||||
self.assertEqual(humanize_speed(None), '')
|
||||
|
||||
def test_zero(self):
|
||||
self.assertEqual(humanize_speed(0), '')
|
||||
|
||||
def test_empty_string(self):
|
||||
self.assertEqual(humanize_speed(''), '')
|
||||
|
||||
# Kbps (below 1000)
|
||||
|
||||
def test_kbps(self):
|
||||
self.assertEqual(humanize_speed(100), '100 Kbps')
|
||||
|
||||
def test_kbps_low(self):
|
||||
self.assertEqual(humanize_speed(1), '1 Kbps')
|
||||
|
||||
# Mbps (1,000 – 999,999)
|
||||
|
||||
def test_mbps_whole(self):
|
||||
self.assertEqual(humanize_speed(100_000), '100 Mbps')
|
||||
|
||||
def test_mbps_decimal(self):
|
||||
self.assertEqual(humanize_speed(1_544), '1.544 Mbps')
|
||||
|
||||
def test_mbps_10(self):
|
||||
self.assertEqual(humanize_speed(10_000), '10 Mbps')
|
||||
|
||||
# Gbps (1,000,000 – 999,999,999)
|
||||
|
||||
def test_gbps_whole(self):
|
||||
self.assertEqual(humanize_speed(1_000_000), '1 Gbps')
|
||||
|
||||
def test_gbps_decimal(self):
|
||||
self.assertEqual(humanize_speed(2_500_000), '2.5 Gbps')
|
||||
|
||||
def test_gbps_10(self):
|
||||
self.assertEqual(humanize_speed(10_000_000), '10 Gbps')
|
||||
|
||||
def test_gbps_25(self):
|
||||
self.assertEqual(humanize_speed(25_000_000), '25 Gbps')
|
||||
|
||||
def test_gbps_40(self):
|
||||
self.assertEqual(humanize_speed(40_000_000), '40 Gbps')
|
||||
|
||||
def test_gbps_100(self):
|
||||
self.assertEqual(humanize_speed(100_000_000), '100 Gbps')
|
||||
|
||||
def test_gbps_400(self):
|
||||
self.assertEqual(humanize_speed(400_000_000), '400 Gbps')
|
||||
|
||||
def test_gbps_800(self):
|
||||
self.assertEqual(humanize_speed(800_000_000), '800 Gbps')
|
||||
|
||||
# Tbps (1,000,000,000+)
|
||||
|
||||
def test_tbps_whole(self):
|
||||
self.assertEqual(humanize_speed(1_000_000_000), '1 Tbps')
|
||||
|
||||
def test_tbps_decimal(self):
|
||||
self.assertEqual(humanize_speed(1_600_000_000), '1.6 Tbps')
|
||||
|
||||
# Edge cases
|
||||
|
||||
def test_string_input(self):
|
||||
"""Ensure string values are cast to int correctly."""
|
||||
self.assertEqual(humanize_speed('2500000'), '2.5 Gbps')
|
||||
|
||||
def test_non_round_remainder_preserved(self):
|
||||
"""Ensure fractional parts with interior zeros are preserved."""
|
||||
self.assertEqual(humanize_speed(1_001_000), '1.001 Gbps')
|
||||
|
||||
def test_trailing_zeros_stripped(self):
|
||||
"""Ensure trailing fractional zeros are stripped (5.500 → 5.5)."""
|
||||
self.assertEqual(humanize_speed(5_500_000), '5.5 Gbps')
|
||||
|
||||
Reference in New Issue
Block a user