From 6098977ee35cc9442c4bf95bdc18d2ce85ca3a0a Mon Sep 17 00:00:00 2001 From: JCWasmx86 Date: Sat, 1 Jun 2024 09:50:25 +0200 Subject: [PATCH] More --- netbox/extras/views.py | 23 +-- netbox/templates/extras/objectchange.html | 24 +-- netbox/utilities/data.py | 175 ++++++++++++++++++++++ 3 files changed, 190 insertions(+), 32 deletions(-) diff --git a/netbox/extras/views.py b/netbox/extras/views.py index 12953d775..ec188fcb5 100644 --- a/netbox/extras/views.py +++ b/netbox/extras/views.py @@ -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, diff --git a/netbox/templates/extras/objectchange.html b/netbox/templates/extras/objectchange.html index a2afdbe24..4f988c108 100644 --- a/netbox/templates/extras/objectchange.html +++ b/netbox/templates/extras/objectchange.html @@ -112,15 +112,10 @@
{% if object.prechange_data %} {% spaceless %} -
{% for k, v in object.prechange_data.items %}{% spaceless %}
-                    {% if k != 'custom_fields' or not cfr_list %}
-                        {{ k }}: {{ v|json }}
-                    {% else %}
-                    {{ k }}: {{% for cfr_data in cfr_list %}    {{ cfr_data.0|json }}: {{ cfr_data.1|json|fixindent }}
-                    {% endfor %}}
-                    {% endif %}
-                {% endspaceless %}{% endfor %}
-                
+
{% for text, changed in text_before %}{% spaceless %}
+                      {{ text }}
+                      {% endspaceless %}{% endfor %}
+                  
{% endspaceless %} {% elif non_atomic_change %} {% trans "Warning: Comparing non-atomic change to previous change record" %} ({{ prev_change.pk }}) @@ -136,14 +131,9 @@
{% if object.postchange_data %} {% spaceless %} -
{% for k, v in object.postchange_data.items %}{% spaceless %}
-                        {% if k != 'custom_fields' or not cfa_list %}
-                            {{ k }}: {{ v|json }}
-                        {% else %}
-                            {{ k }}: {{% for cfa_data in cfa_list %}    {{ cfa_data.0|json }}: {{ cfa_data.1|json|fixindent }}
-{% endfor %}}
-                        {% endif %}
-                        {% endspaceless %}{% endfor %}
+                    
{% for text,changed in text_after %}{% spaceless %}
+                      {{ text }}
+                      {% endspaceless %}{% endfor %}
                     
{% endspaceless %} {% else %} diff --git a/netbox/utilities/data.py b/netbox/utilities/data.py index 88a54a00a..952d91e3a 100644 --- a/netbox/utilities/data.py +++ b/netbox/utilities/data.py @@ -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((" ", 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((" <", 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((" ", 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((" <", 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 \ No newline at end of file