Provide a way to show enum display text for some version diff fields

master view must explicitly declare which enums for which fields
This commit is contained in:
Lance Edgar 2023-11-30 18:23:47 -06:00
parent 2a9d5f74ce
commit 35131c8732
7 changed files with 172 additions and 26 deletions

6
docs/api/diffs.rst Normal file
View file

@ -0,0 +1,6 @@
``tailbone.diffs``
==================
.. automodule:: tailbone.diffs
:members:

View file

@ -81,6 +81,12 @@ override when defining your subclass.
override this for certain views, if so that should be done within
:meth:`get_help_url()`.
.. attribute:: MasterView.version_diff_factory
Optional factory to use for version diff objects. By default
this is *not set* but a subclass is free to set it. See also
:meth:`get_version_diff_factory()`.
Methods to Override
-------------------
@ -100,6 +106,14 @@ subclass.
.. automethod:: MasterView.get_model_key
.. automethod:: MasterView.get_version_diff_enums
.. automethod:: MasterView.get_version_diff_factory
.. automethod:: MasterView.make_version_diff
.. automethod:: MasterView.title_for_version
Support Methods
---------------

View file

@ -0,0 +1,6 @@
``tailbone.views.members``
==========================
.. automodule:: tailbone.views.members
:members:

View file

@ -44,6 +44,7 @@ Package API:
api/api/batch/core
api/api/batch/ordering
api/diffs
api/forms
api/grids
api/grids.core
@ -53,6 +54,7 @@ Package API:
api/views/batch.vendorcatalog
api/views/core
api/views/master
api/views/members
api/views/purchasing.batch
api/views/purchasing.ordering

View file

@ -34,35 +34,38 @@ from webhelpers2.html import HTML
class Diff(object):
"""
Core diff class. In sore need of documentation.
You must provide the old and new data sets, and the set of
relevant fields as well, if they cannot be easily introspected.
:param old_data: Dict of "old" data values.
:param new_data: Dict of "old" data values.
:param fields: Sequence of relevant field names. Note that
both data dicts are expected to have keys which match these
field names. If you do not specify the fields then they
will (hopefully) be introspected from the old or new data
sets; however this will not work if they are both empty.
:param monospace: If true, this flag will cause the value
columns to be rendered in monospace font. This is assumed
to be helpful when comparing "raw" data values which are
shown as e.g. ``repr(val)``.
:param enums: Optional dict of enums for use when displaying field
values. If specified, keys should be field names and values
should be enum dicts.
"""
def __init__(self, old_data, new_data, columns=None, fields=None,
def __init__(self, old_data, new_data, columns=None, fields=None, enums=None,
render_field=None, render_value=None, nature='dirty',
monospace=False, extra_row_attrs=None):
"""
Constructor. You must provide the old and new data sets, and
the set of relevant fields as well, if they cannot be easily
introspected.
:param old_data: Dict of "old" data values.
:param new_data: Dict of "old" data values.
:param fields: Sequence of relevant field names. Note that
both data dicts are expected to have keys which match these
field names. If you do not specify the fields then they
will (hopefully) be introspected from the old or new data
sets; however this will not work if they are both empty.
:param monospace: If true, this flag will cause the value
columns to be rendered in monospace font. This is assumed
to be helpful when comparing "raw" data values which are
shown as e.g. ``repr(val)``.
"""
self.old_data = old_data
self.new_data = new_data
self.columns = columns or ["field name", "old value", "new value"]
self.fields = fields or self.make_fields()
self.enums = enums or {}
self._render_field = render_field or self.render_field_default
self.render_value = render_value or self.render_value_default
self.nature = nature
@ -92,7 +95,7 @@ class Diff(object):
for the given field. May be an empty string, or a snippet of HTML
attribute syntax, e.g.:
.. code-highlight:: none
.. code-block:: none
class="diff" foo="bar"
@ -132,7 +135,21 @@ class Diff(object):
class VersionDiff(Diff):
"""
Special diff class, for use with version history views
Special diff class, for use with version history views. Note that
while based on :class:`Diff`, this class uses a different
signature for the constructor.
:param version: Reference to a Continuum version record (object).
:param \*args: Typical usage will not require positional args
beyond the ``version`` param, in which case ``old_data`` and
``new_data`` params will be auto-determined based on the
``version``. But if you specify positional args then nothing
automatic is done, they are passed as-is to the parent
:class:`Diff` constructor.
:param \*\*kwargs: Remaining kwargs are passed as-is to the
:class:`Diff` constructor.
"""
def __init__(self, version, *args, **kwargs):
@ -176,9 +193,40 @@ class VersionDiff(Diff):
if field not in unwanted]
def render_version_value(self, field, value, version):
"""
Render the cell value text for the given version/field info.
Note that this method is used to render both sides of the diff
(before and after values).
:param field: Name of the field, as string.
:param value: Raw value for the field, as obtained from ``version``.
:param version: Reference to the Continuum version object.
:returns: Rendered text as string, or ``None``.
"""
text = HTML.tag('span', c=[repr(value)],
style='font-family: monospace;')
# assume the enum display is all we need, if enum exists for the field
if field in self.enums:
# but skip the enum display if None
display = self.enums[field].get(value)
if display is None and value is None:
return text
# otherwise show enum display to the right of raw value
display = self.enums[field].get(value, str(value))
return HTML.tag('span', c=[
text,
HTML.tag('span', c=[display],
style='margin-left: 2rem; font-style: italic; font-weight: bold;'),
])
# next we look for a relationship and may render the foreign object
for prop in self.mapper.relationships:
if prop.uselist:
continue

View file

