Add smarts to show display text for some version diff fields
e.g. show `str(customer)` along with `customer_uuid` since almost nobody will "care" about the uuid so much, they just want the name
This commit is contained in:
parent
edb5393cdc
commit
9efe767654
|
@ -2,7 +2,7 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# Rattail -- Retail Software Framework
|
# Rattail -- Retail Software Framework
|
||||||
# Copyright © 2010-2019 Lance Edgar
|
# Copyright © 2010-2023 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of Rattail.
|
# This file is part of Rattail.
|
||||||
#
|
#
|
||||||
|
@ -24,7 +24,8 @@
|
||||||
Tools for displaying data diffs
|
Tools for displaying data diffs
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals, absolute_import
|
import sqlalchemy as sa
|
||||||
|
import sqlalchemy_continuum as continuum
|
||||||
|
|
||||||
from pyramid.renderers import render
|
from pyramid.renderers import render
|
||||||
from webhelpers2.html import HTML
|
from webhelpers2.html import HTML
|
||||||
|
@ -36,7 +37,7 @@ class Diff(object):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, old_data, new_data, columns=None, fields=None,
|
def __init__(self, old_data, new_data, columns=None, fields=None,
|
||||||
render_field=None, render_value=None,
|
render_field=None, render_value=None, nature='dirty',
|
||||||
monospace=False, extra_row_attrs=None):
|
monospace=False, extra_row_attrs=None):
|
||||||
"""
|
"""
|
||||||
Constructor. You must provide the old and new data sets, and
|
Constructor. You must provide the old and new data sets, and
|
||||||
|
@ -64,6 +65,7 @@ class Diff(object):
|
||||||
self.fields = fields or self.make_fields()
|
self.fields = fields or self.make_fields()
|
||||||
self._render_field = render_field or self.render_field_default
|
self._render_field = render_field or self.render_field_default
|
||||||
self.render_value = render_value or self.render_value_default
|
self.render_value = render_value or self.render_value_default
|
||||||
|
self.nature = nature
|
||||||
self.monospace = monospace
|
self.monospace = monospace
|
||||||
self.extra_row_attrs = extra_row_attrs
|
self.extra_row_attrs = extra_row_attrs
|
||||||
|
|
||||||
|
@ -126,3 +128,80 @@ class Diff(object):
|
||||||
def render_new_value(self, field):
|
def render_new_value(self, field):
|
||||||
value = self.new_value(field)
|
value = self.new_value(field)
|
||||||
return self.render_value(field, value)
|
return self.render_value(field, value)
|
||||||
|
|
||||||
|
|
||||||
|
class VersionDiff(Diff):
|
||||||
|
"""
|
||||||
|
Special diff class, for use with version history views
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, version, *args, **kwargs):
|
||||||
|
self.title = kwargs.pop('title', None)
|
||||||
|
|
||||||
|
if 'nature' not in kwargs:
|
||||||
|
if version.previous and version.operation_type == continuum.Operation.DELETE:
|
||||||
|
kwargs['nature'] = 'deleted'
|
||||||
|
elif version.previous:
|
||||||
|
kwargs['nature'] = 'dirty'
|
||||||
|
else:
|
||||||
|
kwargs['nature'] = 'new'
|
||||||
|
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self.version = version
|
||||||
|
self.mapper = sa.inspect(continuum.parent_class(type(self.version)))
|
||||||
|
|
||||||
|
def render_version_value(self, field, value, version):
|
||||||
|
text = HTML.tag('span', c=[repr(value)],
|
||||||
|
style='font-family: monospace;')
|
||||||
|
|
||||||
|
for prop in self.mapper.relationships:
|
||||||
|
if prop.uselist:
|
||||||
|
continue
|
||||||
|
|
||||||
|
for col in prop.local_columns:
|
||||||
|
if col.name != field:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not hasattr(version, prop.key):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if col in self.mapper.primary_key:
|
||||||
|
continue
|
||||||
|
|
||||||
|
ref = getattr(version, prop.key)
|
||||||
|
if ref:
|
||||||
|
ref = ref.version_parent
|
||||||
|
if ref:
|
||||||
|
return HTML.tag('span', c=[
|
||||||
|
text,
|
||||||
|
HTML.tag('span', c=[str(ref)],
|
||||||
|
style='margin-left: 2rem; font-style: italic; font-weight: bold;'),
|
||||||
|
])
|
||||||
|
|
||||||
|
return text
|
||||||
|
|
||||||
|
def render_old_value(self, field):
|
||||||
|
if self.nature == 'new':
|
||||||
|
return ''
|
||||||
|
value = self.old_value(field)
|
||||||
|
return self.render_version_value(field, value, self.version.previous)
|
||||||
|
|
||||||
|
def render_new_value(self, field):
|
||||||
|
if self.nature == 'deleted':
|
||||||
|
return ''
|
||||||
|
value = self.new_value(field)
|
||||||
|
return self.render_version_value(field, value, self.version)
|
||||||
|
|
||||||
|
def as_struct(self):
|
||||||
|
values = {}
|
||||||
|
for field in self.fields:
|
||||||
|
values[field] = {'before': self.render_old_value(field),
|
||||||
|
'after': self.render_new_value(field)}
|
||||||
|
return {
|
||||||
|
'key': id(self.version),
|
||||||
|
'model_title': self.title,
|
||||||
|
'diff_class': self.nature,
|
||||||
|
'fields': self.fields,
|
||||||
|
'values': values,
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
## -*- coding: utf-8; -*-
|
## -*- coding: utf-8; -*-
|
||||||
<table class="diff dirty${' monospace' if diff.monospace else ''}">
|
<table class="diff ${diff.nature} ${' monospace' if diff.monospace else ''}">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
% for column in diff.columns:
|
% for column in diff.columns:
|
||||||
|
|
|
@ -50,71 +50,12 @@
|
||||||
</div><!-- form-wrapper -->
|
</div><!-- form-wrapper -->
|
||||||
|
|
||||||
<div class="versions-wrapper">
|
<div class="versions-wrapper">
|
||||||
% for version in versions:
|
% for diff in version_diffs:
|
||||||
|
<h2>${diff.title}</h2>
|
||||||
<h2>${title_for_version(version)}</h2>
|
${diff.render_html()}
|
||||||
|
|
||||||
% if version.previous and version.operation_type == continuum.Operation.DELETE:
|
|
||||||
<table class="diff monospace deleted">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>field name</th>
|
|
||||||
<th>old value</th>
|
|
||||||
<th>new value</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
% for field in fields_for_version(version):
|
|
||||||
<tr>
|
|
||||||
<td class="field">${field}</td>
|
|
||||||
<td class="value old-value">${render_old_value(version, field)}</td>
|
|
||||||
<td class="value new-value"> </td>
|
|
||||||
</tr>
|
|
||||||
% endfor
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
% elif version.previous:
|
|
||||||
<table class="diff monospace dirty">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>field name</th>
|
|
||||||
<th>old value</th>
|
|
||||||
<th>new value</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
% for field in fields_for_version(version):
|
|
||||||
<tr${' class="diff"' if getattr(version, field) != getattr(version.previous, field) else ''|n}>
|
|
||||||
<td class="field">${field}</td>
|
|
||||||
<td class="value old-value">${render_old_value(version, field)}</td>
|
|
||||||
<td class="value new-value">${render_new_value(version, field, 'dirty')}</td>
|
|
||||||
</tr>
|
|
||||||
% endfor
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
% else:
|
|
||||||
<table class="diff monospace new">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>field name</th>
|
|
||||||
<th>old value</th>
|
|
||||||
<th>new value</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
% for field in fields_for_version(version):
|
|
||||||
<tr>
|
|
||||||
<td class="field">${field}</td>
|
|
||||||
<td class="value old-value"> </td>
|
|
||||||
<td class="value new-value">${render_new_value(version, field, 'new')}</td>
|
|
||||||
</tr>
|
|
||||||
% endfor
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
% endif
|
|
||||||
|
|
||||||
% endfor
|
% endfor
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1456,8 +1456,8 @@
|
||||||
:class="{diff: version.values[field].after != version.values[field].before}"
|
:class="{diff: version.values[field].after != version.values[field].before}"
|
||||||
v-show="revisionShowAllFields || version.values[field].after != version.values[field].before">
|
v-show="revisionShowAllFields || version.values[field].after != version.values[field].before">
|
||||||
<td class="field">{{ field }}</td>
|
<td class="field">{{ field }}</td>
|
||||||
<td class="old-value">{{ version.values[field].before }}</td>
|
<td class="old-value" v-html="version.values[field].before"></td>
|
||||||
<td class="new-value">{{ version.values[field].after }}</td>
|
<td class="new-value" v-html="version.values[field].after"></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -1361,6 +1361,20 @@ class MasterView(View):
|
||||||
if newer:
|
if newer:
|
||||||
next_url = self.request.route_url('{}.version'.format(route_prefix), uuid=instance.uuid, txnid=newer.id)
|
next_url = self.request.route_url('{}.version'.format(route_prefix), uuid=instance.uuid, txnid=newer.id)
|
||||||
|
|
||||||
|
version_diffs = []
|
||||||
|
versions = self.get_relevant_versions(transaction, instance)
|
||||||
|
for version in versions:
|
||||||
|
|
||||||
|
old_data = {}
|
||||||
|
new_data = {}
|
||||||
|
fields = self.fields_for_version(version)
|
||||||
|
for field in fields:
|
||||||
|
if version.previous:
|
||||||
|
old_data[field] = getattr(version.previous, field)
|
||||||
|
new_data[field] = getattr(version, field)
|
||||||
|
diff = self.make_version_diff(version, old_data, new_data, fields=fields)
|
||||||
|
version_diffs.append(diff)
|
||||||
|
|
||||||
return self.render_to_response('view_version', {
|
return self.render_to_response('view_version', {
|
||||||
'instance': instance,
|
'instance': instance,
|
||||||
'instance_title': "{} (history)".format(instance_title),
|
'instance_title': "{} (history)".format(instance_title),
|
||||||
|
@ -1368,7 +1382,7 @@ class MasterView(View):
|
||||||
'instance_url': self.get_action_url('versions', instance),
|
'instance_url': self.get_action_url('versions', instance),
|
||||||
'transaction': transaction,
|
'transaction': transaction,
|
||||||
'changed': localtime(self.rattail_config, transaction.issued_at, from_utc=True),
|
'changed': localtime(self.rattail_config, transaction.issued_at, from_utc=True),
|
||||||
'versions': self.get_relevant_versions(transaction, instance),
|
'version_diffs': version_diffs,
|
||||||
'show_prev_next': True,
|
'show_prev_next': True,
|
||||||
'prev_url': prev_url,
|
'prev_url': prev_url,
|
||||||
'next_url': next_url,
|
'next_url': next_url,
|
||||||
|
@ -4815,6 +4829,11 @@ class MasterView(View):
|
||||||
def make_diff(self, old_data, new_data, **kwargs):
|
def make_diff(self, old_data, new_data, **kwargs):
|
||||||
return diffs.Diff(old_data, new_data, **kwargs)
|
return diffs.Diff(old_data, new_data, **kwargs)
|
||||||
|
|
||||||
|
def make_version_diff(self, version, old_data, new_data, **kwargs):
|
||||||
|
if 'title' not in kwargs:
|
||||||
|
kwargs['title'] = self.title_for_version(version)
|
||||||
|
return diffs.VersionDiff(version, old_data, new_data, **kwargs)
|
||||||
|
|
||||||
##############################
|
##############################
|
||||||
# Configuration Views
|
# Configuration Views
|
||||||
##############################
|
##############################
|
||||||
|
|
|
@ -1398,25 +1398,15 @@ class PersonView(MasterView):
|
||||||
# also organize final transaction/versions (diff) map
|
# also organize final transaction/versions (diff) map
|
||||||
vmap = {}
|
vmap = {}
|
||||||
for version in versions:
|
for version in versions:
|
||||||
|
|
||||||
if version.previous and version.operation_type == continuum.Operation.DELETE:
|
|
||||||
diff_class = 'deleted'
|
|
||||||
elif version.previous:
|
|
||||||
diff_class = 'dirty'
|
|
||||||
else:
|
|
||||||
diff_class = 'new'
|
|
||||||
|
|
||||||
# collect before/after field values for version
|
|
||||||
fields = self.fields_for_version(version)
|
fields = self.fields_for_version(version)
|
||||||
values = {}
|
|
||||||
|
old_data = {}
|
||||||
|
new_data = {}
|
||||||
for field in fields:
|
for field in fields:
|
||||||
before = ''
|
if version.previous:
|
||||||
after = ''
|
old_data[field] = getattr(version.previous, field)
|
||||||
if diff_class != 'new':
|
new_data[field] = getattr(version, field)
|
||||||
before = repr(getattr(version.previous, field))
|
diff = self.make_version_diff(version, old_data, new_data, fields=fields)
|
||||||
if diff_class != 'deleted':
|
|
||||||
after = repr(getattr(version, field))
|
|
||||||
values[field] = {'before': before, 'after': after}
|
|
||||||
|
|
||||||
if version.transaction_id not in vmap:
|
if version.transaction_id not in vmap:
|
||||||
txn = version.transaction
|
txn = version.transaction
|
||||||
|
@ -1439,13 +1429,7 @@ class PersonView(MasterView):
|
||||||
'versions': [],
|
'versions': [],
|
||||||
}
|
}
|
||||||
|
|
||||||
vmap[version.transaction_id]['versions'].append({
|
vmap[version.transaction_id]['versions'].append(diff.as_struct())
|
||||||
'key': id(version),
|
|
||||||
'model_title': self.title_for_version(version),
|
|
||||||
'diff_class': diff_class,
|
|
||||||
'fields': fields,
|
|
||||||
'values': values,
|
|
||||||
})
|
|
||||||
|
|
||||||
return {'data': data, 'vmap': vmap}
|
return {'data': data, 'vmap': vmap}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue