Add support for version history in person profile view
yay, finally
This commit is contained in:
parent
816e652357
commit
cfdb492349
|
@ -18,11 +18,59 @@
|
||||||
${dynamic_content_title}
|
${dynamic_content_title}
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
<%def name="render_instance_header_buttons()">
|
||||||
|
% if request.has_perm('people_profile.view_versions'):
|
||||||
|
<b-button v-if="!viewingHistory"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="history"
|
||||||
|
@click="viewHistory()">
|
||||||
|
View History
|
||||||
|
</b-button>
|
||||||
|
<div v-if="viewingHistory"
|
||||||
|
class="buttons">
|
||||||
|
<b-button icon-pack="fas"
|
||||||
|
icon-left="redo"
|
||||||
|
@click="refreshHistory()"
|
||||||
|
:disabled="gettingRevisions">
|
||||||
|
{{ gettingRevisions ? "Working, please wait..." : "Refresh History" }}
|
||||||
|
</b-button>
|
||||||
|
<b-button icon-pack="fas"
|
||||||
|
icon-left="user"
|
||||||
|
@click="viewingHistory = false">
|
||||||
|
View Profile
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
% endif
|
||||||
|
</%def>
|
||||||
|
|
||||||
<%def name="page_content()">
|
<%def name="page_content()">
|
||||||
<profile-info @change-content-title="changeContentTitle">
|
<profile-info @change-content-title="changeContentTitle"
|
||||||
|
% if request.has_perm('people_profile.view_versions'):
|
||||||
|
:viewing-history="viewingHistory"
|
||||||
|
:getting-revisions="gettingRevisions"
|
||||||
|
:revisions="revisions"
|
||||||
|
:revision-version-map="revisionVersionMap"
|
||||||
|
% endif
|
||||||
|
>
|
||||||
</profile-info>
|
</profile-info>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
<%def name="render_this_page_component()">
|
||||||
|
## TODO: should override this in a cleaner way! too much duplicate code w/ parent template
|
||||||
|
<this-page @change-content-title="changeContentTitle"
|
||||||
|
% if can_edit_help:
|
||||||
|
:configure-fields-help="configureFieldsHelp"
|
||||||
|
% endif
|
||||||
|
% if request.has_perm('people_profile.view_versions'):
|
||||||
|
:viewing-history="viewingHistory"
|
||||||
|
:getting-revisions="gettingRevisions"
|
||||||
|
:revisions="revisions"
|
||||||
|
:revision-version-map="revisionVersionMap"
|
||||||
|
% endif
|
||||||
|
>
|
||||||
|
</this-page>
|
||||||
|
</%def>
|
||||||
|
|
||||||
<%def name="render_this_page()">
|
<%def name="render_this_page()">
|
||||||
${self.page_content()}
|
${self.page_content()}
|
||||||
</%def>
|
</%def>
|
||||||
|
@ -551,6 +599,16 @@
|
||||||
{{ member._key }}
|
{{ member._key }}
|
||||||
</b-field>
|
</b-field>
|
||||||
|
|
||||||
|
<b-field horizontal label="Person">
|
||||||
|
<a v-if="member.person_uuid != person.uuid"
|
||||||
|
:href="member.view_profile_url">
|
||||||
|
{{ member.person_display_name }}
|
||||||
|
</a>
|
||||||
|
<span v-if="member.person_uuid == person.uuid">
|
||||||
|
{{ member.person_display_name }}
|
||||||
|
</span>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
<b-field horizontal label="Membership Type">
|
<b-field horizontal label="Membership Type">
|
||||||
<a v-if="member.view_membership_type_url"
|
<a v-if="member.view_membership_type_url"
|
||||||
:href="member.view_membership_type_url">
|
:href="member.view_membership_type_url">
|
||||||
|
@ -562,7 +620,7 @@
|
||||||
</b-field>
|
</b-field>
|
||||||
|
|
||||||
<b-field horizontal label="Active">
|
<b-field horizontal label="Active">
|
||||||
{{ member.active }}
|
{{ member.active ? "Yes" : "No" }}
|
||||||
</b-field>
|
</b-field>
|
||||||
|
|
||||||
<b-field horizontal label="Joined">
|
<b-field horizontal label="Joined">
|
||||||
|
@ -574,16 +632,6 @@
|
||||||
{{ member.withdrew }}
|
{{ member.withdrew }}
|
||||||
</b-field>
|
</b-field>
|
||||||
|
|
||||||
<b-field horizontal label="Person">
|
|
||||||
<a v-if="member.person_uuid != person.uuid"
|
|
||||||
:href="member.view_profile_url">
|
|
||||||
{{ member.person_display_name }}
|
|
||||||
</a>
|
|
||||||
<span v-if="member.person_uuid == person.uuid">
|
|
||||||
{{ member.person_display_name }}
|
|
||||||
</span>
|
|
||||||
</b-field>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons" style="align-items: start;">
|
<div class="buttons" style="align-items: start;">
|
||||||
${self.render_member_panel_buttons(member)}
|
${self.render_member_panel_buttons(member)}
|
||||||
|
@ -1019,14 +1067,112 @@
|
||||||
${self.render_user_tab()}
|
${self.render_user_tab()}
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
<%def name="render_profile_info_extra_buttons()"></%def>
|
||||||
|
|
||||||
<%def name="render_profile_info_template()">
|
<%def name="render_profile_info_template()">
|
||||||
<script type="text/x-template" id="profile-info-template">
|
<script type="text/x-template" id="profile-info-template">
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
|
${self.render_profile_info_extra_buttons()}
|
||||||
|
|
||||||
<b-tabs v-model="activeTab"
|
<b-tabs v-model="activeTab"
|
||||||
|
% if request.has_perm('people_profile.view_versions'):
|
||||||
|
v-show="!viewingHistory"
|
||||||
|
% endif
|
||||||
type="is-boxed"
|
type="is-boxed"
|
||||||
@input="activeTabChanged">
|
@input="activeTabChanged">
|
||||||
${self.render_profile_tabs()}
|
${self.render_profile_tabs()}
|
||||||
</b-tabs>
|
</b-tabs>
|
||||||
|
|
||||||
|
% if request.has_perm('people_profile.view_versions'):
|
||||||
|
|
||||||
|
${revisions_grid.render_buefy_table_element(data_prop='revisions',
|
||||||
|
show_footer=True,
|
||||||
|
vshow='viewingHistory',
|
||||||
|
loading='gettingRevisions')|n}
|
||||||
|
|
||||||
|
<b-modal :active.sync="showingRevisionDialog">
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-content">
|
||||||
|
|
||||||
|
<div style="display: flex; justify-content: space-between;">
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<b-field horizontal label="Changed">
|
||||||
|
<div v-html="revision.changed"></div>
|
||||||
|
</b-field>
|
||||||
|
<b-field horizontal label="Changed by">
|
||||||
|
<div v-html="revision.changed_by"></div>
|
||||||
|
</b-field>
|
||||||
|
<b-field horizontal label="IP Address">
|
||||||
|
<div v-html="revision.remote_addr"></div>
|
||||||
|
</b-field>
|
||||||
|
<b-field horizontal label="Comment">
|
||||||
|
<div v-html="revision.comment"></div>
|
||||||
|
</b-field>
|
||||||
|
<b-field horizontal label="TXN ID">
|
||||||
|
<div v-html="revision.txnid"></div>
|
||||||
|
</b-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<b-button @click="viewPrevRevision()"
|
||||||
|
:disabled="!revision.prev_txnid">
|
||||||
|
« Prev
|
||||||
|
</b-button>
|
||||||
|
<b-button @click="viewNextRevision()"
|
||||||
|
:disabled="!revision.next_txnid">
|
||||||
|
» Next
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<b-button @click="toggleVersionFields()">
|
||||||
|
{{ revisionShowAllFields ? "Show Diffs Only" : "Show All Fields" }}
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<div v-for="version in revision.versions"
|
||||||
|
:key="version.key">
|
||||||
|
|
||||||
|
<p class="block has-text-weight-bold">
|
||||||
|
{{ version.model_title }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<table class="diff monospace is-size-7"
|
||||||
|
:class="version.diff_class">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>field name</th>
|
||||||
|
<th>old value</th>
|
||||||
|
<th>new value</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="field in version.fields"
|
||||||
|
:key="field"
|
||||||
|
:class="{diff: 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="old-value">{{ version.values[field].before }}</td>
|
||||||
|
<td class="new-value">{{ version.values[field].after }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</b-modal>
|
||||||
|
% endif
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
</%def>
|
</%def>
|
||||||
|
@ -1611,11 +1757,28 @@
|
||||||
phoneTypeOptions: ${json.dumps(phone_type_options)|n},
|
phoneTypeOptions: ${json.dumps(phone_type_options)|n},
|
||||||
emailTypeOptions: ${json.dumps(email_type_options)|n},
|
emailTypeOptions: ${json.dumps(email_type_options)|n},
|
||||||
maxLengths: ${json.dumps(max_lengths)|n},
|
maxLengths: ${json.dumps(max_lengths)|n},
|
||||||
|
|
||||||
|
% if request.has_perm('people_profile.view_versions'):
|
||||||
|
loadingRevisions: false,
|
||||||
|
showingRevisionDialog: false,
|
||||||
|
revision: {},
|
||||||
|
revisionShowAllFields: false,
|
||||||
|
% endif
|
||||||
}
|
}
|
||||||
|
|
||||||
let ProfileInfo = {
|
let ProfileInfo = {
|
||||||
template: '#profile-info-template',
|
template: '#profile-info-template',
|
||||||
mixins: [FormPosterMixin],
|
mixins: [FormPosterMixin],
|
||||||
|
|
||||||
|
% if request.has_perm('people_profile.view_versions'):
|
||||||
|
props: {
|
||||||
|
viewingHistory: Boolean,
|
||||||
|
gettingRevisions: Boolean,
|
||||||
|
revisions: Array,
|
||||||
|
revisionVersionMap: null,
|
||||||
|
},
|
||||||
|
% endif
|
||||||
|
|
||||||
computed: {},
|
computed: {},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
||||||
|
@ -1641,6 +1804,29 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
activeTabChangedExtra(value) {},
|
activeTabChangedExtra(value) {},
|
||||||
|
|
||||||
|
% if request.has_perm('people_profile.view_versions'):
|
||||||
|
|
||||||
|
viewRevision(row) {
|
||||||
|
this.revision = this.revisionVersionMap[row.txnid]
|
||||||
|
this.showingRevisionDialog = true
|
||||||
|
},
|
||||||
|
|
||||||
|
viewPrevRevision() {
|
||||||
|
let txnid = this.revision.prev_txnid
|
||||||
|
this.revision = this.revisionVersionMap[txnid]
|
||||||
|
},
|
||||||
|
|
||||||
|
viewNextRevision() {
|
||||||
|
let txnid = this.revision.next_txnid
|
||||||
|
this.revision = this.revisionVersionMap[txnid]
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleVersionFields() {
|
||||||
|
this.revisionShowAllFields = !this.revisionShowAllFields
|
||||||
|
},
|
||||||
|
|
||||||
|
% endif
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1662,6 +1848,13 @@
|
||||||
${parent.modify_this_page_vars()}
|
${parent.modify_this_page_vars()}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
% if request.has_perm('people_profile.view_versions'):
|
||||||
|
ThisPage.props.viewingHistory = Boolean
|
||||||
|
ThisPage.props.gettingRevisions = Boolean
|
||||||
|
ThisPage.props.revisions = Array
|
||||||
|
ThisPage.props.revisionVersionMap = null
|
||||||
|
% endif
|
||||||
|
|
||||||
ThisPage.methods.changeContentTitle = function(newTitle) {
|
ThisPage.methods.changeContentTitle = function(newTitle) {
|
||||||
this.$emit('change-content-title', newTitle)
|
this.$emit('change-content-title', newTitle)
|
||||||
}
|
}
|
||||||
|
@ -1717,5 +1910,49 @@
|
||||||
${self.make_profile_info_component()}
|
${self.make_profile_info_component()}
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
<%def name="modify_whole_page_vars()">
|
||||||
|
${parent.modify_whole_page_vars()}
|
||||||
|
|
||||||
|
% if request.has_perm('people_profile.view_versions'):
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
WholePageData.viewingHistory = false
|
||||||
|
WholePageData.gettingRevisions = false
|
||||||
|
WholePageData.gotRevisions = false
|
||||||
|
WholePageData.revisions = []
|
||||||
|
WholePageData.revisionVersionMap = null
|
||||||
|
|
||||||
|
WholePage.methods.viewHistory = function() {
|
||||||
|
this.viewingHistory = true
|
||||||
|
|
||||||
|
if (!this.gotRevisions && !this.gettingRevisions) {
|
||||||
|
this.getRevisions()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WholePage.methods.refreshHistory = function() {
|
||||||
|
if (!this.gettingRevisions) {
|
||||||
|
this.getRevisions()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WholePage.methods.getRevisions = function() {
|
||||||
|
this.gettingRevisions = true
|
||||||
|
|
||||||
|
let url = '${url('people.view_profile_revisions', uuid=person.uuid)}'
|
||||||
|
this.simpleGET(url, {}, response => {
|
||||||
|
this.revisions = response.data.data
|
||||||
|
this.revisionVersionMap = response.data.vmap
|
||||||
|
this.gotRevisions = true
|
||||||
|
this.gettingRevisions = false
|
||||||
|
}, response => {
|
||||||
|
this.gettingRevisions = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
% endif
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
|
||||||
${parent.body()}
|
${parent.body()}
|
||||||
|
|
|
@ -30,6 +30,7 @@ from collections import OrderedDict
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy import orm
|
from sqlalchemy import orm
|
||||||
|
import sqlalchemy_continuum as continuum
|
||||||
|
|
||||||
from rattail.db import model, api
|
from rattail.db import model, api
|
||||||
from rattail.db.util import maxlen
|
from rattail.db.util import maxlen
|
||||||
|
@ -42,6 +43,7 @@ from webhelpers2.html import HTML, tags
|
||||||
|
|
||||||
from tailbone import forms, grids
|
from tailbone import forms, grids
|
||||||
from tailbone.views import MasterView
|
from tailbone.views import MasterView
|
||||||
|
from tailbone.util import raw_datetime
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@ -429,6 +431,9 @@ class PersonView(MasterView):
|
||||||
'dynamic_content_title': self.get_context_content_title(person),
|
'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'
|
template = 'view_profile_buefy'
|
||||||
return self.render_to_response(template, context)
|
return self.render_to_response(template, context)
|
||||||
|
|
||||||
|
@ -1015,6 +1020,188 @@ class PersonView(MasterView):
|
||||||
'employee': self.get_context_employee(employee),
|
'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):
|
def make_note_form(self, mode, person):
|
||||||
schema = NoteSchema().bind(session=self.Session(),
|
schema = NoteSchema().bind(session=self.Session(),
|
||||||
person_uuid=person.uuid)
|
person_uuid=person.uuid)
|
||||||
|
@ -1269,6 +1456,18 @@ class PersonView(MasterView):
|
||||||
renderer='json',
|
renderer='json',
|
||||||
permission='employees.edit')
|
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
|
# manage notes from profile view
|
||||||
if cls.manage_notes_from_profile_view:
|
if cls.manage_notes_from_profile_view:
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue