View log
This commit is contained in:
51
ir/model.py
51
ir/model.py
@@ -1538,6 +1538,10 @@ class Log(ResourceAccessMixin, ModelSQL, ModelView):
|
||||
})
|
||||
action = fields.Function(
|
||||
fields.Char("Action"), 'get_action', searcher='search_action')
|
||||
old_values = fields.Text("Old Values", readonly=True)
|
||||
new_values = fields.Text("New Values", readonly=True)
|
||||
change_summary = fields.Function(
|
||||
fields.Text("Changes"), 'get_change_summary')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
@@ -1583,6 +1587,53 @@ class Log(ResourceAccessMixin, ModelSQL, ModelView):
|
||||
return f'{field_name} : {state}'
|
||||
return self.target
|
||||
|
||||
@staticmethod
|
||||
def _load_change_values(value):
|
||||
if not value:
|
||||
return {}
|
||||
try:
|
||||
return json.loads(value)
|
||||
except (TypeError, ValueError):
|
||||
return {}
|
||||
|
||||
@staticmethod
|
||||
def _format_change_value(value):
|
||||
if isinstance(value, list):
|
||||
if len(value) > 6:
|
||||
return '[%s items]' % len(value)
|
||||
return ', '.join(Log._format_change_value(v) for v in value)
|
||||
if isinstance(value, dict):
|
||||
return json.dumps(value, sort_keys=True, ensure_ascii=False)
|
||||
if value in (None, ''):
|
||||
return ''
|
||||
return str(value)
|
||||
|
||||
def get_change_summary(self, name):
|
||||
if self.event != 'write':
|
||||
return ''
|
||||
old_values = self._load_change_values(self.old_values)
|
||||
new_values = self._load_change_values(self.new_values)
|
||||
if not old_values and not new_values:
|
||||
return ''
|
||||
pool = Pool()
|
||||
Field = pool.get('ir.model.field')
|
||||
model = self.resource.__name__ if self.resource else None
|
||||
lines = []
|
||||
for field_name in sorted(set(old_values) | set(new_values)):
|
||||
old_value = old_values.get(field_name)
|
||||
new_value = new_values.get(field_name)
|
||||
if old_value == new_value:
|
||||
continue
|
||||
if model:
|
||||
field_label = Field.get_name(model, field_name)
|
||||
else:
|
||||
field_label = field_name
|
||||
lines.append('%s: %s -> %s' % (
|
||||
field_label,
|
||||
self._format_change_value(old_value),
|
||||
self._format_change_value(new_value)))
|
||||
return '\n'.join(lines)
|
||||
|
||||
@classmethod
|
||||
def search_action(cls, name, clause):
|
||||
return [('target', *clause[1:])]
|
||||
|
||||
@@ -13,4 +13,10 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="event"/>
|
||||
<field name="action"/>
|
||||
</group>
|
||||
<label name="change_summary"/>
|
||||
<field name="change_summary"/>
|
||||
<label name="old_values"/>
|
||||
<field name="old_values"/>
|
||||
<label name="new_values"/>
|
||||
<field name="new_values"/>
|
||||
</form>
|
||||
|
||||
@@ -8,4 +8,5 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="user" expand="1"/>
|
||||
<field name="event"/>
|
||||
<field name="action" expand="1"/>
|
||||
<field name="change_summary" optional="1"/>
|
||||
</tree>
|
||||
|
||||
@@ -5,6 +5,7 @@ import base64
|
||||
import csv
|
||||
import datetime
|
||||
import decimal
|
||||
import json
|
||||
import random
|
||||
import time
|
||||
import warnings
|
||||
@@ -193,6 +194,33 @@ class ModelStorage(Model):
|
||||
resource=record, event=event, target=target, user=user,
|
||||
**extra))
|
||||
|
||||
@staticmethod
|
||||
def _json_log_value(value):
|
||||
if isinstance(value, (datetime.date, datetime.datetime, datetime.time)):
|
||||
return value.isoformat()
|
||||
if isinstance(value, decimal.Decimal):
|
||||
return str(value)
|
||||
if isinstance(value, bytes):
|
||||
return '<binary>'
|
||||
if isinstance(value, Model):
|
||||
return str(value)
|
||||
if isinstance(value, (list, tuple)):
|
||||
return [ModelStorage._json_log_value(v) for v in value]
|
||||
if isinstance(value, dict):
|
||||
return {
|
||||
str(k): ModelStorage._json_log_value(v)
|
||||
for k, v in value.items()
|
||||
}
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def _log_value_text(cls, values):
|
||||
values = {
|
||||
name: cls._json_log_value(value)
|
||||
for name, value in values.items()
|
||||
}
|
||||
return json.dumps(values, sort_keys=True, ensure_ascii=False)
|
||||
|
||||
@classmethod
|
||||
def create(cls, vlist):
|
||||
'''
|
||||
@@ -267,7 +295,20 @@ class ModelStorage(Model):
|
||||
all_records += records
|
||||
all_fields.update(values.keys())
|
||||
if transaction.check_access and values:
|
||||
cls.log(records, 'write', ','.join(sorted(values.keys())))
|
||||
fields_names = list(sorted(values.keys()))
|
||||
with without_check_access():
|
||||
old_values = {
|
||||
row['id']: {
|
||||
name: row.get(name)
|
||||
for name in fields_names
|
||||
}
|
||||
for row in cls.read(
|
||||
[r.id for r in records], fields_names)}
|
||||
for record in records:
|
||||
cls.log([record], 'write', ','.join(fields_names),
|
||||
old_values=cls._log_value_text(
|
||||
old_values.get(record.id, {})),
|
||||
new_values=cls._log_value_text(values))
|
||||
|
||||
ModelAccess.check(cls.__name__, 'write')
|
||||
ModelFieldAccess.check(cls.__name__, all_fields, 'write')
|
||||
|
||||
@@ -1198,7 +1198,9 @@ class Purchase(metaclass=PoolMeta):
|
||||
for log in logs:
|
||||
date = getattr(log, 'create_date', None)
|
||||
user = getattr(getattr(log, 'user', None), 'rec_name', None) or ''
|
||||
text = getattr(log, 'action', None) or getattr(log, 'target', None)
|
||||
text = getattr(log, 'change_summary', None)
|
||||
text = text or getattr(log, 'action', None) or getattr(
|
||||
log, 'target', None)
|
||||
if not text:
|
||||
text = getattr(log, 'event_string', None) or log.event
|
||||
activity.append({
|
||||
|
||||
Reference in New Issue
Block a user