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
					
				
					 6 changed files with 118 additions and 95 deletions
				
			
		| 
						 | 
					@ -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()}
 | 
				
			||||||
 | 
					  % endfor
 | 
				
			||||||
    % 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
 | 
					 | 
				
			||||||
</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…
	
	Add table
		Add a link
		
	
		Reference in a new issue