Merge lp:~openerp-dev/openobject-addons/trunk-bug-832635-nch into lp:openobject-addons

Proposed by Naresh(OpenERP)
Status: Superseded
Proposed branch: lp:~openerp-dev/openobject-addons/trunk-bug-832635-nch
Merge into: lp:openobject-addons
Diff against target: 450 lines (+144/-212)
1 file modified
audittrail/audittrail.py (+144/-212)
To merge this branch: bzr merge lp:~openerp-dev/openobject-addons/trunk-bug-832635-nch
Reviewer Review Type Date Requested Status
Antony Lesuisse (OpenERP) Needs Fixing
Vo Minh Thu Pending
Olivier Dony (Odoo) Pending
Review via email: mp+74416@code.launchpad.net

This proposal has been superseded by a proposal from 2011-10-03.

Description of the change

Hello,

This merge proposal contains fix for the linked bug as well as refactoring and improvement of the audit trail module.

please provide your feedback,

Thanks,

To post a comment you must log in.
Revision history for this message
Antony Lesuisse (OpenERP) (al-openerp) wrote :

Could you update your merge proposal to the lastest trunk i committed a refactoring to
- override *_cr method to reuse the cursor.
- defer any database access after checking that auditrail is actually installed.

review: Needs Fixing
Revision history for this message
Antony Lesuisse (OpenERP) (al-openerp) wrote :

Please use trunk version of

check_rules
execute_cr
exec_workflow_cr

instead of

check_rule_subscription
audit_log_call

because the former use _cr, are shorter and much faster (no useless db hits).
Remove all cr.commit().

review: Needs Fixing
Revision history for this message
Naresh(OpenERP) (nch-openerp) wrote :

hello,

made compatible to new signatures...

