This commit is contained in:
JCWasmx86 2024-04-24 17:57:53 +00:00 committed by GitHub
commit 016ba2c716
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 67 additions and 19 deletions

View File

@ -22,7 +22,7 @@ from utilities.htmx import is_htmx
from utilities.paginator import EnhancedPaginator, get_paginate_count
from utilities.rqworker import get_workers_for_queue
from utilities.templatetags.builtins.filters import render_markdown
from utilities.utils import copy_safe_request, count_related, get_viewname, normalize_querydict, shallow_compare_dict
from utilities.utils import copy_safe_request, count_related, deep_compare_dict, get_viewname, normalize_querydict
from utilities.views import ContentTypePermissionRequiredMixin, register_model_view
from . import filtersets, forms, tables
from .forms.reports import ReportForm
@ -719,21 +719,28 @@ class ObjectChangeView(generic.ObjectView):
prechange_data = instance.prechange_data
if prechange_data and instance.postchange_data:
diff_added = shallow_compare_dict(
prechange_data or dict(),
instance.postchange_data or dict(),
exclude=['last_updated'],
)
diff_removed = {
x: prechange_data.get(x) for x in diff_added
} if prechange_data else {}
diff_added, diff_removed = deep_compare_dict(prechange_data, instance.postchange_data, exclude=('last_updated'))
custom_fields_added = diff_added['custom_fields'] if 'custom_fields' in diff_added else None
custom_fields_removed = diff_removed['custom_fields'] if 'custom_fields' in diff_removed else None
cfr_list = []
if custom_fields_added:
for cf, cf_value in prechange_data['custom_fields'].items():
cfr_list.append((cf, cf_value, cf in custom_fields_added))
cfa_list = []
if custom_fields_removed:
for cf, cf_value in instance.postchange_data['custom_fields'].items():
cfa_list.append((cf, cf_value, cf in custom_fields_removed))
else:
diff_added = None
diff_removed = None
cfa_list = None
cfr_list = None
return {
'diff_added': diff_added,
'diff_removed': diff_removed,
"cfa_list": cfa_list,
"cfr_list": cfr_list,
'next_change': next_change,
'prev_change': prev_change,
'related_changes_table': related_changes_table,

View File

@ -119,7 +119,12 @@
<div class="card-body">
{% if object.prechange_data %}
<pre class="change-data">{% for k, v in object.prechange_data.items %}{% spaceless %}
<span{% if k in diff_removed %} class="removed"{% endif %}>{{ k }}: {{ v|json }}</span>
{% if k != 'custom_fields' or not cfr_list %}
<span{% if k in diff_removed %} class="removed"{% endif %}>{{ k }}: {{ v|json }}</span>
{% else %}
<span>{{ k }}: {</span>{% for cfr_data in cfr_list %}<span{% if cfr_data.2 %} class="removed"{% endif %}> {{ cfr_data.0|json }}: {{ cfr_data.1|json|fixindent }}</span>
{% endfor %}<span>}</span>
{% endif %}
{% endspaceless %}{% endfor %}
</pre>
{% elif non_atomic_change %}
@ -138,7 +143,12 @@
<div class="card-body">
{% if object.postchange_data %}
<pre class="change-data">{% for k, v in object.postchange_data.items %}{% spaceless %}
<span{% if k in diff_added %} class="added"{% endif %}>{{ k }}: {{ v|json }}</span>
{% if k != 'custom_fields' or not cfa_list %}
<span{% if k in diff_added %} class="added"{% endif %}>{{ k }}: {{ v|json }}</span>
{% else %}
<span>{{ k }}: {</span>{% for cfa_data in cfa_list %}<span{% if cfa_data.2 %} class="added"{% endif %}> {{ cfa_data.0|json }}: {{ cfa_data.1|json|fixindent }}</span>
{% endfor %}<span>}</span>
{% endif %}
{% endspaceless %}{% endfor %}
</pre>
{% else %}

View File

@ -20,6 +20,7 @@ __all__ = (
'applied_filters',
'as_range',
'divide',
'fixindent',
'get_item',
'get_key',
'humanize_megabytes',
@ -331,3 +332,19 @@ def applied_filters(context, model, form, query_params):
'applied_filters': applied_filters,
'save_link': save_link,
}
@register.filter
def fixindent(value: str) -> str:
"""
Fixes the indentation of multiline strings so they align well
within the changelog view.
"""
lines = value.splitlines(keepends=True)
# For 1 line, the indentation doesn't need to be fixed
if len(lines) == 1:
return value
ret = lines[0]
for line in lines[1:]:
ret += f' {line}'
return ret

View File

@ -395,20 +395,34 @@ def prepare_cloned_fields(instance):
return QueryDict(urlencode(params), mutable=True)
def shallow_compare_dict(source_dict, destination_dict, exclude=tuple()):
def deep_compare_dict(old, new, exclude=tuple()):
"""
Return a new dictionary of the different keys. The values of `destination_dict` are returned. Only the equality of
the first layer of keys/values is checked. `exclude` is a list or tuple of keys to be ignored.
Return a tuple of two dictionaries `(removed_diffs, added_diffs)` in a format
that is compatible with the requirements of `ObjectChangeView`.
`exclude` is a list or tuple of keys to be ignored.
"""
difference = {}
added_diffs = {}
removed_diffs = {}
for key, value in destination_dict.items():
for key in old:
if key in exclude:
continue
if source_dict.get(key) != value:
difference[key] = value
return difference
old_data = old[key]
new_data = new[key]
if old_data != new_data:
if isinstance(old_data, dict) and isinstance(new_data, dict):
(sub_added, sub_removed) = deep_compare_dict(old_data, new_data, exclude=exclude)
if len(sub_removed) > 0:
removed_diffs[key] = sub_removed
if len(sub_added) > 0:
added_diffs[key] = sub_added
else:
removed_diffs[key] = old_data
added_diffs[key] = new_data
return added_diffs, removed_diffs
def flatten_dict(d, prefix='', separator='.'):