Add support for version history in person profile view
yay, finally
This commit is contained in:
parent
816e652357
commit
cfdb492349
2 changed files with 448 additions and 12 deletions
|
@ -30,6 +30,7 @@ from collections import OrderedDict
|
|||
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy import orm
|
||||
import sqlalchemy_continuum as continuum
|
||||
|
||||
from rattail.db import model, api
|
||||
from rattail.db.util import maxlen
|
||||
|
@ -42,6 +43,7 @@ from webhelpers2.html import HTML, tags
|
|||
|
||||
from tailbone import forms, grids
|
||||
from tailbone.views import MasterView
|
||||
from tailbone.util import raw_datetime
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
@ -429,6 +431,9 @@ class PersonView(MasterView):
|
|||
'dynamic_content_title': self.get_context_content_title(person),
|
||||
}
|
||||
|
||||
if self.request.has_perm('people_profile.view_versions'):
|
||||
context['revisions_grid'] = self.profile_revisions_grid(person)
|
||||
|
||||
template = 'view_profile_buefy'
|
||||
return self.render_to_response(template, context)
|
||||
|
||||
|
@ -1015,6 +1020,188 @@ class PersonView(MasterView):
|
|||
'employee': self.get_context_employee(employee),
|
||||
}
|
||||
|
||||
def profile_revisions_grid(self, person):
|
||||
route_prefix = self.get_route_prefix()
|
||||
factory = self.get_grid_factory()
|
||||
g = factory(
|
||||
'{}.profile.revisions'.format(route_prefix),
|
||||
[], # start with empty data!
|
||||
request=self.request,
|
||||
columns=[
|
||||
'changed',
|
||||
'changed_by',
|
||||
'remote_addr',
|
||||
'comment',
|
||||
],
|
||||
labels={
|
||||
'remote_addr': "IP Address",
|
||||
},
|
||||
linked_columns=[
|
||||
'changed',
|
||||
'changed_by',
|
||||
'comment',
|
||||
],
|
||||
main_actions=[
|
||||
self.make_action('view', icon='eye', url='#',
|
||||
click_handler='viewRevision(props.row)'),
|
||||
],
|
||||
)
|
||||
return g
|
||||
|
||||
def profile_revisions_collect(self, person, versions=None):
|
||||
model = self.model
|
||||
versions = versions or []
|
||||
|
||||
# Person
|
||||
cls = continuum.version_class(model.Person)
|
||||
query = self.Session.query(cls)\
|
||||
.filter(cls.uuid == person.uuid)
|
||||
versions.extend(query.all())
|
||||
|
||||
# User
|
||||
cls = continuum.version_class(model.User)
|
||||
query = self.Session.query(cls)\
|
||||
.filter(cls.person_uuid == person.uuid)
|
||||
versions.extend(query.all())
|
||||
|
||||
# Member
|
||||
cls = continuum.version_class(model.Member)
|
||||
query = self.Session.query(cls)\
|
||||
.filter(cls.person_uuid == person.uuid)
|
||||
versions.extend(query.all())
|
||||
|
||||
# Employee
|
||||
cls = continuum.version_class(model.Employee)
|
||||
query = self.Session.query(cls)\
|
||||
.filter(cls.person_uuid == person.uuid)
|
||||
versions.extend(query.all())
|
||||
|
||||
# EmployeeHistory
|
||||
cls = continuum.version_class(model.EmployeeHistory)
|
||||
query = self.Session.query(cls)\
|
||||
.join(model.Employee,
|
||||
model.Employee.uuid == cls.employee_uuid)\
|
||||
.filter(model.Employee.person_uuid == person.uuid)
|
||||
versions.extend(query.all())
|
||||
|
||||
# PersonPhoneNumber
|
||||
cls = continuum.version_class(model.PersonPhoneNumber)
|
||||
query = self.Session.query(cls)\
|
||||
.filter(cls.parent_uuid == person.uuid)
|
||||
versions.extend(query.all())
|
||||
|
||||
# PersonEmailAddress
|
||||
cls = continuum.version_class(model.PersonEmailAddress)
|
||||
query = self.Session.query(cls)\
|
||||
.filter(cls.parent_uuid == person.uuid)
|
||||
versions.extend(query.all())
|
||||
|
||||
# PersonMailingAddress
|
||||
cls = continuum.version_class(model.PersonMailingAddress)
|
||||
query = self.Session.query(cls)\
|
||||
.filter(cls.parent_uuid == person.uuid)
|
||||
versions.extend(query.all())
|
||||
|
||||
# CustomerPerson
|
||||
cls = continuum.version_class(model.CustomerPerson)
|
||||
query = self.Session.query(cls)\
|
||||
.filter(cls.person_uuid == person.uuid)
|
||||
versions.extend(query.all())
|
||||
|
||||
# Customer
|
||||
cls = continuum.version_class(model.Customer)
|
||||
query = self.Session.query(cls)\
|
||||
.join(model.CustomerPerson, model.CustomerPerson.customer_uuid == cls.uuid)\
|
||||
.filter(model.CustomerPerson.person_uuid == person.uuid)
|
||||
versions.extend(query.all())
|
||||
|
||||
# PersonNote
|
||||
cls = continuum.version_class(model.PersonNote)
|
||||
query = self.Session.query(cls)\
|
||||
.filter(cls.parent_uuid == person.uuid)
|
||||
versions.extend(query.all())
|
||||
|
||||
return versions
|
||||
|
||||
def profile_revisions_data(self):
|
||||
"""
|
||||
View which locates and organizes all relevant "transaction"
|
||||
(version) history data for a given Person. Returns JSON, for
|
||||
use with the Buefy table element on the full profile view.
|
||||
"""
|
||||
person = self.get_instance()
|
||||
versions = self.profile_revisions_collect(person)
|
||||
|
||||
# organize final table data
|
||||
data = []
|
||||
all_txns = set([v.transaction for v in versions])
|
||||
for i, txn in enumerate(
|
||||
sorted(all_txns, key=lambda txn: txn.issued_at, reverse=True),
|
||||
1):
|
||||
data.append({
|
||||
'txnid': txn.id,
|
||||
'changed': raw_datetime(self.rattail_config, txn.issued_at),
|
||||
'changed_by': str(txn.user or '') or None,
|
||||
'remote_addr': txn.remote_addr,
|
||||
'comment': txn.meta.get('comment'),
|
||||
})
|
||||
# also stash the sequential index for this transaction, for use later
|
||||
txn._sequential_index = i
|
||||
|
||||
# also organize final transaction/versions (diff) map
|
||||
vmap = {}
|
||||
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)
|
||||
values = {}
|
||||
for field in fields:
|
||||
before = ''
|
||||
after = ''
|
||||
if diff_class != 'new':
|
||||
before = repr(getattr(version.previous, field))
|
||||
if diff_class != 'deleted':
|
||||
after = repr(getattr(version, field))
|
||||
values[field] = {'before': before, 'after': after}
|
||||
|
||||
if version.transaction_id not in vmap:
|
||||
txn = version.transaction
|
||||
prev_txnid = None
|
||||
next_txnid = None
|
||||
if txn._sequential_index < len(data):
|
||||
prev_txnid = data[txn._sequential_index]['txnid']
|
||||
if txn._sequential_index > 1:
|
||||
next_txnid = data[txn._sequential_index - 2]['txnid']
|
||||
vmap[txn.id] = {
|
||||
'index': txn._sequential_index,
|
||||
'txnid': txn.id,
|
||||
'prev_txnid': prev_txnid,
|
||||
'next_txnid': next_txnid,
|
||||
'changed': raw_datetime(self.rattail_config, txn.issued_at,
|
||||
verbose=True),
|
||||
'changed_by': str(txn.user or '') or None,
|
||||
'remote_addr': txn.remote_addr,
|
||||
'comment': txn.meta.get('comment'),
|
||||
'versions': [],
|
||||
}
|
||||
|
||||
vmap[version.transaction_id]['versions'].append({
|
||||
'key': id(version),
|
||||
'model_title': self.title_for_version(version),
|
||||
'diff_class': diff_class,
|
||||
'fields': fields,
|
||||
'values': values,
|
||||
})
|
||||
|
||||
return {'data': data, 'vmap': vmap}
|
||||
|
||||
def make_note_form(self, mode, person):
|
||||
schema = NoteSchema().bind(session=self.Session(),
|
||||
person_uuid=person.uuid)
|
||||
|
@ -1269,6 +1456,18 @@ class PersonView(MasterView):
|
|||
renderer='json',
|
||||
permission='employees.edit')
|
||||
|
||||
# profile - revisions data
|
||||
config.add_tailbone_permission('people_profile',
|
||||
'people_profile.view_versions',
|
||||
"View full version history for a profile")
|
||||
config.add_route(f'{route_prefix}.view_profile_revisions',
|
||||
f'{instance_url_prefix}/profile/revisions',
|
||||
request_method='GET')
|
||||
config.add_view(cls, attr='profile_revisions_data',
|
||||
route_name=f'{route_prefix}.view_profile_revisions',
|
||||
permission='people_profile.view_versions',
|
||||
renderer='json')
|
||||
|
||||
# manage notes from profile view
|
||||
if cls.manage_notes_from_profile_view:
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue