def format_pint_violation(rule, source_value): """ Format a pint min, max violation for human readability. :param rule :param source_value : Quantity - value to format into range :return (formatted_value, formatted_min, formatted_max) : (String, String, String) """ formatted_min = formatted_max = None incoming_data_units = source_value.units rule_units = ureg(rule.units) if rule_units.dimensionless: rule_value = source_value else: rule_value = source_value.to(rule_units) pretty_source_units = pretty_units(source_value) pretty_rule_units = pretty_units(rule_value) if incoming_data_units != rule_units: formatted_value = '{:.1f} {} → {:.1f} {}'.format( source_value.magnitude, pretty_source_units, rule_value.magnitude, pretty_rule_units, ) else: formatted_value = '{:.1f} {}'.format(source_value.magnitude, pretty_rule_units) if rule.min is not None: formatted_min = '{:.1f} {}'.format(rule.min, pretty_rule_units) if rule.max is not None: formatted_max = '{:.1f} {}'.format(rule.max, pretty_rule_units) return (formatted_value, formatted_min, formatted_max)
def minimum_valid(self, value): """ Validate that the value is not less than the minimum specified by the rule. :param value: Value to validate rule against :return: bool, True is valid, False if the value is out of range """ # Convert the rule into the correct types for checking the data rule_min = self.min if rule_min is None: return True else: if isinstance(value, datetime): value = value.astimezone(get_current_timezone()).replace(tzinfo=pytz.UTC) rule_min = make_aware(datetime.strptime(str(int(rule_min)), '%Y%m%d'), pytz.UTC) elif isinstance(value, date): value = value rule_min = datetime.strptime(str(int(rule_min)), '%Y%m%d').date() elif isinstance(value, int): rule_min = int(rule_min) elif isinstance(value, ureg.Quantity): rule_min = rule_min * ureg(self.units) elif not isinstance(value, (str, unicode)): # must be a float... value = float(value) try: if value < rule_min: return False else: # If rule_min is undefined/None or value is okay, then it is valid. return True except ValueError: raise ComparisonError("Value could not be compared numerically")
def maximum_valid(self, value): """ Validate that the value is not greater than the maximum specified by the rule. :param value: Value to validate rule against :return: bool, True is valid, False if the value is out of range """ # Convert the rule into the correct types for checking the data rule_max = self.max if rule_max is None: return True else: if isinstance(value, datetime): value = value.astimezone(get_current_timezone()).replace(tzinfo=pytz.UTC) rule_max = make_aware(datetime.strptime(str(int(rule_max)), '%Y%m%d'), pytz.UTC) elif isinstance(value, date): value = value rule_max = datetime.strptime(str(int(rule_max)), '%Y%m%d').date() elif isinstance(value, int): rule_max = int(rule_max) elif isinstance(value, ureg.Quantity): rule_max = rule_max * ureg(self.units) elif not isinstance(value, basestring): # must be a float... value = float(value) try: if value > rule_max: return False else: return True except ValueError: raise ComparisonError("Value could not be compared numerically")
def to_internal_value(self, data): # get the field off of the database table to get the base units field = self.root.Meta.model._meta.get_field(self.field_name) try: data = float(data) * ureg(field.base_units) except ValueError: data = None return data
def to_internal_value(self, data): # get the field off of the database table to get the base units field = self.root.Meta.model._meta.get_field(self.field_name) try: org = self.root.instance.organization if field.base_units == 'kBtu/ft**2/year': data = float(data) * ureg(org.display_units_eui) elif field.base_units == 'ft**2': data = float(data) * ureg(org.display_units_area) else: # This shouldn't happen unless we're supporting a new pints_unit QuantityField. data = float(data) * ureg(field.base_units) except AttributeError: data = float(data) * ureg(field.base_units) except ValueError: data = None return data
def pint_cleaner(value, units, *args): """Try to convert value to a meaningful (magnitude, units) object.""" value = float_cleaner(value) # API breakage if None does not return None if value is None: return None try: value = value * ureg(units) except ValueError: value = None except TypeError: message = 'pint_cleaner cannot convert {} to a valid Quantity'.format(type(value)) raise TypeError(message) return value
def format_pint_violation(rule, source_value): """ Format a pint min, max violation for human readability. :param rule :param source_value : Quantity - value to format into range :return (formatted_value, formatted_min, formatted_max) : (String, String, String) """ def pretty_units(q): """ hack; can lose it when Pint gets something like a "{:~U}" format code see https://github.com/hgrecco/pint/pull/231 """ return u"{:~P}".format(q).split(" ")[1] formatted_min = formatted_max = None incoming_data_units = source_value.units rule_units = ureg(rule.units) rule_value = source_value.to(rule_units) pretty_source_units = pretty_units(source_value) pretty_rule_units = pretty_units(rule_value) if incoming_data_units != rule_units: formatted_value = u"{:.1f} {} → {:.1f} {}".format( source_value.magnitude, pretty_source_units, rule_value.magnitude, pretty_rule_units, ) else: formatted_value = u"{:.1f} {}".format(source_value, pretty_rule_units) if rule.min is not None: formatted_min = u"{:.1f} {}".format(rule.min, pretty_rule_units) if rule.max is not None: formatted_max = u"{:.1f} {}".format(rule.max, pretty_rule_units) return (formatted_value, formatted_min, formatted_max)
def test_mapping_takes_into_account_selected_units(self): # Just as in the previous test, build extra_data PropertyState raw_state = self.property_state_factory.get_property_state_as_extra_data( import_file_id=self.import_file.id, source_type=ASSESSED_RAW, data_state=DATA_STATE_IMPORT, ) # Replace the site_eui and gross_floor_area key-value that gets # autogenerated by get_property_state_as_extra_data del raw_state.extra_data['site_eui'] raw_state.extra_data['Site EUI'] = 100 del raw_state.extra_data['gross_floor_area'] raw_state.extra_data['Gross Floor Area'] = 100 raw_state.save() self.import_file.raw_save_done = True self.import_file.save() # Build mappings - with unit-aware destinations and non-default unit choices suggested_mappings = mapper.build_column_mapping( list(raw_state.extra_data.keys()), Column.retrieve_all_by_tuple(self.org), previous_mapping=get_column_mapping, map_args=[self.org], thresh=80) mappings = [] for raw_column, suggestion in suggested_mappings.items(): if raw_column == 'Site EUI': mappings.append({ "from_field": raw_column, "from_units": 'kWh/m**2/year', "to_table_name": 'PropertyState', "to_field": 'site_eui', "to_field_display_name": 'Site EUI', }) elif raw_column == 'Gross Floor Area': mappings.append({ "from_field": raw_column, "from_units": 'm**2', "to_table_name": 'PropertyState', "to_field": 'gross_floor_area', "to_field_display_name": 'Gross Floor Area', }) else: other_mapping = { "from_field": raw_column, "from_units": None, "to_table_name": suggestion[0], "to_field": suggestion[1], "to_field_display_name": suggestion[1], } mappings.append(other_mapping) # Perform mapping, creating the initial PropertyState records. Column.create_mappings(mappings, self.org, self.user, self.import_file.id) tasks.map_data(self.import_file.id) # Verify that the values have been converted appropriately state = self.import_file.find_unmatched_property_states().get() self.assertAlmostEqual(state.site_eui, (100 * ureg('kWh/m**2/year')).to('kBtu/ft**2/year')) self.assertAlmostEqual(state.gross_floor_area, (100 * ureg('m**2')).to('ft**2'))
def pretty_units_from_spec(unit_spec): quantity = 0 * ureg(unit_spec) # doesn't matter what the number is return pretty_units(quantity)