def build_diff_of_subitem_dict(self, earlier_item, later_item, subitem_model) -> List[Dict]: difference_dict = {} for field, earlier_value in earlier_item.items(): if field == 'id': pass else: later_value = later_item[field] if not self.raw: earlier_value = strip_tags(str(earlier_value)) later_value = strip_tags(str(later_value)) if earlier_value is None: # Can't perform a diff on a null value earlier_value = 'None' if later_value is None: later_value = 'None' difference_dict[field] = { 'is_html': self.is_field_html(field, subitem_model), 'diff': self.perform_diff_on_field(earlier_value, later_value) } return [difference_dict]
def generate_diff(self, earlier_dict, later_dict): """ Returns a dictionary containing a list of tuples with the differences per field. The first element of the tuple specifies if it is an insertion (1), a deletion (-1), or an equality (0). Example: {field: [(0, hello), (1, world)]} """ field_to_diff = {} # Only get the shared field names field_names = list(set(earlier_dict.keys()).intersection(set(later_dict.keys()))) for field in earlier_dict: # Iterate through all fields in the dictionary show_field = field not in self.hidden_diff_fields and field in field_names if show_field: # Don't show fields like modified, which are set by the database earlier_value = earlier_dict[field] later_value = later_dict[field] if not self.both_fields_empty(earlier_value, later_value): if isinstance(earlier_value, str) or isinstance(earlier_value, int): # No special treatment required for strings and int earlier = str(earlier_value) later = str(later_value) if not self.raw: # Strip tags if it's not raw earlier = strip_tags(earlier) later = strip_tags(later) is_html_field = self.is_field_html(field, self.model) field_to_diff[field] = {'user_friendly_name': field.title(), 'subitem': False, 'is_html': is_html_field, 'diffs': self.perform_diff_on_field(earlier, later)} elif isinstance(earlier_value, dict): # It's a single subitem subitem_model = self.get_model_from_foreign_key_field(self.model, self.clean_field(field)) field_to_diff[field] = { 'user_friendly_name': self.get_user_friendly_field_name(field, self.model), 'subitem': True, 'diffs': self.build_diff_of_subitem_dict(earlier_value, later_value, subitem_model) } elif isinstance(earlier_value, list): # It's a list of subitems subitem_model = self.get_model_from_foreign_key_field(self.model, field) field_to_diff[field] = { 'user_friendly_name': self.get_user_friendly_field_name(field, self.model), 'subitem': True, 'diffs': self.build_diff_of_subitems(earlier_value, later_value, subitem_model)} return field_to_diff
def generate_diff_for_added_or_removed_fields(self, ids, values, subitem_model, added=True): """ Generates the diff for fields that have been added/removed from a concept comparision""" differences = [] for id in ids: item = values[id] difference_dict = {} for field, value in item.items(): if field == 'id': pass else: if not self.raw: value = strip_tags(str(value)) # Because DiffMatchPatch returns a list of tuples of diffs # for consistent display we also return a list of tuples of diffs is_html = False if subitem_model is CustomValue: custom_field_id = item['field'] if custom_field_id in self.get_html_custom_field_ids() and field == 'content': is_html = True else: is_html = self.is_field_html(field, subitem_model) if added: difference_dict[field] = {'is_html': is_html, 'diff': [(1, value)]} else: difference_dict[field] = {'is_html': is_html, 'diff': [(-1, value)]} differences.append(difference_dict) return differences
def zws(string): # Adds a zerowidth space before an em-dash """ ``zws`` or "zero width space" is used to insert a soft break near em-dashed. Since em-dashs are commonly used in Data Element Concept names, this helps them wrap in the right places. For example:: <h1>{% zws item.name %}</h1> """ from aristotle_mdr.utils.utils import strip_tags return mark_safe(strip_tags(string.replace("—", "­—")))
def build_diff_of_item(self, earlier_item, later_item, subitem_model) -> Dict[str, Dict]: """Function that performs the actual comparision of a subitem""" difference_dict = {} for field, earlier_value in earlier_item.items(): if field == 'id': # Don't compare ID pass else: later_value = later_item[field] earlier_value = str(earlier_value) later_value = str(later_value) if not self.raw: earlier_value = strip_tags(earlier_value) later_value = strip_tags(later_value) # Custom logic to determine if CustomValue field is HTML is_html = False if subitem_model is CustomValue: is_html = self.is_custom_field_html(field, later_item) else: is_html = self.is_field_html(field, subitem_model) difference = [(difference_code, self.replace_item_id(subitem_model, field, difference)) for difference_code, difference in self.diff_field(earlier_value, later_value)] difference_dict[field] = { 'is_html': is_html, 'diff': difference } return difference_dict
def build_diff_of_subitems(self, earlier_values, later_values, subitem_model) -> List[Dict]: """ Given a list of dictionaries containing representations of objects, iterates through and returns a list of difference dictionaries per field Example: [{'field': [(0, hello), (1, world)], 'other_field': [(0, goodbye), (-1, world)]] """ differences: list = [] if not self.both_fields_empty(earlier_values, later_values): key = self.get_subitem_key(subitem_model) earlier_items = {item[key]: item for item in earlier_values} earlier_items = self.replace_id_with_names(subitem_model, earlier_items) later_items = {item[key]: item for item in later_values} later_items = self.replace_id_with_names(subitem_model, later_items) # Items that are in the later items but not the earlier items have been 'added' added_ids = set(later_items.keys()) - set(earlier_items.keys()) added_items = self.generate_diff_for_added_or_removed_fields(added_ids, later_items, subitem_model, added=True) if added_items: differences.extend(added_items) # Items that are in the earlier items but not the later items have been 'removed' removed_ids = set(earlier_items.keys()) - set(later_items.keys()) removed_items = self.generate_diff_for_added_or_removed_fields(removed_ids, earlier_items, subitem_model, added=False) if removed_items: differences.extend(removed_items) # Items with IDs that are present in both earlier and later data have been changed, # so we want to perform a field-by-field dict comparision. changed_ids = set(earlier_items).intersection(set(later_items)) for id in changed_ids: earlier_item = earlier_items[id] later_item = later_items[id] difference_dict = {} for field, earlier_value in earlier_item.items(): if field == 'id': pass else: later_value = later_item[field] earlier_value = str(earlier_value) later_value = str(later_value) if not self.raw: earlier_value = strip_tags(earlier_value) later_value = strip_tags(later_value) # Custom logic to determine if CustomValue field is HTML is_html = False if subitem_model is CustomValue: custom_field_id = later_item['field'] if custom_field_id in self.get_html_custom_field_ids() and field == 'content': is_html = True else: is_html = self.is_field_html(field, subitem_model) difference_dict[field] = {'is_html': is_html, 'diff': self.perform_diff_on_field(earlier_value, later_value)} if difference_dict: differences.append(difference_dict) return differences