@ -597,7 +597,6 @@ class MasterView(View):
return defaults
def configure_row_grid(self, grid):
# super(MasterView, self).configure_row_grid(grid)
self.set_row_labels(grid)
self.configure_column_customer_key(grid)
@ -1528,6 +1527,15 @@ class MasterView(View):
})
def title_for_version(self, version):
"""
Must return the title text for the given version. By default
this will be the :term:`rattail:model title` for the version's
data class.
:param version: Reference to a Continuum version object.
:returns: Title text for the version, as string.
"""
cls = continuum.parent_class(version.__class__)
return cls.get_model_title()
@ -4962,13 +4970,52 @@ class MasterView(View):
return diffs.Diff(old_data, new_data, **kwargs)
def get_version_diff_factory(self, **kwargs):
"""
Must return the factory to be used when creating version diff
objects.
By default this returns the
:class:`tailbone.diffs.VersionDiff` class, unless
:attr:`version_diff_factory` is set, in which case that is
returned as-is.
:returns: A factory which can produce
:class:`~tailbone.diffs.VersionDiff` objects.
"""
if hasattr(self, 'version_diff_factory'):
return self.version_diff_factory
return diffs.VersionDiff
def get_version_diff_enums(self, version):
"""
This can optionally return a dict of field enums, to be passed
to the version diff factory. This method is called as part of
:meth:`make_version_diff()`.
"""
def make_version_diff(self, version, *args, **kwargs):
"""
Make a version diff object, using the factory returned by
:meth:`get_version_diff_factory()`.
:param version: Reference to a Continuum version object.
:param title: If specified, must be as a kwarg. Optional
override for the version title text. If not specified,
:meth:`title_for_version()` is called for the title.
:param \*args: Additional args to pass to the factory.
:param \*\*kwargs: Additional kwargs to pass to the factory.
:returns: A :class:`~tailbone.diffs.VersionDiff` object.
"""
if 'title' not in kwargs:
kwargs['title'] = self.title_for_version(version)
if 'enums' not in kwargs:
kwargs['enums'] = self.get_version_diff_enums(version)
factory = self.get_version_diff_factory()
return factory(version, *args, **kwargs)

View file

@ -27,6 +27,7 @@ Member Views
from collections import OrderedDict
import sqlalchemy as sa
import sqlalchemy_continuum as continuum
from rattail.db import model
from rattail.db.model import MembershipType, Member, MemberEquityPayment
@ -71,6 +72,7 @@ class MembershipTypeView(MasterView):
]
def configure_grid(self, g):
""" """
super().configure_grid(g)
g.set_sort_defaults('number')
@ -79,6 +81,7 @@ class MembershipTypeView(MasterView):
g.set_link('name')
def get_row_data(self, memtype):
""" """
model = self.model
return self.Session.query(model.Member)\
.filter(model.Member.membership_type == memtype)
@ -102,7 +105,7 @@ class MemberView(MasterView):
"""
Master view for the Member class.
"""
model_class = model.Member
model_class = Member
is_contact = True
touchable = True
has_versions = True
@ -169,6 +172,7 @@ class MemberView(MasterView):
return app.get_people_handler().get_quickie_search_placeholder()
def configure_grid(self, g):
""" """
super().configure_grid(g)
route_prefix = self.get_route_prefix()
model = self.model
@ -263,13 +267,16 @@ class MemberView(MasterView):
default=False)
def grid_extra_class(self, member, i):
""" """
if not member.active:
return 'warning'
if member.equity_current is False:
return 'notice'
def configure_form(self, f):
""" """
super().configure_form(f)
model = self.model
member = f.model_instance
# date fields
@ -342,6 +349,7 @@ class MemberView(MasterView):
return app.render_currency(total)
def template_kwargs_view(self, **kwargs):
""" """
kwargs = super().template_kwargs_view(**kwargs)
app = self.get_rattail_app()
member = kwargs['instance']
@ -360,10 +368,12 @@ class MemberView(MasterView):
return kwargs
def render_default_email(self, member, field):
""" """
if member.emails:
return member.emails[0].address
def render_default_phone(self, member, field):
""" """
if member.phones:
return member.phones[0].number
@ -376,6 +386,7 @@ class MemberView(MasterView):
return tags.link_to(text, url)
def get_row_data(self, member):
""" """
model = self.model
return self.Session.query(model.MemberEquityPayment)\
.filter(model.MemberEquityPayment.member == member)
@ -395,6 +406,7 @@ class MemberView(MasterView):
uuid=payment.uuid)
def configure_get_simple_settings(self):
""" """
return [
# General
@ -417,7 +429,7 @@ class MemberEquityPaymentView(MasterView):
"""
Master view for the MemberEquityPayment class.
"""
model_class = model.MemberEquityPayment
model_class = MemberEquityPayment
route_prefix = 'member_equity_payments'
url_prefix = '/member-equity-payments'
supports_grid_totals = True
@ -450,6 +462,7 @@ class MemberEquityPaymentView(MasterView):
]
def query(self, session):
""" """
query = super().query(session)
model = self.model
@ -458,6 +471,7 @@ class MemberEquityPaymentView(MasterView):
return query
def configure_grid(self, g):
""" """
super().configure_grid(g)
model = self.model
@ -502,6 +516,7 @@ class MemberEquityPaymentView(MasterView):
return {'totals_display': app.render_currency(total)}
def configure_form(self, f):
""" """
super().configure_form(f)
model = self.model
payment = f.model_instance
@ -543,6 +558,14 @@ class MemberEquityPaymentView(MasterView):
# status_code
f.set_enum('status_code', model.MemberEquityPayment.STATUS)
def get_version_diff_enums(self, version):
""" """
model = self.model
cls = continuum.parent_class(version.__class__)
if cls is model.MemberEquityPayment:
return {'status_code': model.MemberEquityPayment.STATUS}
def defaults(config, **kwargs):
base = globals()