{{ field.name }}
{% endif %} + {% if field.description %} +{{ field.contents|safe }}
+
From 9290db23b84ccd6f9e8c2cbcb1d6ef61d5bf9704 Mon Sep 17 00:00:00 2001
From: Micah Denbraver {{ field.contents|safe }} {% trans "This object doesn't have a change history." %} {{ field.contents|safe }}
+ {% for type, content in field.compare_nodes %}{{ content|safe }}{% endfor %}
+
{added_content}'.format(
+ removed_content=prev, added_content=curr)
+ return curr
+ p_a, p_b, p_l = (0, 0, 0)
+ try:
+ for block in difflib.SequenceMatcher(a=prev, b=curr).get_matching_blocks():
+ a, b, l = block
+ removed = prev[p_a+p_l:a]
+ added = curr[p_b+p_l:b]
+ same = curr[b:b+l]
+ if removed:
+ markup += '{content}'.format(content="".join(removed))
+ if added:
+ markup += '{content}'.format(content="".join(added))
+ if same:
+ markup += '{content}'.format(content="".join(same))
+ p_a, p_b, p_l = block
+
+ except TypeError:
+ return curr
+ return markup
+
+ fields = [{
+ 'name': field.attname,
+ 'contents': generate_diff(getattr(prev, field.attname), getattr(curr, field.attname)),
+ } for field in self.model._meta.fields]
+ opts = self.model._meta
+ d = {
+ 'title': _('Compare %s') % force_text(obj),
+ 'app_label': opts.app_label,
+ 'module_name': capfirst(force_text(opts.verbose_name_plural)),
+ 'object_id': object_id,
+ 'object': obj,
+ 'history_bef': prev,
+ 'history_aft': curr,
+ 'fields': fields,
+ 'opts': opts,
+ 'add': False,
+ 'change': False,
+ 'show_delete': False,
+ 'is_popup': False,
+ 'save_as': self.save_as,
+ 'has_add_permission': self.has_add_permission(request),
+ 'has_change_permission': self.has_change_permission(request, obj),
+ 'has_delete_permission': self.has_delete_permission(request, obj),
+ }
+ return render(request, template_name=self.object_compare_template,
+ current_app=self.admin_site.name, dictionary=d)
+
def save_model(self, request, obj, form, change):
"""Set special model attribute to user for reference after save"""
obj._history_user = request.user
diff --git a/simple_history/templates/simple_history/history_compare.html b/simple_history/templates/simple_history/history_compare.html
new file mode 100644
index 000000000..b66c42159
--- /dev/null
+++ b/simple_history/templates/simple_history/history_compare.html
@@ -0,0 +1,47 @@
+{% extends "admin/base_site.html" %}
+{% load i18n admin_urls admin_static admin_modify %}
+
+{% block extrastyle %}{{ block.super }}{% endblock %}
+
+{% block breadcrumbs %}
+
+{% endblock %}
+
+{% block content %}
+
+
+
+{{ field.name }}
{% endif %}
+ {% if field.description %}
+
-
-
+
{% else %}
-
-
-
- {% for action in action_list %}
+ {% trans 'Object' %}
- {% trans 'Date/time' %}
- {% trans 'Comment' %}
- {% trans 'Changed by' %}
-
{added_content}'.format(
- removed_content=prev, added_content=curr)
- return curr
- p_a, p_b, p_l = (0, 0, 0)
- try:
- for block in difflib.SequenceMatcher(a=prev, b=curr).get_matching_blocks():
- a, b, l = block
- removed = prev[p_a+p_l:a]
- added = curr[p_b+p_l:b]
- same = curr[b:b+l]
- if removed:
- markup += '{content}'.format(content="".join(removed))
- if added:
- markup += '{content}'.format(content="".join(added))
- if same:
- markup += '{content}'.format(content="".join(same))
- p_a, p_b, p_l = block
-
- except TypeError:
- return curr
- return markup
+ prev = history.get(pk=request.GET['from'])
+ curr = history.get(pk=request.GET['to'])
fields = [{
'name': field.attname,
- 'contents': generate_diff(getattr(prev, field.attname), getattr(curr, field.attname)),
+ 'content': getattr(curr, field.attname),
+ 'compare_nodes': self._get_delta_nodes(
+ getattr(prev, field.attname), getattr(curr, field.attname)),
} for field in self.model._meta.fields]
opts = self.model._meta
d = {
@@ -241,6 +216,31 @@ def generate_diff(prev, curr):
return render(request, template_name=self.object_compare_template,
current_app=self.admin_site.name, dictionary=d)
+ @staticmethod
+ def _get_delta_nodes(a, b):
+ delta_nodes = []
+ try:
+ a = re.split("(\W)", a)
+ b = re.split("(\W)", b)
+ except TypeError:
+ if a != b:
+ return [('removed', a), ('added', b)]
+ return [('unchanged', b)]
+ prev_a_start, prev_b_start, prev_len = (0, 0, 0)
+ for block in difflib.SequenceMatcher(a=a, b=b).get_matching_blocks():
+ a_start, b_start, length = block
+ removed = "".join(a[prev_a_start + prev_len:a_start])
+ added = "".join(b[prev_b_start + prev_len:b_start])
+ same = "".join(b[b_start:b_start + length])
+ if removed:
+ delta_nodes.append(['removed', removed])
+ if added:
+ delta_nodes.append(['added', added])
+ if same:
+ delta_nodes.append(['unchanged', same])
+ prev_a_start, prev_b_start, prev_len = block
+ return delta_nodes
+
def save_model(self, request, obj, form, change):
"""Set special model attribute to user for reference after save"""
obj._history_user = request.user
diff --git a/simple_history/templates/simple_history/history_compare.html b/simple_history/templates/simple_history/history_compare.html
index b66c42159..58851016a 100644
--- a/simple_history/templates/simple_history/history_compare.html
+++ b/simple_history/templates/simple_history/history_compare.html
@@ -16,13 +16,16 @@
{% block content %}
@@ -31,14 +34,16 @@
{% for field in fields %}
{{ field.name }}
{% endif %}
- {% if field.description %}
- {{ field.name }}
{% endif %}
+ {% if field.description %}
+