Merge lp:~openerp-dev/openobject-addons/trunk-bug-832635-nch into lp:openobject-addons
- trunk-bug-832635-nch
- Merge into trunk
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 |
Related bugs: |
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.
Commit message
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 : | # |
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_
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 |
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.