1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as
9 # published by the Free Software Foundation, either version 3 of the
10 # License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ##############################################################################
22 from xml.dom import minidom
23 from osv.osv import osv_pool
24 from osv import fields,osv
29 class recording_objects_proxy(osv_pool):
30 def execute(self, *args, **argv):
31 if args[3] == 'create':
32 _old_args = args[4].copy()
33 elif args[3] == 'write':
34 _old_args = args[5].copy()
35 elif len(args) >= 5 and isinstance(args[4], dict):
36 _old_args = args[4].copy()
37 elif len(args) > 5 and args[3] != 'write' and isinstance(args[5], dict):
38 _old_args = args[5].copy()
41 res = super(recording_objects_proxy, self).execute(*args, **argv)
42 pool = pooler.get_pool(args[0])
43 mod = pool.get('ir.module.record')
45 if mod and mod.recording:
46 if args[3] not in ('default_get','read','fields_view_get','fields_get','search','search_count','name_search','name_get','get','request_get', 'get_sc', 'unlink'):
47 if _old_args is not None:
48 if args[3] != 'write' and args[3] != 'create' and len(args) > 5 and isinstance(args[5], dict):
52 mod.recording_data.append(('osv_memory_action', args, argv ,None))
54 if args[3] == 'create':
55 args[4].update(_old_args)
56 elif args[3] == 'write':
57 args[5].update(_old_args)
58 mod.recording_data.append(('query', args, argv,res))
61 def exec_workflow(self, *args, **argv):
62 res = super(recording_objects_proxy, self).exec_workflow(*args, **argv)
63 pool = pooler.get_pool(args[0])
64 mod = pool.get('ir.module.record')
65 if mod and mod.recording:
66 mod.recording_data.append(('workflow', args, argv))
69 recording_objects_proxy()
71 class xElement(minidom.Element):
72 """dom.Element with compact print
73 The Element in minidom has a problem: if printed, adds whitespace
74 around the text nodes. The standard will not ignore that whitespace.
75 This class simply prints the contained nodes in their compact form, w/o
78 def writexml(self, writer, indent="", addindent="", newl=""):
80 minidom.Element.writexml(self, writer, indent='', addindent='', newl='')
83 def doc_createXElement(xdoc, tagName):
85 e.ownerDocument = xdoc
89 from tools import yaml_tag # This import is not unused! Do not remove!
90 # Please do not override yaml_tag here: modify it in server bin/tools/yaml_tag.py
92 class base_module_record(osv.osv):
93 _name = "ir.module.record"
97 def __init__(self, *args, **kwargs):
99 self.recording_data = []
101 super(base_module_record, self).__init__(*args, **kwargs)
104 def _create_id(self, cr, uid, model, data):
108 name = filter(lambda x: x in string.letters, (data.get('name','') or '').lower())
111 # name=data.get('name','') or ''.lower()
112 val = model.replace('.','_')+'_'+ name + str(i)
114 if val not in self.ids.values():
118 def _get_id(self, cr, uid, model, id):
119 if type(id)==type(()):
121 if (model,id) in self.ids:
122 res_id = self.ids[(model,id)]
124 dt = self.pool.get('ir.model.data')
125 dtids = dt.search(cr, uid, [('model','=',model), ('res_id','=',id)])
128 obj = dt.browse(cr, uid, dtids[0])
129 self.depends[obj.module] = True
130 return obj.module+'.'+obj.name, obj.noupdate
132 def _create_record(self, cr, uid, doc, model, data, record_id, noupdate=False):
133 data_pool = self.pool.get('ir.model.data')
134 model_pool = self.pool.get(model)
136 record = doc.createElement('record')
137 record.setAttribute("id", record_id)
138 record.setAttribute("model", model)
139 record_list = [record]
141 lids = data_pool.search(cr, uid, [('model','=',model)])
142 res = data_pool.read(cr, uid, lids[:1], ['module'])
144 self.depends[res[0]['module']]=True
145 fields = model_pool.fields_get(cr, uid)
146 for key,val in data.items():
147 if not (val or (fields[key]['type']=='boolean')):
149 if (fields[key]['type'] in ('integer','float') or
150 fields[key]['type'] == 'selection' and isinstance(val, int)):
151 field = doc.createElement('field')
152 field.setAttribute("name", key)
153 field.setAttribute("eval", val and str(val) or 'False' )
154 record.appendChild(field)
155 elif fields[key]['type'] in ('boolean',):
156 field = doc.createElement('field')
157 field.setAttribute("name", key)
158 field.setAttribute("eval", val and '1' or '0' )
159 record.appendChild(field)
160 elif fields[key]['type'] in ('many2one',):
161 field = doc.createElement('field')
162 field.setAttribute("name", key)
163 if type(val) in (type(''),type(u'')):
166 id,update = self._get_id(cr, uid, fields[key]['relation'], val)
167 noupdate = noupdate or update
169 relation_pool = self.pool.get(fields[key]['relation'])
171 field.setAttribute("model", fields[key]['relation'])
172 fld_nm = relation_pool._rec_name
173 name = relation_pool.read(cr, uid, val,[fld_nm])[fld_nm] or False
174 field.setAttribute("search", str([(str(fld_nm) ,'=', name)]))
176 field.setAttribute("ref", id)
177 record.appendChild(field)
178 elif fields[key]['type'] in ('one2many',):
179 for valitem in (val or []):
180 if valitem[0] in (0,1):
181 if key in model_pool._columns:
182 model_pool._columns[key]._fields_id
184 model_pool._inherit_fields[key][2]._fields_id
186 newid = self._create_id(cr, uid, fields[key]['relation'], valitem[2])
189 newid,update = self._get_id(cr, uid, fields[key]['relation'], valitem[1])
191 newid = self._create_id(cr, uid, fields[key]['relation'], valitem[2])
193 self.ids[(fields[key]['relation'], valitem[1])] = newid
195 childrecord, update = self._create_record(cr, uid, doc, fields[key]['relation'],valitem[2], newid)
196 noupdate = noupdate or update
197 record_list += childrecord
200 elif fields[key]['type'] in ('many2many',):
202 for valitem in (val or []):
204 for id2 in valitem[2]:
205 id,update = self._get_id(cr, uid, fields[key]['relation'], id2)
206 self.ids[(fields[key]['relation'],id2)] = id
207 noupdate = noupdate or update
209 field = doc.createElement('field')
210 field.setAttribute("name", key)
211 field.setAttribute("eval", "[(6,0,["+','.join(map(lambda x: "ref('%s')" % (x,), res))+'])]')
212 record.appendChild(field)
214 field = doc_createXElement(doc, 'field')
215 field.setAttribute("name", key)
216 field.appendChild(doc.createTextNode(val))
217 record.appendChild(field)
219 return record_list, noupdate
221 def _create_yaml_record(self, cr, uid, model, data, record_id):
222 record={'model': model, 'id': str(record_id)}
224 model_pool = self.pool.get(model)
225 data_pool = self.pool.get('ir.model.data')
226 lids = data_pool.search(cr, uid, [('model','=',model)])
228 res = data_pool.read(cr, uid, lids[:1], ['module'])
231 self.depends[res[0]['module']]=True
232 fields = model_pool.fields_get(cr, uid)
235 defaults[model] = model_pool.default_get(cr, uid, data)
238 for key,val in data.items():
239 if ((key in defaults[model]) and (val == defaults[model][key])) and not(fields[key].get('required',False)):
241 if fields[key]['type'] in ('integer','float'):
245 elif not (val or (fields[key]['type']=='function')):
247 elif fields[key]['type'] in ('boolean',):
251 elif fields[key]['type'] in ('many2one',):
252 if type(val) in (type(''), type(u'')):
255 id, update = self._get_id(cr, uid, fields[key]['relation'], val)
257 elif fields[key]['type'] in ('one2many',):
259 for valitem in (val or []):
260 if valitem[0] in (0,1):
261 if key in model_pool._columns:
262 fname = model_pool._columns[key]._fields_id
264 fname = model_pool._inherit_fields[key][2]._fields_id
265 del valitem[2][fname] #delete parent_field from child's fields list
267 childrecord = self._create_yaml_record(cr, uid, fields[key]['relation'],valitem[2], None)
268 items[0].append(childrecord['attrs'])
270 elif fields[key]['type'] in ('many2many',):
271 if (key in defaults[model]) and (val[0][2] == defaults[model][key]):
274 for valitem in (val or []):
276 for id2 in valitem[2]:
277 id,update = self._get_id(cr, uid, fields[key]['relation'], id2)
278 self.ids[(fields[key]['relation'],id2)] = id
287 attrs[key]=tools.ustr(val)
288 attrs[key]=attrs[key].replace('"','\'')
289 record['attrs'] = attrs
292 def get_copy_data(self, cr, uid, model, id, result):
294 obj=self.pool.get(model)
295 data=obj.read(cr, uid,[id])
296 if type(data)==type([]):
302 mod_fields = obj.fields_get(cr, uid)
303 for f in filter(lambda a: isinstance(obj._columns[a], fields.function)\
304 and (not obj._columns[a].store),obj._columns):
307 for key,val in data.items():
308 if result.has_key(key):
310 if mod_fields[key]['type'] == 'many2one':
311 if type(data[key])==type(True) or type(data[key])==type(1):
312 result[key]=data[key]
316 result[key]=data[key][0]
318 elif mod_fields[key]['type'] in ('one2many',):
319 # continue # due to this start stop recording will not record one2many field
320 rel = mod_fields[key]['relation']
323 for rel_id in data[key]:
325 res.append(self.get_copy_data(cr, uid,rel,rel_id,{}))
329 result[key]=data[key]
331 elif mod_fields[key]['type'] == 'many2many':
332 result[key]=[(6,0,data[key])]
335 result[key]=data[key]
336 for k,v in obj._inherits.items():
340 def _create_function(self, cr, uid, doc, model, name, record_id):
341 record = doc.createElement('function')
342 record.setAttribute("name", name)
343 record.setAttribute("model", model)
344 record_list = [record]
346 value = doc.createElement('value')
347 value.setAttribute('eval', '[ref(\'%s\')]' % (record_id, ))
348 value.setAttribute('model', model)
350 record.appendChild(value)
351 return record_list, False
353 def _generate_object_xml(self, cr, uid, rec, recv, doc, result=None):
358 id,update = self._get_id(cr, uid, rec[2], id)
359 noupdate = noupdate or update
362 record,update = self._create_record(cr, uid, doc, rec[2], rec[5], id)
363 noupdate = noupdate or update
364 record_list += record
366 elif rec[4] in ('menu_create',):
368 id,update = self._get_id(cr, uid, rec[3], id)
369 noupdate = noupdate or update
372 record,update = self._create_function(cr, uid, doc, rec[3], rec[4], id)
373 noupdate = noupdate or update
374 record_list += record
376 elif rec[3]=='create':
377 id = self._create_id(cr, uid, rec[2],rec[4])
378 record,noupdate = self._create_record(cr, uid, doc, rec[2], rec[4], id)
379 self.ids[(rec[2], result)] = id
380 record_list += record
383 data=self.get_copy_data(cr,uid,rec[2],rec[4],rec[5])
384 copy_rec=(rec[0],rec[1],rec[2],rec[3],rec[4],data,rec[5])
386 rec_data=[(self.recording_data[0][0],rec,self.recording_data[0][2],self.recording_data[0][3])]
387 self.recording_data=rec_data
388 id = self._create_id(cr, uid, rec[2],rec[5])
389 record,noupdate = self._create_record(cr, uid, doc, rec[2], rec[5], id)
390 self.ids[(rec[2], result)] = id
391 record_list += record
393 return record_list,noupdate
395 def _generate_object_yaml(self, cr, uid, rec, result=None):
396 if self.mode=="create":
397 yml_id = self._create_id(cr, uid, rec[2],rec[4])
398 self.ids[(rec[2], result)] = yml_id
399 record = self._create_yaml_record(cr, uid, rec[2], rec[4], yml_id)
401 if self.mode=="workflow":
402 id,update = self._get_id(cr, uid, rec[2], rec[4])
404 data['model'] = rec[2]
405 data['action'] = rec[3]
408 if self.mode=="write":
409 id,update = self._get_id(cr, uid, rec[2],rec[4][0])
410 record = self._create_yaml_record(cr, uid, rec[2], rec[5], id)
412 data=self.get_copy_data(cr,uid,rec[2],rec[4],rec[5])
413 copy_rec=(rec[0],rec[1],rec[2],rec[3],rec[4],data,rec[5])
415 rec_data=[(self.recording_data[0][0],rec,self.recording_data[0][2],self.recording_data[0][3])]
416 self.recording_data=rec_data
417 id = self._create_id(cr, uid, rec[2],rec[5])
418 record = self._create_yaml_record(cr, uid, str(rec[2]), rec[5], id)
419 self.ids[(rec[2], result)] = id
422 def _generate_function_yaml(self, cr, uid, args):
423 db, uid, model, action, ids, context = args
424 temp_context = context.copy()
425 active_id = temp_context['active_id']
426 active_model = temp_context['active_model']
427 active_id, update = self._get_id(cr, uid, active_model, active_id)
430 rec_id, noupdate = self._get_id(cr, uid, model, ids[0])
431 temp_context['active_id'] = "ref('%s')"%unicode(active_id)
432 temp_context['active_ids'][0] = "ref('%s')"%str(active_id)
434 function['model'] = model
435 function['action'] = action
436 attrs = "self.%s(cr, uid, [ref('%s')], {" %(action, rec_id, )
437 for k, v in temp_context.iteritems():
438 if isinstance(v, str):
439 f= "'"+k+"': "+"'%s'"%v + ", "
441 v=str(v).replace('"', '')
442 f= "'"+k+"': "+"%s"%v + ", "
444 attrs=str(attrs)+'})'
445 function['attrs'] = attrs
448 def _generate_assert_xml(self, rec, doc):
451 def generate_xml(self, cr, uid):
452 # Create the minidom document
453 if len(self.recording_data):
455 doc = minidom.Document()
456 terp = doc.createElement("openerp")
457 doc.appendChild(terp)
458 for rec in self.recording_data:
459 if rec[0]=='workflow':
460 rec_id,noupdate = self._get_id(cr, uid, rec[1][2], rec[1][4])
463 data = doc.createElement("data")
464 terp.appendChild(data)
465 wkf = doc.createElement('workflow')
466 data.appendChild(wkf)
467 wkf.setAttribute("model", rec[1][2])
468 wkf.setAttribute("action", rec[1][3])
470 data.setAttribute("noupdate", "1")
471 wkf.setAttribute("ref", rec_id)
473 res_list,noupdate = self._generate_object_xml(cr, uid, rec[1], rec[2], doc, rec[3])
474 data = doc.createElement("data")
476 data.setAttribute("noupdate", "1")
478 terp.appendChild(data)
480 data.appendChild(res)
481 elif rec[0]=='assert':
483 return doc.toprettyxml(indent="\t").encode('utf-8')
485 def generate_yaml(self, cr, uid):
487 if len(self.recording_data):
490 for rec in self.recording_data:
491 if rec[1][3] == 'create':
493 elif rec[1][3] == 'write':
495 elif rec[1][3] == 'copy':
497 elif rec[0] == 'workflow':
499 elif rec[0] == 'osv_memory_action':
500 self.mode='osv_memory_action'
503 if self.mode == "workflow":
504 record = self._generate_object_yaml(cr, uid, rec[1],rec[0])
505 yaml_file += "!comment Performing a workflow action %s on module %s"%(record['action'], record['model']) + '''\n'''
506 object = yaml.load(unicode('''\n !workflow %s \n'''%record,'iso-8859-1'))
507 yaml_file += str(object) + '''\n\n'''
508 elif self.mode == 'osv_memory_action':
509 osv_action = self._generate_function_yaml(cr, uid, rec[1])
510 yaml_file += "!comment Performing an osv_memory action %s on module %s"%(osv_action['action'], osv_action['model']) + '''\n'''
511 osv_action = yaml.load(unicode('''\n !python %s \n'''%osv_action,'iso-8859-1'))
512 yaml_file += str(osv_action) + '''\n'''
513 attrs = yaml.dump(osv_action.attrs, default_flow_style=False)
514 attrs = attrs.replace("''", '"')
515 attrs = attrs.replace("'", '')
516 yaml_file += attrs + '''\n\n'''
518 record = self._generate_object_yaml(cr, uid, rec[1], rec[3])
519 if self.mode == "create" or self.mode == "copy":
520 yaml_file += "!comment Creating a %s record"%(record['model']) + '''\n'''
522 yaml_file += "!comment Modifying a %s record"%(record['model']) + '''\n'''
523 object = yaml.load(unicode('''\n !record %s \n'''%record,'iso-8859-1'))
524 yaml_file += str(object) + '''\n'''
525 attrs = yaml.dump(object.attrs, default_flow_style=False)
526 yaml_file += attrs + '''\n\n'''
529 for line in yaml_file.split('\n'):
530 line=line.replace("''","'")
531 if (line.find('!record') == 0) or (line.find('!workflow') == 0) or (line.find('!python') == 0):
532 line = "- \n" + " " + line
533 elif line.find('!comment') == 0:
534 line=line.replace('!comment','- \n ')
535 elif line.find('- -') != -1:
536 line=line.replace('- -',' -')
540 yaml_result += line + '''\n'''
545 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: