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
=== modified file 'audittrail/audittrail.py'
--- audittrail/audittrail.py 2011-09-29 22:23:47 +0000
+++ audittrail/audittrail.py 2011-10-03 05:50:30 +0000
@@ -43,22 +43,17 @@
43 "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"),43 "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"),
44 "log_action": fields.boolean("Log Action",help="Select this if you want to keep track of actions on the object of this rule"),44 "log_action": fields.boolean("Log Action",help="Select this if you want to keep track of actions on the object of this rule"),
45 "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"),45 "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"),
46 "state": fields.selection((("draft", "Draft"),46 "state": fields.selection((("draft", "Draft"), ("subscribed", "Subscribed")), "State", required=True),
47 ("subscribed", "Subscribed")),
48 "State", required=True),
49 "action_id": fields.many2one('ir.actions.act_window', "Action ID"),47 "action_id": fields.many2one('ir.actions.act_window', "Action ID"),
50
51 }48 }
52
53 _defaults = {49 _defaults = {
54 'state': lambda *a: 'draft',50 'state': lambda *a: 'draft',
55 'log_create': lambda *a: 1,51 'log_create': lambda *a: 1,
56 'log_unlink': lambda *a: 1,52 'log_unlink': lambda *a: 1,
57 'log_write': lambda *a: 1,53 'log_write': lambda *a: 1,
58 }54 }
59
60 _sql_constraints = [55 _sql_constraints = [
61 ('model_uniq', 'unique (object_id)', """There is a rule defined on this object\n You cannot define another one the same object!""")56 ('model_uniq', 'unique (object_id)', """There is a rule defined on this object\n You can not define other on the same!""")
62 ]57 ]
63 __functions = {}58 __functions = {}
6459
@@ -175,58 +170,10 @@
175 'field_description': fields.char('Field Description', size=64),170 'field_description': fields.char('Field Description', size=64),
176 }171 }
177172
173
178class audittrail_objects_proxy(object_proxy):174class audittrail_objects_proxy(object_proxy):
179 """ Uses Object proxy for auditing changes on object of subscribed Rules"""175 """ Uses Object proxy for auditing changes on object of subscribed Rules"""
180176
181 def get_value_text(self, cr, uid, field_name, values, model, context=None):
182 """
183 Gets textual values for the fields
184 e.g.: For field of type many2one it gives its name value instead of id
185
186 @param cr: the current row, from the database cursor,
187 @param uid: the current user’s ID for security checks,
188 @param field_name: List of fields for text values
189 @param values: Values for field to be converted into textual values
190 @return: values: List of textual values for given fields
191 """
192 if not context:
193 context = {}
194 if field_name in('__last_update','id'):
195 return values
196 pool = pooler.get_pool(cr.dbname)
197 field_pool = pool.get('ir.model.fields')
198 model_pool = pool.get('ir.model')
199 obj_pool = pool.get(model.model)
200 if obj_pool._inherits:
201 inherits_ids = model_pool.search(cr, uid, [('model', '=', obj_pool._inherits.keys()[0])])
202 field_ids = field_pool.search(cr, uid, [('name', '=', field_name), ('model_id', 'in', (model.id, inherits_ids[0]))])
203 else:
204 field_ids = field_pool.search(cr, uid, [('name', '=', field_name), ('model_id', '=', model.id)])
205 field_id = field_ids and field_ids[0] or False
206 assert field_id, _("'%s' field does not exist in '%s' model" %(field_name, model.model))
207
208 field = field_pool.read(cr, uid, field_id)
209 relation_model = field['relation']
210 relation_model_pool = relation_model and pool.get(relation_model) or False
211
212 if field['ttype'] == 'many2one':
213 res = False
214 relation_id = False
215 if values and type(values) == tuple:
216 relation_id = values[0]
217 if relation_id and relation_model_pool:
218 relation_model_object = relation_model_pool.read(cr, uid, relation_id, [relation_model_pool._rec_name])
219 res = relation_model_object[relation_model_pool._rec_name]
220 return res
221
222 elif field['ttype'] in ('many2many','one2many'):
223 res = []
224 for relation_model_object in relation_model_pool.read(cr, uid, values, [relation_model_pool._rec_name]):
225 res.append(relation_model_object[relation_model_pool._rec_name])
226 return res
227
228 return values
229
230 def create_log_line(self, cr, uid, log_id, model, lines=[]):177 def create_log_line(self, cr, uid, log_id, model, lines=[]):
231 """178 """
232 Creates lines for changed fields with its old and new values179 Creates lines for changed fields with its old and new values
@@ -241,48 +188,70 @@
241 model_pool = pool.get('ir.model')188 model_pool = pool.get('ir.model')
242 field_pool = pool.get('ir.model.fields')189 field_pool = pool.get('ir.model.fields')
243 log_line_pool = pool.get('audittrail.log.line')190 log_line_pool = pool.get('audittrail.log.line')
244 #start Loop
245 for line in lines:191 for line in lines:
246 if line['name'] in('__last_update','id'):192 field_obj = obj_pool._all_columns.get(line['name'])
247 continue193 assert field_obj, _("'%s' field does not exist in '%s' model" %(line['name'], model.model))
194 field_obj = field_obj.column
195 old_value = line.get('old_value', '')
196 new_value = line.get('new_value', '')
197 old_value_text = line.get('old_value_text', '')
198 new_value_text = line.get('new_value_text', '')
199 search_models = [ model.id ]
248 if obj_pool._inherits:200 if obj_pool._inherits:
249 inherits_ids = model_pool.search(cr, uid, [('model', '=', obj_pool._inherits.keys()[0])])201 search_models += model_pool.search(cr, uid, [('model', 'in', obj_pool._inherits.keys())])
250 field_ids = field_pool.search(cr, uid, [('name', '=', line['name']), ('model_id', 'in', (model.id, inherits_ids[0]))])202 field_id = field_pool.search(cr, uid, [('name', '=', line['name']), ('model_id', 'in', search_models)])
251 else:
252 field_ids = field_pool.search(cr, uid, [('name', '=', line['name']), ('model_id', '=', model.id)])
253 field_id = field_ids and field_ids[0] or False
254 assert field_id, _("'%s' field does not exist in '%s' model" %(line['name'], model.model))
255
256 field = field_pool.read(cr, uid, field_id)
257 old_value = 'old_value' in line and line['old_value'] or ''
258 new_value = 'new_value' in line and line['new_value'] or ''
259 old_value_text = 'old_value_text' in line and line['old_value_text'] or ''
260 new_value_text = 'new_value_text' in line and line['new_value_text'] or ''
261
262 if old_value_text == new_value_text:203 if old_value_text == new_value_text:
263 continue204 continue
264 if field['ttype'] == 'many2one':205 if field_obj._type == 'many2one':
265 if type(old_value) == tuple:206 old_value = old_value and old_value[0] or old_value
266 old_value = old_value[0]207 new_value = new_value and new_value[0] or new_value
267 if type(new_value) == tuple:
268 new_value = new_value[0]
269 vals = {208 vals = {
270 "log_id": log_id,209 "log_id": log_id,
271 "field_id": field_id,210 "field_id": field_id and field_id[0] or False,
272 "old_value": old_value,211 "old_value": old_value,
273 "new_value": new_value,212 "new_value": new_value,
274 "old_value_text": old_value_text,213 "old_value_text": old_value_text,
275 "new_value_text": new_value_text,214 "new_value_text": new_value_text,
276 "field_description": field['field_description']215 "field_description": field_obj.string
277 }216 }
278 line_id = log_line_pool.create(cr, uid, vals)217 line_id = log_line_pool.create(cr, uid, vals)
279 cr.commit()
280 #End Loop
281 return True218 return True
282219
220 def get_value_text(self, cr, uid, pool, resource_pool, method, field, value, recursive=True):
221 field_obj = (resource_pool._all_columns.get(field)).column
222 if field_obj._type in ('one2many','many2many'):
223 if recursive:
224 self.log_fct(cr, uid, field_obj._obj, method, None, value, 'child_relation_log')
225 data = pool.get(field_obj._obj).name_get(cr, uid, value)
226 return map(lambda x:x[1], data)
227 elif field_obj._type == 'many2one':
228 return value and value[1] or value
229 return False
230
231 def start_log_process(self, cr, user_id, model, method, resource_data, pool, resource_pool):
232 key1 = '%s_value'%(method == 'create' and 'new' or 'old')
233 key2 = '%s_value_text'%(method == 'create' and 'new' or 'old')
234 uid = 1
235 vals = { 'method': method, 'object_id': model.id,'user_id': user_id}
236 for resource, fields in resource_data.iteritems():
237 vals.update({'res_id': resource})
238 log_id = pool.get('audittrail.log').create(cr, uid, vals)
239 lines = []
240 for field_key, value in fields.iteritems():
241 if field_key in ('__last_update', 'id'):continue
242 ret_val = self.get_value_text(cr, uid, pool, resource_pool, method, field_key, value, method != 'read')
243 line = {
244 'name': field_key,
245 key1: value,
246 key2: ret_val and ret_val or value
247 }
248 lines.append(line)
249 self.create_log_line(cr, uid, log_id, model, lines)
250 return True
251
283 def log_fct(self, cr, uid, model, method, fct_src, *args):252 def log_fct(self, cr, uid, model, method, fct_src, *args):
284 """253 """
285 Logging function: This function is performs logging oprations according to method254 Logging function: This function is performs logging operations according to method
286 @param db: the current database255 @param db: the current database
287 @param uid: the current user’s ID for security checks,256 @param uid: the current user’s ID for security checks,
288 @param object: Object who's values are being changed257 @param object: Object who's values are being changed
@@ -293,99 +262,43 @@
293 """262 """
294 uid_orig = uid263 uid_orig = uid
295 uid = 1264 uid = 1
296 res2 = args
297 pool = pooler.get_pool(cr.dbname)265 pool = pooler.get_pool(cr.dbname)
298 resource_pool = pool.get(model)266 resource_pool = pool.get(model)
267 model_pool = pool.get('ir.model')
299 log_pool = pool.get('audittrail.log')268 log_pool = pool.get('audittrail.log')
300 model_pool = pool.get('ir.model')269 model_ids = model_pool.search(cr, 1, [('model', '=', model)])
301
302 model_ids = model_pool.search(cr, uid, [('model', '=', model)])
303 model_id = model_ids and model_ids[0] or False270 model_id = model_ids and model_ids[0] or False
304 assert model_id, _("'%s' Model does not exist..." %(model))271 assert model_id, _("'%s' Model does not exist..." %(model))
305 model = model_pool.browse(cr, uid, model_id)272 model = model_pool.browse(cr, uid, model_id)
306273 relational_table_log = args and args[-1] == 'child_relation_log' or False
307 if method in ('create'):274 if method == 'create':
308 res_id = fct_src(cr, uid_orig, model.model, method, *args)275 resource_data = {}
309 resource = resource_pool.read(cr, uid, res_id, args[0].keys())276 fields_to_read = []
310 vals = {277 if relational_table_log:
311 "method": method,278 res_id = args[0]
312 "object_id": model.id,279 else:
313 "user_id": uid_orig,280 res_id = fct_src(cr, uid_orig, model.model, method, *args)
314 "res_id": resource['id'],281 fields_to_read = args[0].keys()
315 }282 if res_id:
316 if 'id' in resource:283 resource = resource_pool.read(cr, uid, res_id, fields_to_read)
317 del resource['id']284 if not isinstance(resource,list):
318 log_id = log_pool.create(cr, uid, vals)285 resource = [resource]
319 lines = []286 map(lambda x: resource_data.setdefault(x['id'], x), resource)
320 for field in resource:287 self.start_log_process(cr, uid_orig, model, method, resource_data, pool, resource_pool)
321 line = {
322 'name': field,
323 'new_value': resource[field],
324 'new_value_text': self.get_value_text(cr, uid, field, resource[field], model)
325 }
326 lines.append(line)
327 self.create_log_line(cr, uid, log_id, model, lines)
328
329 return res_id288 return res_id
330289
331 elif method in ('read'):290 elif method in('read', 'unlink'):
332 res_ids = args[0]291 res_ids = args[0]
333 old_values = {}292 old_values = {}
334 res = fct_src(cr, uid_orig, model.model, method, *args)293 if method == 'read':
335 if type(res) == list:294 res = fct_src(cr, uid_orig, model.model, method, *args)
336 for v in res:295 map(lambda x: old_values.setdefault(x['id'], x), res)
337 old_values[v['id']] = v
338 else:296 else:
339 old_values[res['id']] = res297 res = resource_pool.read(cr, uid, res_ids)
340 for res_id in old_values:298 map(lambda x:old_values.setdefault(x['id'], x), res)
341 vals = {299 self.start_log_process(cr, uid_orig, model, method, old_values, pool, resource_pool)
342 "method": method,300 if not relational_table_log and method == 'unlink':
343 "object_id": model.id,301 res = fct_src(cr, uid_orig, model.model, method, *args)
344 "user_id": uid_orig,
345 "res_id": res_id,
346
347 }
348 log_id = log_pool.create(cr, uid, vals)
349 lines = []
350 for field in old_values[res_id]:
351 line = {
352 'name': field,
353 'old_value': old_values[res_id][field],
354 'old_value_text': self.get_value_text(cr, uid, field, old_values[res_id][field], model)
355 }
356 lines.append(line)
357
358 self.create_log_line(cr, uid, log_id, model, lines)
359 return res
360
361 elif method in ('unlink'):
362 res_ids = args[0]
363 old_values = {}
364 for res_id in res_ids:
365 old_values[res_id] = resource_pool.read(cr, uid, res_id)
366
367 for res_id in res_ids:
368 vals = {
369 "method": method,
370 "object_id": model.id,
371 "user_id": uid_orig,
372 "res_id": res_id,
373
374 }
375 log_id = log_pool.create(cr, uid, vals)
376 lines = []
377 for field in old_values[res_id]:
378 if field in ('id'):
379 continue
380 line = {
381 'name': field,
382 'old_value': old_values[res_id][field],
383 'old_value_text': self.get_value_text(cr, uid, field, old_values[res_id][field], model)
384 }
385 lines.append(line)
386
387 self.create_log_line(cr, uid, log_id, model, lines)
388 res = fct_src(cr, uid_orig, model.model, method, *args)
389 return res302 return res
390 else:303 else:
391 res_ids = []304 res_ids = []
@@ -394,53 +307,76 @@
394 res_ids = args[0]307 res_ids = args[0]
395 old_values = {}308 old_values = {}
396 fields = []309 fields = []
397 if len(args)>1 and type(args[1]) == dict:310 if len(args) > 1 and isinstance(args[1], dict):
398 fields = args[1].keys()311 fields = args[1].keys()
399 if type(res_ids) in (long, int):312 if isinstance(res_ids, (long, int)):
400 res_ids = [res_ids]313 res_ids = [res_ids]
401 if res_ids:314 if res_ids:
402 for resource in resource_pool.read(cr, uid, res_ids):315 x2m_old_values = {}
403 resource_id = resource['id']316 old_values = {}
404 if 'id' in resource:317 def inline_process_old_data(res_ids, model, model_id=False):
405 del resource['id']318 resource_pool = pool.get(model)
406 old_values_text = {}319 resource_data = resource_pool.read(cr, uid, res_ids)
407 old_value = {}320 _old_values = {}
408 for field in resource.keys():321 for resource in resource_data:
409 old_value[field] = resource[field]322 _old_values_text = {}
410 old_values_text[field] = self.get_value_text(cr, uid, field, resource[field], model)323 _old_value = {}
411 old_values[resource_id] = {'text':old_values_text, 'value': old_value}324 resource_id = resource['id']
412325 for field in resource.keys():
326 if field in ('__last_update', 'id'):continue
327 field_obj = (resource_pool._all_columns.get(field)).column
328 if field_obj._type in ('one2many','many2many'):
329 if self.check_rules(cr, uid, field_obj._obj, method):
330 x2m_model_ids = model_pool.search(cr, 1, [('model', '=', field_obj._obj)])
331 x2m_model_id = x2m_model_ids and x2m_model_ids[0] or False
332 assert x2m_model_id, _("'%s' Model does not exist..." %(field_obj._obj))
333 x2m_model = model_pool.browse(cr, uid, x2m_model_id)
334 x2m_old_values.update(inline_process_old_data(resource[field], field_obj._obj, x2m_model))
335 ret_val = self.get_value_text(cr, uid, pool, resource_pool, method, field, resource[field], False)
336 _old_value[field] = resource[field]
337 _old_values_text[field] = ret_val and ret_val or resource[field]
338 _old_values[resource_id] = {'text':_old_values_text, 'value': _old_value}
339 if model_id:
340 _old_values[resource_id].update({'model_id':model_id})
341 return _old_values
342 old_values.update(inline_process_old_data(res_ids, model.model))
413 res = fct_src(cr, uid_orig, model.model, method, *args)343 res = fct_src(cr, uid_orig, model.model, method, *args)
414
415 if res_ids:344 if res_ids:
416 for resource in resource_pool.read(cr, uid, res_ids):345 def inline_process_new_data(res_ids, model, dict_to_use={}):
417 resource_id = resource['id']346 resource_pool = pool.get(model.model)
418 if 'id' in resource:347 resource_data = resource_pool.read(cr, uid, res_ids)
419 del resource['id']348 vals = {'method': method,'object_id': model.id,'user_id': uid_orig }
420 vals = {349 for resource in resource_data:
421 "method": method,350 resource_id = resource['id']
422 "object_id": model.id,351 vals.update({'res_id': resource_id})
423 "user_id": uid_orig,352 log_id = log_pool.create(cr, uid, vals)
424 "res_id": resource_id,353 lines = []
425 }354 for field in resource.keys():
426355 if field in ('__last_update', 'id'):continue
427356 field_obj = (resource_pool._all_columns.get(field)).column
428 log_id = log_pool.create(cr, uid, vals)357 if field_obj._type in ('one2many','many2many'):
429 lines = []358 if self.check_rules(cr, uid, field_obj._obj, method):
430 for field in resource.keys():359 x2m_model_ids = model_pool.search(cr, 1, [('model', '=', field_obj._obj)])
431 line = {360 x2m_model_id = x2m_model_ids and x2m_model_ids[0] or False
432 'name': field,361 assert x2m_model_id, _("'%s' Model does not exist..." %(field_obj._obj))
433 'new_value': resource[field],362 x2m_model = model_pool.browse(cr, uid, x2m_model_id)
434 'old_value': old_values[resource_id]['value'][field],363 x2m_old_values.update(inline_process_old_data(resource[field], field_obj._obj, x2m_model))
435 'new_value_text': self.get_value_text(cr, uid, field, resource[field], model),364 inline_process_new_data(resource[field], x2m_model, x2m_old_values)
436 'old_value_text': old_values[resource_id]['text'][field]365 ret_val = self.get_value_text(cr, uid, pool, resource_pool, method, field, resource[field], False)
437 }366 line = {
438 lines.append(line)367 'name': field,
439368 'new_value': resource[field],
440 self.create_log_line(cr, uid, log_id, model, lines)369 'old_value': resource_id in dict_to_use and dict_to_use[resource_id]['value'].get(field),
370 'new_value_text': ret_val and ret_val or resource[field],
371 'old_value_text': resource_id in dict_to_use and dict_to_use[resource_id]['text'].get(field)
372 }
373 lines.append(line)
374 self.create_log_line(cr, uid, log_id, model, lines)
375 return True
376 inline_process_new_data(res_ids, model, old_values)
441 return res377 return res
442 return True378 return True
443379
444 def check_rules(self, cr, uid, model, method):380 def check_rules(self, cr, uid, model, method):
445 pool = pooler.get_pool(cr.dbname)381 pool = pooler.get_pool(cr.dbname)
446 # Check if auditrails is installed for that db and then if one rule match382 # Check if auditrails is installed for that db and then if one rule match
@@ -460,18 +396,14 @@
460 def execute_cr(self, cr, uid, model, method, *args, **kw):396 def execute_cr(self, cr, uid, model, method, *args, **kw):
461 fct_src = super(audittrail_objects_proxy, self).execute_cr397 fct_src = super(audittrail_objects_proxy, self).execute_cr
462 if self.check_rules(cr,uid,model,method):398 if self.check_rules(cr,uid,model,method):
463 res = self.log_fct(cr, uid, model, method, fct_src, *args)399 return self.log_fct(cr, uid, model, method, fct_src, *args)
464 else:400 return fct_src(cr, uid, model, method, *args)
465 res = fct_src(cr, uid, model, method, *args)
466 return res
467401
468 def exec_workflow_cr(self, cr, uid, model, method, *args, **argv):402 def exec_workflow_cr(self, cr, uid, model, method, *args, **argv):
469 fct_src = super(audittrail_objects_proxy, self).exec_workflow_cr403 fct_src = super(audittrail_objects_proxy, self).exec_workflow_cr
470 if self.check_rules(cr,uid,model,'workflow'):404 if self.check_rules(cr,uid,model,'workflow'):
471 res = self.log_fct(cr, uid, model, method, fct_src, *args)405 return self.log_fct(cr, uid, model, method, fct_src, *args)
472 else:406 return fct_src(cr, uid, model, method, *args)
473 res = fct_src(cr, uid, model, method, *args)
474 return res
475407
476audittrail_objects_proxy()408audittrail_objects_proxy()
477409

Subscribers

People subscribed via source and target branches

to all changes: