Skip to content

Commit

Permalink
Fix unit conversion for non numeric values (#4555)
Browse files Browse the repository at this point in the history
* guard against none

* guard against all exceptions

* more robust type checking

* db patterns

* Fixed several issues with tests

---------

Co-authored-by: Alex Swindler <[email protected]>
  • Loading branch information
perryr16 and axelstudios authored Feb 29, 2024
1 parent 79f1575 commit 86103c5
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 6 deletions.
14 changes: 12 additions & 2 deletions seed/models/tax_lot_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,22 @@ def extra_data_to_dict_with_mapping(cls, instance, mappings, fields=None, units=
"""
data = {}

def check_and_convert_numeric(value):
if isinstance(value, (int, float)) and not isinstance(value, bool):
return True, value
if isinstance(value, str):
try:
return True, float(value)
except ValueError:
pass
return False, value

if fields:
for field in fields:
value = instance.get(field, None)
if units.get(field):
is_numeric, value = check_and_convert_numeric(value)
if is_numeric and units.get(field):
value = value * ureg(units[field])

if field in mappings:
data[mappings[field]] = value
else:
Expand Down
4 changes: 2 additions & 2 deletions seed/tests/test_analysis_pipelines.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def test_prepare_analysis_raises_exception_when_analysis_status_indicates_alread

# the status should not have changed
self.analysis.refresh_from_db()
self.assertTrue(Analysis.RUNNING, self.analysis.status)
self.assertEqual(Analysis.RUNNING, self.analysis.status)

def test_prepare_analysis_is_successful_when_analysis_status_is_valid(self):
# Setup
Expand Down Expand Up @@ -744,7 +744,7 @@ def test_prepare_analysis_fails_when_it_fails_to_make_at_least_one_input_file(se
analysis=self.analysis_b,
analysis_property_view=None,
)
self.assertTrue('No files were able to be prepared for the analysis', analysis_message.user_message)
self.assertEqual('No files were able to be prepared for the analysis', analysis_message.user_message)

def test_start_analysis_is_successful_when_inputs_are_valid(self):
# Setup
Expand Down
2 changes: 1 addition & 1 deletion seed/tests/test_columns.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def test_save_mappings_dict(self):
'Ewok': ('TaxLotState', 'Hattin', '', True),
}
self.assertDictEqual(expected, test_mapping)
self.assertTrue(test_mapping['Ewok'], 'Hattin')
self.assertEqual(test_mapping['Ewok'][1], 'Hattin')

c_wookiee = Column.objects.filter(column_name='Wookiee')[0]
# Since the raw column is wookiee, then it should NOT be extra data
Expand Down
49 changes: 48 additions & 1 deletion seed/tests/test_tax_lot_property.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
TaxLotProperty,
TaxLotView
)
from seed.serializers.pint import DEFAULT_UNITS
from seed.tasks import set_update_to_now
from seed.test_helpers.fake import (
FakePropertyFactory,
Expand Down Expand Up @@ -220,7 +221,7 @@ def test_json_export(self):
record_level_keys = list(data['features'][0]['properties'].keys())

self.assertIn('Address Line 1', record_level_keys)
self.assertTrue('Gross Floor Area', record_level_keys)
self.assertIn('Gross Floor Area', record_level_keys)

# ids 52 up to and including 102
self.assertEqual(len(data['features']), 51)
Expand Down Expand Up @@ -249,6 +250,52 @@ def test_set_update_to_now(self):
self.assertGreater(tv.state.updated, before_refresh)
self.assertGreater(tv.taxlot.updated, before_refresh)

def test_extra_data_unit_conversion(self):
def create_column(column_name):
Column.objects.create(
is_extra_data=True,
column_name=column_name,
organization=self.org,
table_name='PropertyState',
data_type='area',
)

column_names = ['area_int', 'area_float', 'area_bool', 'area_none', 'area_str', 'area_str_int', 'area_str_float']
mapping = {}
units = {}
for column_name in column_names:
create_column(column_name)
mapping[column_name] = column_name
units[column_name] = DEFAULT_UNITS['area']

state = self.property_view.state
state.extra_data['area_int'] = 123
state.extra_data['area_float'] = 12.3
state.extra_data['area_bool'] = True
state.extra_data['area_none'] = None
state.extra_data['area_str'] = 'string'
state.extra_data['area_str_int'] = '123'
state.extra_data['area_str_float'] = '12.3'
state.save()

obj_dict = TaxLotProperty.extra_data_to_dict_with_mapping(
state.extra_data,
mapping,
fields=list(mapping.keys()),
units=units
)
self.assertEqual(obj_dict['area_int'].m, 123)
self.assertEqual(str(obj_dict['area_int'].u), 'foot ** 2')
self.assertEqual(obj_dict['area_float'].m, 12.3)
self.assertEqual(str(obj_dict['area_float'].u), 'foot ** 2')
self.assertEqual(obj_dict['area_bool'], True)
self.assertIsNone(obj_dict['area_none'])
self.assertEqual(obj_dict['area_str'], 'string')
self.assertEqual(obj_dict['area_str_int'].m, 123)
self.assertEqual(str(obj_dict['area_str_int'].u), 'foot ** 2')
self.assertEqual(obj_dict['area_str_float'].m, 12.3)
self.assertEqual(str(obj_dict['area_str_float'].u), 'foot ** 2')

def tearDown(self):
for x in self.properties:
PropertyView.objects.get(pk=x).delete()

0 comments on commit 86103c5

Please sign in to comment.