From 092f7549ca89f4d9281942d8ccbff2443aaa4e38 Mon Sep 17 00:00:00 2001 From: Jeremy Stretch Date: Wed, 12 Mar 2025 14:42:38 -0400 Subject: [PATCH] Fixes #18869: Ensure `to_meters()` always returns a clean decimal value (#18883) * Fixes #18869: Ensure to_meters() always returns a clean decimal value * Handle float values --- netbox/utilities/conversion.py | 25 +++++----- netbox/utilities/tests/test_conversions.py | 53 ++++++++++++++++++++++ 2 files changed, 66 insertions(+), 12 deletions(-) create mode 100644 netbox/utilities/tests/test_conversions.py diff --git a/netbox/utilities/conversion.py b/netbox/utilities/conversion.py index 6ce32212a..36ac072d8 100644 --- a/netbox/utilities/conversion.py +++ b/netbox/utilities/conversion.py @@ -1,4 +1,4 @@ -from decimal import Decimal +from decimal import Decimal, InvalidOperation from django.utils.translation import gettext as _ @@ -37,28 +37,29 @@ def to_grams(weight, unit) -> int: ) -def to_meters(length, unit): +def to_meters(length, unit) -> Decimal: """ - Convert the given length to meters. + Convert the given length to meters, returning a Decimal value. """ try: - if length < 0: - raise ValueError(_("Length must be a positive number")) - except TypeError: + length = Decimal(length) + except InvalidOperation: raise TypeError(_("Invalid value '{length}' for length (must be a number)").format(length=length)) + if length < 0: + raise ValueError(_("Length must be a positive number")) if unit == CableLengthUnitChoices.UNIT_KILOMETER: - return length * 1000 + return round(Decimal(length * 1000), 4) if unit == CableLengthUnitChoices.UNIT_METER: - return length + return round(Decimal(length), 4) if unit == CableLengthUnitChoices.UNIT_CENTIMETER: - return length / 100 + return round(Decimal(length / 100), 4) if unit == CableLengthUnitChoices.UNIT_MILE: - return length * Decimal(1609.344) + return round(length * Decimal(1609.344), 4) if unit == CableLengthUnitChoices.UNIT_FOOT: - return length * Decimal(0.3048) + return round(length * Decimal(0.3048), 4) if unit == CableLengthUnitChoices.UNIT_INCH: - return length * Decimal(0.0254) + return round(length * Decimal(0.0254), 4) raise ValueError( _("Unknown unit {unit}. Must be one of the following: {valid_units}").format( unit=unit, diff --git a/netbox/utilities/tests/test_conversions.py b/netbox/utilities/tests/test_conversions.py new file mode 100644 index 000000000..2338a7ac2 --- /dev/null +++ b/netbox/utilities/tests/test_conversions.py @@ -0,0 +1,53 @@ +from decimal import Decimal + +from dcim.choices import CableLengthUnitChoices +from netbox.choices import WeightUnitChoices +from utilities.conversion import to_grams, to_meters +from utilities.testing.base import TestCase + + +class ConversionsTest(TestCase): + + def test_to_grams(self): + self.assertEqual( + to_grams(1, WeightUnitChoices.UNIT_KILOGRAM), + 1000 + ) + self.assertEqual( + to_grams(1, WeightUnitChoices.UNIT_GRAM), + 1 + ) + self.assertEqual( + to_grams(1, WeightUnitChoices.UNIT_POUND), + 453 + ) + self.assertEqual( + to_grams(1, WeightUnitChoices.UNIT_OUNCE), + 28 + ) + + def test_to_meters(self): + self.assertEqual( + to_meters(1.5, CableLengthUnitChoices.UNIT_KILOMETER), + Decimal('1500') + ) + self.assertEqual( + to_meters(1, CableLengthUnitChoices.UNIT_METER), + Decimal('1') + ) + self.assertEqual( + to_meters(1, CableLengthUnitChoices.UNIT_CENTIMETER), + Decimal('0.01') + ) + self.assertEqual( + to_meters(1, CableLengthUnitChoices.UNIT_MILE), + Decimal('1609.344') + ) + self.assertEqual( + to_meters(1, CableLengthUnitChoices.UNIT_FOOT), + Decimal('0.3048') + ) + self.assertEqual( + to_meters(1, CableLengthUnitChoices.UNIT_INCH), + Decimal('0.0254') + )