def merge_state(merged_state, state1, state2, priorities): """ Set attributes on our Canonical model, saving differences. :param merged_state: PropertyState/TaxLotState model inst. :param state1: PropertyState/TaxLotState model inst. Left parent. :param state2: PropertyState/TaxLotState model inst. Right parent. :param priorities: dict, column names with favor new or existing :return: inst(``merged_state``), updated. """ # Calculate the difference between the two states and save into a dictionary can_attrs = get_state_attrs([state1, state2]) default = state2 for attr in can_attrs: # Do we have any differences between these fields? - Check if not None instead of if value. attr_values = [ value for value in list(can_attrs[attr].values()) if value is not None ] attr_values = [v for v in attr_values if v is not None] attr_value = None # Two, differing values are set. if len(attr_values) > 1: # If we have more than one value for this field, choose based on the column priority col_prior = priorities.get(attr, 'Favor New') if col_prior == 'Favor New': attr_value = can_attrs[attr][state2] else: # favor the existing field attr_value = can_attrs[attr][state1] # No values are set elif len(attr_values) < 1: attr_value = None # There is only one value set. else: attr_value = attr_values.pop() if callable(attr): # This callable will be responsible for setting the attribute value, not just returning it. attr(merged_state, default) else: setattr(merged_state, attr, attr_value) merged_state.extra_data = _merge_extra_data(state1.extra_data, state2.extra_data, priorities['extra_data']) # merge measures, scenarios, simulations if isinstance(merged_state, PropertyState): PropertyState.merge_relationships(merged_state, state1, state2) return merged_state
def merge_state(merged_state, state1, state2, can_attrs, default=None): """ Set attributes on our Canonical model, saving differences. :param merged_state: PropertyState/TaxLotState model inst. :param state1: PropertyState/TaxLotState model inst. Left parent. :param state2: PropertyState/TaxLotState model inst. Right parent. :param can_attrs: dict of dicts, {'attr_name': {'dataset1': 'value'...}}. :param default: (optional), which dataset's value to default to. :return: inst(``merged_state``), updated. """ default = default or state2 changes = [] for attr in can_attrs: # Do we have any differences between these fields? - Check if not None instead of if value. attr_values = list( set([ value for value in can_attrs[attr].values() if value is not None ])) attr_values = [v for v in attr_values if v is not None] attr_value = None # Two, differing values are set. if len(attr_values) > 1: # If we have more than one value for this field, save each of the field options in the DB, # but opt for the default when there is a difference. attr_value = can_attrs[attr][default] # No values are set elif len(attr_values) < 1: attr_value = None # There is only one value set. else: attr_value = attr_values.pop() if callable(attr): # This callable will be responsible for setting the attribute value, not just returning it. attr(merged_state, default) else: setattr(merged_state, attr, attr_value) merged_extra_data, merged_extra_data_sources = _merge_extra_data( state1, state2, default=default) merged_state.extra_data = merged_extra_data # merge measures, scenarios, simulations if isinstance(merged_state, PropertyState): PropertyState.merge_relationships(merged_state, state1, state2) return merged_state, changes
def merge_state(merged_state, state1, state2, priorities, ignore_merge_protection=False): """ Set attributes on our Canonical model, saving differences. :param merged_state: PropertyState/TaxLotState model inst. :param state1: PropertyState/TaxLotState model inst. Left parent. :param state2: PropertyState/TaxLotState model inst. Right parent. :param priorities: dict, column names with favor new or existing :return: inst(``merged_state``), updated. """ # Calculate the difference between the two states and save into a dictionary can_attrs = get_state_attrs([state1, state2]) # Handle geocoding results first so that recognize_empty logic is not processed on them. _merge_geocoding_results(merged_state, state1, state2, priorities, can_attrs, ignore_merge_protection) recognize_empty_columns = state2.organization.column_set.filter( table_name=state2.__class__.__name__, recognize_empty=True, is_extra_data=False ).values_list('column_name', flat=True) default = state2 for attr in can_attrs: recognize_empty = attr in recognize_empty_columns attr_values = [ value for value in list(can_attrs[attr].values()) if value is not None or recognize_empty ] attr_value = None # Two, differing values are set. if len(attr_values) > 1: # If we have more than one value for this field, choose based on the column priority col_prior = priorities.get(attr, 'Favor New') if ignore_merge_protection or col_prior == 'Favor New': attr_value = can_attrs[attr][state2] else: # favor the existing field attr_value = can_attrs[attr][state1] # No values are set elif len(attr_values) < 1: attr_value = None # There is only one value set. else: attr_value = attr_values.pop() if callable(attr): # This callable will be responsible for setting the attribute value, not just returning it. attr(merged_state, default) else: setattr(merged_state, attr, attr_value) recognize_empty_ed_columns = state2.organization.column_set.filter( table_name=state2.__class__.__name__, recognize_empty=True, is_extra_data=True ).values_list('column_name', flat=True) merged_state.extra_data = _merge_extra_data( state1.extra_data, state2.extra_data, priorities['extra_data'], recognize_empty_ed_columns, ignore_merge_protection ) # merge measures, scenarios, simulations if isinstance(merged_state, PropertyState): PropertyState.merge_relationships(merged_state, state1, state2) return merged_state