applications: compare elements definitions (#81496)
gitea/hobo/pipeline/head This commit looks good Details

This commit is contained in:
Lauréline Guérin 2023-09-22 15:36:00 +02:00
parent ee8ee01378
commit 8107b3083e
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
4 changed files with 99 additions and 4 deletions

View File

@ -7,12 +7,33 @@
{% endblock %}
{% block appbar %}
<h2>{% trans "Compare versions" %}</h2>
<h2>{% trans 'Compare versions' %} ({% if mode == 'manifest' %}{% trans "Manifest" %}{% else %}{% trans "Elements definitions" %}{% endif %})</h2>
<span class="actions">
<a href="?version1={{ version1.pk }}&version2={{ version2.pk }}&mode=elements">{% trans "Compare elements definitions" %}</a>
<a href="?version1={{ version1.pk }}&version2={{ version2.pk }}&mode=manifest">{% trans "Compare manifest" %}</a>
</span>
{% endblock %}
{% block content %}
<p class="version-description">{{ fromdesc|safe }} ➔ {{ todesc|safe }}</p>
<div class="diff">
{{ diff_serialization|safe }}
{% if mode == 'manifest' %}
{{ diff_serialization|safe }}
{% else %}
<ul class="objects-list single-links application-content">
{% for element in elements %}
<li>
<a>
{% if not element.real_element %}<span class="tag tag-error">{% trans "Element not found" %}</span> {% endif %}{{ element.name }} <span class="extra-info">- {{ element.type_label }}</span>
{% if element.real_element.get_redirect_url %}
<a class="link-action-icon link" href="{{ element.real_element.get_redirect_url }}?application={{ app.slug }}&version1={{ version1.number }}&version2={{ version2.number }}&compare">
{% trans "see" %}
</a>
{% endif %}
</a>
</li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endblock %}

View File

@ -181,8 +181,11 @@ class VersionCompareView(DetailView):
id1 = self.request.GET.get('version1')
id2 = self.request.GET.get('version2')
mode = self.request.GET.get('mode') or 'manifest'
if not id1 or not id2:
raise Http404
if mode not in ['manifest', 'elements']:
raise Http404
version1 = get_object_or_404(Version, pk=id1, application=self.object)
version2 = get_object_or_404(Version, pk=id2, application=self.object)
@ -190,11 +193,12 @@ class VersionCompareView(DetailView):
if version1.last_update_timestamp > version2.last_update_timestamp:
version1, version2 = version2, version1
kwargs['mode'] = mode
kwargs['version1'] = version1
kwargs['version2'] = version2
kwargs['fromdesc'] = self.get_version_desc(version1)
kwargs['todesc'] = self.get_version_desc(version2)
kwargs.update(self.get_compare_context(version1, version2))
kwargs.update(getattr(self, 'get_compare_%s_context' % mode)(version1, version2))
return super().get_context_data(**kwargs)
@ -212,7 +216,7 @@ class VersionCompareView(DetailView):
)
return manifest
def get_compare_context(self, version1, version2):
def get_compare_manifest_context(self, version1, version2):
manifest1 = self.get_manifest(version1)
s1 = json.dumps(manifest1, sort_keys=True, indent=2)
manifest2 = self.get_manifest(version2)
@ -226,6 +230,37 @@ class VersionCompareView(DetailView):
'diff_serialization': diff_serialization,
}
def get_compare_elements_context(self, version1, version2):
type_labels = {}
object_types = get_object_types()
types = [o['id'] for o in object_types]
for object_type in object_types:
type_labels[object_type['id']] = object_type['singular']
# take more recent version
bundle = version2.bundle.read()
tar_io = io.BytesIO(bundle)
with tarfile.open(fileobj=tar_io) as tar:
manifest = json.loads(tar.extractfile('manifest.json').read().decode())
# and extract elements
elements = manifest.get('elements') or []
# exclude roles, impossible to diff
elements = [e for e in elements if e['type'] != 'roles']
# sort elements
elements = sorted(
elements, key=lambda a: (a['auto-dependency'], types.index(a['type']), slugify(a['name']))
)
# and complete with real elements to have more information, such as redirect url
for element in elements:
element['type_label'] = type_labels.get(element['type'])
try:
real_element = Element.objects.get(type=element['type'], slug=element['slug'])
except Element.DoesNotExist:
element['real_element'] = None
continue
element['real_element'] = real_element
return {'elements': elements}
def get_version_desc(self, version):
return '{name} {number} ({timestamp})'.format(
name=_('Version'),

View File

@ -361,6 +361,10 @@ ul.objects-list.application-content {
}
}
ul.objects-list.single-links li a.link::before {
content: "\f08e"; /* fa-external-link */
}
#versions {
.version {
background: white;

View File

@ -460,6 +460,41 @@ def test_create_application(app, admin_user, settings, analyze):
resp = resp.follow()
assert 'Version 2.0' in resp
assert 'Version 1.0' in resp
resp = resp.click('Compare elements definitions')
assert 'Version 2.0' in resp
assert 'Version 1.0' in resp
assert (
'https://wcs.example.invalid/api/export-import/forms/test-form/redirect/?application=test&version1=2.0&version2=1.0&compare'
in resp
)
assert (
'https://wcs.example.invalid/api/export-import/cards/test-card/redirect/?application=test&version1=2.0&version2=1.0&compare'
in resp
)
assert 'Test Form <span class="extra-info">- Form</span>' in resp
assert 'Test Card <span class="extra-info">- Card Model</span>' in resp
assert 'Element not found' not in resp
Element.objects.filter(type='cards').delete()
resp = app.get(
'/applications/manifest/test/version/compare/?version1=%s&version2=%s&mode=elements'
% (new_version.pk, version.pk)
)
assert 'Version 2.0' in resp
assert 'Version 1.0' in resp
assert (
'https://wcs.example.invalid/api/export-import/forms/test-form/redirect/?application=test&version1=2.0&version2=1.0&compare'
in resp
)
assert (
'https://wcs.example.invalid/api/export-import/cards/test-card/redirect/?application=test&version1=2.0&version2=1.0&compare'
not in resp
)
assert 'Test Form <span class="extra-info">- Form</span>' in resp
assert (
'<span class="tag tag-error">Element not found</span> Test Card <span class="extra-info">- Card Model</span>'
in resp
)
def response_content(url, request):
if url.path == '/api/export-import/bundle-declare/':