Thanks,

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'audittrail/audittrail.py'
2--- audittrail/audittrail.py 2011-09-29 22:23:47 +0000
3+++ audittrail/audittrail.py 2011-10-03 05:50:30 +0000
4@@ -43,22 +43,17 @@
5 "log_create": fields.boolean("Log Creates",help="Select this if you want to keep track of creation on any record of the object of this rule"),
6 "log_action": fields.boolean("Log Action",help="Select this if you want to keep track of actions on the object of this rule"),
7 "log_workflow": fields.boolean("Log Workflow",help="Select this if you want to keep track of workflow on any record of the object of this rule"),
8- "state": fields.selection((("draft", "Draft"),
9- ("subscribed", "Subscribed")),
10- "State", required=True),
11+ "state": fields.selection((("draft", "Draft"), ("subscribed", "Subscribed")), "State", required=True),
12 "action_id": fields.many2one('ir.actions.act_window', "Action ID"),
13-
14 }
15-
16 _defaults = {
17 'state': lambda *a: 'draft',
18 'log_create': lambda *a: 1,
19 'log_unlink': lambda *a: 1,
20 'log_write': lambda *a: 1,
21 }
22-
23 _sql_constraints = [
24- ('model_uniq', 'unique (object_id)', """There is a rule defined on this object\n You cannot define another one the same object!""")
25+ ('model_uniq', 'unique (object_id)', """There is a rule defined on this object\n You can not define other on the same!""")
26 ]
27 __functions = {}
28
29@@ -175,58 +170,10 @@
30 'field_description': fields.char('Field Description', size=64),
31 }
32
33+
34 class audittrail_objects_proxy(object_proxy):
35 """ Uses Object proxy for auditing changes on object of subscribed Rules"""
36
37- def get_value_text(self, cr, uid, field_name, values, model, context=None):
38- """
39- Gets textual values for the fields
40- e.g.: For field of type many2one it gives its name value instead of id
41-
42- @param cr: the current row, from the database cursor,
43- @param uid: the current user’s ID for security checks,
44- @param field_name: List of fields for text values
45- @param values: Values for field to be converted into textual values
46- @return: values: List of textual values for given fields
47- """
48- if not context:
49- context = {}
50- if field_name in('__last_update','id'):
51- return values
52- pool = pooler.get_pool(cr.dbname)
53- field_pool = pool.get('ir.model.fields')
54- model_pool = pool.get('ir.model')
55- obj_pool = pool.get(model.model)
56- if obj_pool._inherits:
57- inherits_ids = model_pool.search(cr, uid, [('model', '=', obj_pool._inherits.keys()[0])])
58- field_ids = field_pool.search(cr, uid, [('name', '=', field_name), ('model_id', 'in', (model.id, inherits_ids[0]))])
59- else:
60- field_ids = field_pool.search(cr, uid, [('name', '=', field_name), ('model_id', '=', model.id)])
61- field_id = field_ids and field_ids[0] or False
62- assert field_id, _("'%s' field does not exist in '%s' model" %(field_name, model.model))
63-
64- field = field_pool.read(cr, uid, field_id)
65- relation_model = field['relation']
66- relation_model_pool = relation_model and pool.get(relation_model) or False
67-
68- if field['ttype'] == 'many2one':
69- res = False
70- relation_id = False
71- if values and type(values) == tuple:
72- relation_id = values[0]
73- if relation_id and relation_model_pool:
74- relation_model_object = relation_model_pool.read(cr, uid, relation_id, [relation_model_pool._rec_name])
75- res = relation_model_object[relation_model_pool._rec_name]
76- return res
77-
78- elif field['ttype'] in ('many2many','one2many'):
79- res = []
80- for relation_model_object in relation_model_pool.read(cr, uid, values, [relation_model_pool._rec_name]):
81- res.append(relation_model_object[relation_model_pool._rec_name])
82- return res
83-
84- return values
85-
86 def create_log_line(self, cr, uid, log_id, model, lines=[]):
87 """
88 Creates lines for changed fields with its old and new values
89@@ -241,48 +188,70 @@
90 model_pool = pool.get('ir.model')
91 field_pool = pool.get('ir.model.fields')
92 log_line_pool = pool.get('audittrail.log.line')
93- #start Loop
94 for line in lines:
95- if line['name'] in('__last_update','id'):
96- continue
97+ field_obj = obj_pool._all_columns.get(line['name'])
98+ assert field_obj, _("'%s' field does not exist in '%s' model" %(line['name'], model.model))
99+ field_obj = field_obj.column
100+ old_value = line.get('old_value', '')
101+ new_value = line.get('new_value', '')
102+ old_value_text = line.get('old_value_text', '')
103+ new_value_text = line.get('new_value_text', '')
104+ search_models = [ model.id ]
105 if obj_pool._inherits:
106- inherits_ids = model_pool.search(cr, uid, [('model', '=', obj_pool._inherits.keys()[0])])
107- field_ids = field_pool.search(cr, uid, [('name', '=', line['name']), ('model_id', 'in', (model.id, inherits_ids[0]))])
108- else:
109- field_ids = field_pool.search(cr, uid, [('name', '=', line['name']), ('model_id', '=', model.id)])
110- field_id = field_ids and field_ids[0] or False
111- assert field_id, _("'%s' field does not exist in '%s' model" %(line['name'], model.model))
112-
113- field = field_pool.read(cr, uid, field_id)
114- old_value = 'old_value' in line and line['old_value'] or ''
115- new_value = 'new_value' in line and line['new_value'] or ''
116- old_value_text = 'old_value_text' in line and line['old_value_text'] or ''
117- new_value_text = 'new_value_text' in line and line['new_value_text'] or ''
118-
119+ search_models += model_pool.search(cr, uid, [('model', 'in', obj_pool._inherits.keys())])
120+ field_id = field_pool.search(cr, uid, [('name', '=', line['name']), ('model_id', 'in', search_models)])
121 if old_value_text == new_value_text:
122 continue
123- if field['ttype'] == 'many2one':
124- if type(old_value) == tuple:
125- old_value = old_value[0]
126- if type(new_value) == tuple:
127- new_value = new_value[0]
128+ if field_obj._type == 'many2one':
129+ old_value = old_value and old_value[0] or old_value
130+ new_value = new_value and new_value[0] or new_value
131 vals = {
132 "log_id": log_id,
133- "field_id": field_id,
134+ "field_id": field_id and field_id[0] or False,
135 "old_value": old_value,
136 "new_value": new_value,
137 "old_value_text": old_value_text,
138 "new_value_text": new_value_text,
139- "field_description": field['field_description']
140+ "field_description": field_obj.string
141 }
142 line_id = log_line_pool.create(cr, uid, vals)
143- cr.commit()
144- #End Loop
145 return True
146
147+ def get_value_text(self, cr, uid, pool, resource_pool, method, field, value, recursive=True):
148+ field_obj = (resource_pool._all_columns.get(field)).column
149+ if field_obj._type in ('one2many','many2many'):
150+ if recursive:
151+ self.log_fct(cr, uid, field_obj._obj, method, None, value, 'child_relation_log')
152+ data = pool.get(field_obj._obj).name_get(cr, uid, value)
153+ return map(lambda x:x[1], data)
154+ elif field_obj._type == 'many2one':
155+ return value and value[1] or value
156+ return False
157+
158+ def start_log_process(self, cr, user_id, model, method, resource_data, pool, resource_pool):
159+ key1 = '%s_value'%(method == 'create' and 'new' or 'old')
160+ key2 = '%s_value_text'%(method == 'create' and 'new' or 'old')
161+ uid = 1
162+ vals = { 'method': method, 'object_id': model.id,'user_id': user_id}
163+ for resource, fields in resource_data.iteritems():
164+ vals.update({'res_id': resource})
165+ log_id = pool.get('audittrail.log').create(cr, uid, vals)
166+ lines = []
167+ for field_key, value in fields.iteritems():
168+ if field_key in ('__last_update', 'id'):continue
169+ ret_val = self.get_value_text(cr, uid, pool, resource_pool, method, field_key, value, method != 'read')
170+ line = {
171+ 'name': field_key,
172+ key1: value,
173+ key2: ret_val and ret_val or value
174+ }
175+ lines.append(line)
176+ self.create_log_line(cr, uid, log_id, model, lines)
177+ return True
178+
179 def log_fct(self, cr, uid, model, method, fct_src, *args):
180 """
181- Logging function: This function is performs logging oprations according to method
182+ Logging function: This function is performs logging operations according to method
183 @param db: the current database
184 @param uid: the current user’s ID for security checks,
185 @param object: Object who's values are being changed
186@@ -293,99 +262,43 @@
187 """
188 uid_orig = uid
189 uid = 1
190- res2 = args
191 pool = pooler.get_pool(cr.dbname)
192 resource_pool = pool.get(model)
193+ model_pool = pool.get('ir.model')
194 log_pool = pool.get('audittrail.log')
195- model_pool = pool.get('ir.model')
196-
197- model_ids = model_pool.search(cr, uid, [('model', '=', model)])
198+ model_ids = model_pool.search(cr, 1, [('model', '=', model)])
199 model_id = model_ids and model_ids[0] or False
200 assert model_id, _("'%s' Model does not exist..." %(model))
201 model = model_pool.browse(cr, uid, model_id)
202-
203- if method in ('create'):
204- res_id = fct_src(cr, uid_orig, model.model, method, *args)
205- resource = resource_pool.read(cr, uid, res_id, args[0].keys())
206- vals = {
207- "method": method,
208- "object_id": model.id,
209- "user_id": uid_orig,
210- "res_id": resource['id'],
211- }
212- if 'id' in resource:
213- del resource['id']
214- log_id = log_pool.create(cr, uid, vals)
215- lines = []
216- for field in resource:
217- line = {
218- 'name': field,
219- 'new_value': resource[field],
220- 'new_value_text': self.get_value_text(cr, uid, field, resource[field], model)
221- }
222- lines.append(line)
223- self.create_log_line(cr, uid, log_id, model, lines)
224-
225+ relational_table_log = args and args[-1] == 'child_relation_log' or False
226+ if method == 'create':
227+ resource_data = {}
228+ fields_to_read = []
229+ if relational_table_log:
230+ res_id = args[0]
231+ else:
232+ res_id = fct_src(cr, uid_orig, model.model, method, *args)
233+ fields_to_read = args[0].keys()
234+ if res_id:
235+ resource = resource_pool.read(cr, uid, res_id, fields_to_read)
236+ if not isinstance(resource,list):
237+ resource = [resource]
238+ map(lambda x: resource_data.setdefault(x['id'], x), resource)
239+ self.start_log_process(cr, uid_orig, model, method, resource_data, pool, resource_pool)
240 return res_id
241
242- elif method in ('read'):
243+ elif method in('read', 'unlink'):
244 res_ids = args[0]
245 old_values = {}
246- res = fct_src(cr, uid_orig, model.model, method, *args)
247- if type(res) == list:
248- for v in res:
249- old_values[v['id']] = v
250+ if method == 'read':
251+ res = fct_src(cr, uid_orig, model.model, method, *args)
252+ map(lambda x: old_values.setdefault(x['id'], x), res)
253 else:
254- old_values[res['id']] = res
255- for res_id in old_values:
256- vals = {
257- "method": method,
258- "object_id": model.id,
259- "user_id": uid_orig,
260- "res_id": res_id,
261-
262- }
263- log_id = log_pool.create(cr, uid, vals)
264- lines = []
265- for field in old_values[res_id]:
266- line = {
267- 'name': field,
268- 'old_value': old_values[res_id][field],
269- 'old_value_text': self.get_value_text(cr, uid, field, old_values[res_id][field], model)
270- }
271- lines.append(line)
272-
273- self.create_log_line(cr, uid, log_id, model, lines)
274- return res
275-
276- elif method in ('unlink'):
277- res_ids = args[0]
278- old_values = {}
279- for res_id in res_ids:
280- old_values[res_id] = resource_pool.read(cr, uid, res_id)
281-
282- for res_id in res_ids:
283- vals = {
284- "method": method,
285- "object_id": model.id,
286- "user_id": uid_orig,
287- "res_id": res_id,
288-
289- }
290- log_id = log_pool.create(cr, uid, vals)
291- lines = []
292- for field in old_values[res_id]:
293- if field in ('id'):
294- continue
295- line = {
296- 'name': field,
297- 'old_value': old_values[res_id][field],
298- 'old_value_text': self.get_value_text(cr, uid, field, old_values[res_id][field], model)
299- }
300- lines.append(line)
301-
302- self.create_log_line(cr, uid, log_id, model, lines)
303- res = fct_src(cr, uid_orig, model.model, method, *args)
304+ res = resource_pool.read(cr, uid, res_ids)
305+ map(lambda x:old_values.setdefault(x['id'], x), res)
306+ self.start_log_process(cr, uid_orig, model, method, old_values, pool, resource_pool)
307+ if not relational_table_log and method == 'unlink':
308+ res = fct_src(cr, uid_orig, model.model, method, *args)
309 return res
310 else:
311 res_ids = []
312@@ -394,53 +307,76 @@
313 res_ids = args[0]
314 old_values = {}
315 fields = []
316- if len(args)>1 and type(args[1]) == dict:
317+ if len(args) > 1 and isinstance(args[1], dict):
318 fields = args[1].keys()
319- if type(res_ids) in (long, int):
320+ if isinstance(res_ids, (long, int)):
321 res_ids = [res_ids]
322 if res_ids:
323- for resource in resource_pool.read(cr, uid, res_ids):
324- resource_id = resource['id']
325- if 'id' in resource:
326- del resource['id']
327- old_values_text = {}
328- old_value = {}
329- for field in resource.keys():
330- old_value[field] = resource[field]
331- old_values_text[field] = self.get_value_text(cr, uid, field, resource[field], model)
332- old_values[resource_id] = {'text':old_values_text, 'value': old_value}
333-
334+ x2m_old_values = {}
335+ old_values = {}
336+ def inline_process_old_data(res_ids, model, model_id=False):
337+ resource_pool = pool.get(model)
338+ resource_data = resource_pool.read(cr, uid, res_ids)
339+ _old_values = {}
340+ for resource in resource_data:
341+ _old_values_text = {}
342+ _old_value = {}
343+ resource_id = resource['id']
344+ for field in resource.keys():
345+ if field in ('__last_update', 'id'):continue
346+ field_obj = (resource_pool._all_columns.get(field)).column
347+ if field_obj._type in ('one2many','many2many'):
348+ if self.check_rules(cr, uid, field_obj._obj, method):
349+ x2m_model_ids = model_pool.search(cr, 1, [('model', '=', field_obj._obj)])
350+ x2m_model_id = x2m_model_ids and x2m_model_ids[0] or False
351+ assert x2m_model_id, _("'%s' Model does not exist..." %(field_obj._obj))
352+ x2m_model = model_pool.browse(cr, uid, x2m_model_id)
353+ x2m_old_values.update(inline_process_old_data(resource[field], field_obj._obj, x2m_model))
354+ ret_val = self.get_value_text(cr, uid, pool, resource_pool, method, field, resource[field], False)
355+ _old_value[field] = resource[field]
356+ _old_values_text[field] = ret_val and ret_val or resource[field]
357+ _old_values[resource_id] = {'text':_old_values_text, 'value': _old_value}
358+ if model_id:
359+ _old_values[resource_id].update({'model_id':model_id})
360+ return _old_values
361+ old_values.update(inline_process_old_data(res_ids, model.model))
362 res = fct_src(cr, uid_orig, model.model, method, *args)
363-
364 if res_ids:
365- for resource in resource_pool.read(cr, uid, res_ids):
366- resource_id = resource['id']
367- if 'id' in resource:
368- del resource['id']
369- vals = {
370- "method": method,
371- "object_id": model.id,
372- "user_id": uid_orig,
373- "res_id": resource_id,
374- }
375-
376-
377- log_id = log_pool.create(cr, uid, vals)
378- lines = []
379- for field in resource.keys():
380- line = {
381- 'name': field,
382- 'new_value': resource[field],
383- 'old_value': old_values[resource_id]['value'][field],
384- 'new_value_text': self.get_value_text(cr, uid, field, resource[field], model),
385- 'old_value_text': old_values[resource_id]['text'][field]
386- }
387- lines.append(line)
388-
389- self.create_log_line(cr, uid, log_id, model, lines)
390+ def inline_process_new_data(res_ids, model, dict_to_use={}):
391+ resource_pool = pool.get(model.model)
392+ resource_data = resource_pool.read(cr, uid, res_ids)
393+ vals = {'method': method,'object_id': model.id,'user_id': uid_orig }
394+ for resource in resource_data:
395+ resource_id = resource['id']
396+ vals.update({'res_id': resource_id})
397+ log_id = log_pool.create(cr, uid, vals)
398+ lines = []
399+ for field in resource.keys():
400+ if field in ('__last_update', 'id'):continue
401+ field_obj = (resource_pool._all_columns.get(field)).column
402+ if field_obj._type in ('one2many','many2many'):
403+ if self.check_rules(cr, uid, field_obj._obj, method):
404+ x2m_model_ids = model_pool.search(cr, 1, [('model', '=', field_obj._obj)])
405+ x2m_model_id = x2m_model_ids and x2m_model_ids[0] or False
406+ assert x2m_model_id, _("'%s' Model does not exist..." %(field_obj._obj))
407+ x2m_model = model_pool.browse(cr, uid, x2m_model_id)
408+ x2m_old_values.update(inline_process_old_data(resource[field], field_obj._obj, x2m_model))
409+ inline_process_new_data(resource[field], x2m_model, x2m_old_values)
410+ ret_val = self.get_value_text(cr, uid, pool, resource_pool, method, field, resource[field], False)
411+ line = {
412+ 'name': field,
413+ 'new_value': resource[field],
414+ 'old_value': resource_id in dict_to_use and dict_to_use[resource_id]['value'].get(field),
415+ 'new_value_text': ret_val and ret_val or resource[field],
416+ 'old_value_text': resource_id in dict_to_use and dict_to_use[resource_id]['text'].get(field)
417+ }
418+ lines.append(line)
419+ self.create_log_line(cr, uid, log_id, model, lines)
420+ return True
421+ inline_process_new_data(res_ids, model, old_values)
422 return res
423 return True
424-
425+
426 def check_rules(self, cr, uid, model, method):
427 pool = pooler.get_pool(cr.dbname)
428 # Check if auditrails is installed for that db and then if one rule match
429@@ -460,18 +396,14 @@
430 def execute_cr(self, cr, uid, model, method, *args, **kw):
431 fct_src = super(audittrail_objects_proxy, self).execute_cr
432 if self.check_rules(cr,uid,model,method):
433- res = self.log_fct(cr, uid, model, method, fct_src, *args)
434- else:
435- res = fct_src(cr, uid, model, method, *args)
436- return res
437+ return self.log_fct(cr, uid, model, method, fct_src, *args)
438+ return fct_src(cr, uid, model, method, *args)
439
440 def exec_workflow_cr(self, cr, uid, model, method, *args, **argv):
441 fct_src = super(audittrail_objects_proxy, self).exec_workflow_cr
442 if self.check_rules(cr,uid,model,'workflow'):
443- res = self.log_fct(cr, uid, model, method, fct_src, *args)
444- else:
445- res = fct_src(cr, uid, model, method, *args)
446- return res
447+ return self.log_fct(cr, uid, model, method, fct_src, *args)
448+ return fct_src(cr, uid, model, method, *args)
449
450 audittrail_objects_proxy()
451

Subscribers

People subscribed via source and target branches

to all changes: