This commit is contained in:
JCWasmx86 2024-06-01 09:50:25 +02:00
parent 46719fb356
commit 6098977ee3
3 changed files with 190 additions and 32 deletions

View File

@ -1,3 +1,4 @@
import json
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.contenttypes.models import ContentType
@ -19,7 +20,7 @@ from extras.dashboard.utils import get_widget_class
from netbox.constants import DEFAULT_ACTION_PERMISSIONS
from netbox.views import generic
from netbox.views.generic.mixins import TableMixin
from utilities.data import deep_compare_dict
from utilities.data import deep_compare_dict, make_diff
from utilities.forms import ConfirmationForm, get_field_value
from utilities.htmx import htmx_partial
from utilities.paginator import EnhancedPaginator, get_paginate_count
@ -730,27 +731,19 @@ class ObjectChangeView(generic.ObjectView):
if prechange_data and instance.postchange_data:
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))
text_before, text_after = make_diff(json.dumps(prechange_data, indent=2, sort_keys=True), json.dumps(instance.postchange_data, indent=2, sort_keys=True))
else:
diff_added = None
diff_removed = None
cfa_list = None
cfr_list = None
text_before = None
text_after = None
return {
'diff_added': diff_added,
'diff_removed': diff_removed,
"cfa_list": cfa_list,
"cfr_list": cfr_list,
'text_before': text_before,
'text_after': text_after,
'next_change': next_change,
'prev_change': prev_change,
'related_changes_table': related_changes_table,

View File

@ -112,13 +112,8 @@
<div class="card-body">
{% if object.prechange_data %}
{% spaceless %}
<pre class="change-data">{% for k, v in object.prechange_data.items %}{% spaceless %}
{% 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 %}
<pre class="change-data">{% for text, changed in text_before %}{% spaceless %}
<span{% if changed %} class="removed"{% endif %}>{{ text }}</span>
{% endspaceless %}{% endfor %}
</pre>
{% endspaceless %}
@ -136,13 +131,8 @@
<div class="card-body">
{% if object.postchange_data %}
{% spaceless %}
<pre class="change-data">{% for k, v in object.postchange_data.items %}{% spaceless %}
{% 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 %}
<pre class="change-data">{% for text,changed in text_after %}{% spaceless %}
<span{% if changed %} class="added"{% endif %}>{{ text }}</span>
{% endspaceless %}{% endfor %}
</pre>
{% endspaceless %}

View File

@ -8,6 +8,7 @@ __all__ = (
'drange',
'flatten_dict',
'deep_compare_dict',
'make_diff',
)
@ -127,3 +128,177 @@ def drange(start, end, step=decimal.Decimal(1)):
while start > end:
yield start
start += step
#
# String utilities
#
def regular_line_ending(s: str):
if s.endswith(","):
return True
return not (s.endswith("{") or s.endswith("["))
def begin_of_complex(s: str):
return s.endswith("[") or s.endswith("{")
def has_indent(s: str, indent: str):
return not s.removeprefix(indent).startswith(" ")
def extract_key(s: str):
if '": ' not in s:
return None
idx = s.find(': ')
substr = s[:idx].replace('"', '').strip()
return substr
def make_diff(old: str, new: str):
old_lines = old.splitlines(False)
new_lines = new.splitlines(False)
old_list: list[tuple[str, bool]] = []
new_list: list[tuple[str, bool]] = []
old_idx = 0
new_idx = 0
while True:
if old_idx == len(old_lines) and new_idx == len(new_lines):
break
if old_idx == len(old_lines):
while new_idx != len(new_lines):
new_list.append((new_lines[new_idx], True))
old_list.append(("", False))
new_idx += 1
break
if new_idx == len(new_lines):
while old_idx != len(old_lines):
old_list.append((old_lines[old_idx], True))
new_list.append(("", False))
old_idx += 1
break
old_s = old_lines[old_idx]
old_idx += 1
old_k = extract_key(old_s)
new_s = new_lines[new_idx]
new_k = extract_key(new_s)
new_idx += 1
# Handle additions of keys
if old_k is not None and new_k is not None and old_k != new_k:
print("Found mismatch:", old_k, new_k)
found_it = False
for old_idx2 in range(old_idx, len(old_lines)):
old_s2 = old_lines[old_idx2]
old_k2 = extract_key(old_s2)
print(">>", old_k2, new_k)
if old_k2 == new_k:
print("Found it", old_k2, old_idx2, new_k)
old_list.append((old_s, False))
new_list.append((" <O>", False))
old_s = "" if old_idx >= len(old_lines) else old_lines[old_idx]
for _ in range(old_idx + 1, old_idx2 + 1):
old_s = "" if old_idx >= len(old_lines) else old_lines[old_idx]
new_list.append((" <<OLDFILLER>", False))
old_list.append((old_s, True))
old_idx += 1
old_list.append((old_s, False))
new_list.append((new_s, False))
old_idx += 1
found_it = True
break
if found_it:
continue
for new_idx2 in range(new_idx, len(new_lines)):
new_s2 = new_lines[new_idx2]
new_k2 = extract_key(new_s2)
print(">>22", old_k, new_k2)
if new_k2 == old_k:
print("NEW Found it", new_k2, new_idx2, old_k)
old_list.append((" <N>", False))
new_list.append((new_s, False))
new_s = "" if new_idx >= len(new_lines) else new_lines[new_idx]
for _ in range(new_idx + 1, new_idx2 + 1):
new_s = "" if new_idx >= len(new_lines) else new_lines[new_idx]
old_list.append((" <<NEWFILLER>", False))
new_idx += 1
new_list.append((new_s, True))
old_list.append((old_s, False))
new_list.append((new_s, False))
new_idx += 1
found_it = True
break
continue
if "{" == old_s.strip() and "{" == new_s.strip():
old_list.append(("{", False))
new_list.append(("{", False))
continue
if "}" == old_s.strip() and "}" == new_s.strip():
old_list.append(("}", False))
new_list.append(("}", False))
continue
# Handle:
# "foo": null "foo": [
# "bar",
# "baz",
# "]"
if regular_line_ending(old_s) and begin_of_complex(new_s):
indent_of_new = new_s.replace(new_s.strip(), "")
new_list.append((new_s, True))
old_list.append((old_s, True))
while not has_indent(new_lines[new_idx], indent_of_new):
old_list.append(("", True))
new_list.append((new_lines[new_idx], True))
new_idx += 1
old_list.append(("", True))
new_list.append((new_lines[new_idx], True))
new_idx += 1
continue
# Handle:
# "foo": { "foo": null
# "foo": "bar"
# }
if begin_of_complex(old_s) and regular_line_ending(new_s):
indent_of_old = old_s.replace(old_s.strip(), "")
old_list.append((old_s, True))
new_list.append((new_s, True))
while not has_indent(old_lines[old_idx], indent_of_old):
new_list.append(("", True))
old_list.append((old_lines[old_idx], True))
old_idx += 1
new_list.append(("", True))
old_list.append((old_lines[old_idx], True))
old_idx += 1
continue
if begin_of_complex(old_s) and begin_of_complex(new_s):
indent_of_old = old_s.replace(old_s.strip(), "")
indent_of_new = new_s.replace(new_s.strip(), "")
old_list.append((old_s, False))
new_list.append((new_s, False))
old_tmp = []
new_tmp = []
while not has_indent(old_lines[old_idx], indent_of_old):
old_tmp.append(old_lines[old_idx])
old_idx += 1
while not has_indent(new_lines[new_idx], indent_of_new):
new_tmp.append(new_lines[new_idx])
new_idx += 1
a, b = make_diff("\n".join(old_tmp), "\n".join(new_tmp))
assert len(a) == len(b)
old_list += a
new_list += b
old_list.append((old_lines[old_idx], False))
old_idx += 1
new_list.append((new_lines[new_idx], False))
new_idx += 1
continue
if old_s == new_s:
old_list.append((old_s, False))
new_list.append((new_s, False))
continue
# Handle:
# "foo": 1(,) "foo": 2(,)
if regular_line_ending(old_s) and regular_line_ending(new_s):
old_list.append((old_s, True))
new_list.append((new_s, True))
continue
print(len(old_list), len(new_list))
assert len(old_list) == len(new_list)
return old_list, new_list