Skip to content

Commit

Permalink
Merge pull request #91 from SSJenny90/fix_temperature_conversion_errors
Browse files Browse the repository at this point in the history
Fix OffsetUnitCalculus error for quantity fields that define temperature values
  • Loading branch information
CarliJoy authored Oct 1, 2023
2 parents 656f303 + 344207d commit 3e7f607
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 4 deletions.
1 change: 1 addition & 0 deletions AUTHORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ Contributors
* Alex Bhandari <[email protected]>
* Jonas Haag <[email protected]>
* Igor Kozyrenko <[email protected]>
* Samuel Scott Jennings <[email protected]>
6 changes: 3 additions & 3 deletions src/quantityfield/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def value_to_string(self, obj) -> str:
def from_db_value(self, value: Any, *args, **kwargs) -> Optional[Quantity]:
if value is None:
return None
return self.ureg.Quantity(value * getattr(self.ureg, self.base_units))
return self.ureg.Quantity(value, getattr(self.ureg, self.base_units))

def to_python(self, value) -> Optional[Quantity]:
if isinstance(value, Quantity):
Expand All @@ -187,7 +187,7 @@ def to_python(self, value) -> Optional[Quantity]:

value = cast(NUMBER_TYPE, to_number(value))

return self.ureg.Quantity(value * getattr(self.ureg, self.base_units))
return self.ureg.Quantity(value, getattr(self.ureg, self.base_units))

def clean(self, value, model_instance) -> Quantity:
"""
Expand Down Expand Up @@ -331,7 +331,7 @@ def clean(self, value):
except (ValueError, TypeError):
raise ValidationError(self.error_messages["invalid"], code="invalid")

val = self.ureg.Quantity(val * getattr(self.ureg, units)).to(self.base_units)
val = self.ureg.Quantity(val, getattr(self.ureg, units)).to(self.base_units)
self.validate(val.magnitude)
self.run_validators(val.magnitude)
return val
Expand Down
1 change: 1 addition & 0 deletions tests/dummyapp/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ def get_readonly_fields(self, request, obj=None):
admin.site.register(FloatFieldSaveModel, ReadOnlyEditing)
admin.site.register(HayBale, ReadOnlyEditing)
admin.site.register(IntFieldSaveModel, ReadOnlyEditing)
admin.site.register(OffsetUnitFloatFieldSaveModel, ReadOnlyEditing)
38 changes: 38 additions & 0 deletions tests/dummyapp/migrations/0002_offsetunitfloatfieldsavemodel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Generated by Django 4.2.5 on 2023-09-25 08:24

from django.db import migrations, models

import quantityfield.fields


class Migration(migrations.Migration):
dependencies = [
("dummyapp", "0001_initial"),
]

operations = [
migrations.CreateModel(
name="OffsetUnitFloatFieldSaveModel",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=20)),
(
"weight",
quantityfield.fields.QuantityField(
base_units="degC", unit_choices=["degC"]
),
),
],
options={
"abstract": False,
},
),
]
6 changes: 6 additions & 0 deletions tests/dummyapp/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,9 @@ class ChoicesDefinedInModel(models.Model):

class ChoicesDefinedInModelInt(models.Model):
weight = IntegerQuantityField("kilogram", unit_choices=["milligram", "pounds"])


class OffsetUnitFloatFieldSaveModel(FieldSaveModel):
# Note: This is a temperature not a weight.
# We wanted to reuse existing test cases inheritance
weight = QuantityField("degC")
39 changes: 39 additions & 0 deletions tests/test_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
FieldSaveModel,
FloatFieldSaveModel,
IntFieldSaveModel,
OffsetUnitFloatFieldSaveModel,
)

Quantity = ureg.Quantity
Expand Down Expand Up @@ -475,3 +476,41 @@ class TestIntFieldSave(IntLikeFieldSaveTestBase, TestCase):

class TestBigIntFieldSave(IntLikeFieldSaveTestBase, TestCase):
MODEL = BigIntFieldSaveModel


class TestOffsetUnitFieldSaveTestBase(FloatLikeFieldSaveTestBase, TestCase):
MODEL = OffsetUnitFloatFieldSaveModel
DEFAULT_WEIGHT_QUANTITY_STR = "100.0 degree_Celsius"
HEAVIEST = 1000
LIGHTEST = 1
FAHRENHEIT_VALUE = 212 # 100 celsius = 212 fahrenheit
COMPARE_QUANTITY = Quantity(100, ureg.fahrenheit)

# Note: weight here is a temperature,
# reused the field name to allow inheritance of float test cae

def test_value_conversion(self):
obj = self.MODEL.objects.first()
degF = obj.weight.to(ureg.fahrenheit) # weight is in celsius
self.assertAlmostEqual(degF.magnitude, self.FAHRENHEIT_VALUE)
self.assertEqual(degF.units, ureg.fahrenheit)

def test_value_stored_as_quantity(self):
obj = self.MODEL.objects.first()
self.assertIsInstance(obj.weight, Quantity)
self.assertEqual(str(obj.weight), self.DEFAULT_WEIGHT_QUANTITY_STR)

def test_stores_value_in_base_units(self):
self.MODEL.objects.create(weight=self.FAHRENHEIT_VALUE, name="fahrenheit")
item = self.MODEL.objects.get(name="fahrenheit")
self.assertEqual(item.weight.units, "degree_Celsius")
self.assertAlmostEqual(item.weight.magnitude, self.FAHRENHEIT_VALUE)

def test_comparison_with_quantity(self):
weight = Quantity(20, ureg.celsius)
qs = self.MODEL.objects.filter(weight__gt=weight)
self.assertNotIn(self.lightest, qs)

def test_comparison_with_quantity_respects_units(self):
qs = self.MODEL.objects.filter(weight__gt=self.COMPARE_QUANTITY)
self.assertNotIn(self.lightest, qs)
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@ docker=
postgres

commands =
pytest -x {posargs}
pytest -vv {posargs}

0 comments on commit 3e7f607

Please sign in to comment.