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
30 #objects_proxy = netsvc.ExportService.getService('object').__class__
31 class recording_objects_proxy(osv_pool):
32 def execute(self, *args, **argv):
33 if len(args) >= 6 and isinstance(args[5], dict):
34 _old_args = args[5].copy()
37 res = super(recording_objects_proxy, self).execute(*args, **argv)
38 pool = pooler.get_pool(args[0])
39 mod = pool.get('ir.module.record')
40 if mod and mod.recording:
41 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'):
42 if _old_args is not None:
43 args[5].update(_old_args)
45 mod.recording_data.append(('query', args, argv,res))
48 def exec_workflow(self, *args, **argv):
49 res = super(recording_objects_proxy, self).exec_workflow(*args, **argv)
50 pool = pooler.get_pool(args[0])
51 mod = pool.get('ir.module.record')
52 if mod and mod.recording:
53 mod.recording_data.append(('workflow', args, argv))
56 recording_objects_proxy()
58 class xElement(minidom.Element):
59 """dom.Element with compact print
60 The Element in minidom has a problem: if printed, adds whitespace
61 around the text nodes. The standard will not ignore that whitespace.
62 This class simply prints the contained nodes in their compact form, w/o
65 def writexml(self, writer, indent="", addindent="", newl=""):
67 minidom.Element.writexml(self, writer, indent='', addindent='', newl='')
70 def doc_createXElement(xdoc, tagName):
72 e.ownerDocument = xdoc
77 class record(yaml.YAMLObject):
79 def __init__(self, model, id=None, attrs={}):
84 return '!record {model: %s, id: %s}:' % (str(self.model,), str(self.id,))
86 class workflow(yaml.YAMLObject):
87 yaml_tag = u'!workflow'
88 def __init__(self, model, action, ref=None):
93 return '!workflow {model: %s, action: %s, ref: %s}' % (str(self.model,), str(self.action,), str(self.ref,))
95 class ref(yaml.YAMLObject):
97 def __init__(self, expr="False"):
100 return 'ref(%s)' % (str(self.expr,))
102 class eval(yaml.YAMLObject):
104 def __init__(self, expr="False"):
107 return 'eval(%s)' % (str(self.expr,))
109 class base_module_record(osv.osv):
110 _name = "ir.module.record"
114 def __init__(self, *args, **kwargs):
116 self.recording_data = []
118 super(base_module_record, self).__init__(*args, **kwargs)
121 def _create_id(self, cr, uid, model, data):
125 name = filter(lambda x: x in string.letters, (data.get('name','') or '').lower())
128 val = model.replace('.','_')+'_'+name+ str(i)
130 if val not in self.ids.values():
134 def _get_id(self, cr, uid, model, id):
135 if type(id)==type(()):
137 if (model,id) in self.ids:
138 return self.ids[(model,id)], False
139 dt = self.pool.get('ir.model.data')
140 dtids = dt.search(cr, uid, [('model','=',model), ('res_id','=',id)])
143 obj = dt.browse(cr, uid, dtids[0])
144 self.depends[obj.module] = True
145 return obj.module+'.'+obj.name, obj.noupdate
147 def _create_record(self, cr, uid, doc, model, data, record_id, noupdate=False):
149 data_pool = self.pool.get('ir.model.data')
150 model_pool = self.pool.get(model)
152 record = doc.createElement('record')
153 record.setAttribute("id", record_id)
154 record.setAttribute("model", model)
155 record_list = [record]
156 lids = data_pool.search(cr, uid, [('model','=',model)])
157 res = data_pool.read(cr, uid, lids[:1], ['module'])
159 self.depends[res[0]['module']]=True
160 fields = model_pool.fields_get(cr, uid)
161 for key,val in data.items():
162 if not (val or (fields[key]['type']=='boolean')):
164 if fields[key]['type'] in ('integer','float'):
165 field = doc.createElement('field')
166 field.setAttribute("name", key)
167 field.setAttribute("eval", val and str(val) or 'False' )
168 record.appendChild(field)
169 elif fields[key]['type'] in ('boolean',):
170 field = doc.createElement('field')
171 field.setAttribute("name", key)
172 field.setAttribute("eval", val and '1' or '0' )
173 record.appendChild(field)
174 elif fields[key]['type'] in ('many2one',):
175 field = doc.createElement('field')
176 field.setAttribute("name", key)
177 if type(val) in (type(''),type(u'')):
180 id,update = self._get_id(cr, uid, fields[key]['relation'], val)
181 noupdate = noupdate or update
183 relation_pool = self.pool.get(fields[key]['relation'])
185 field.setAttribute("model", fields[key]['relation'])
186 fld_nm = relation_pool._rec_name
187 name = relation_pool.read(cr, uid, val,[fld_nm])[fld_nm] or False
188 field.setAttribute("search", str([(str(fld_nm) ,'=', name)]))
190 field.setAttribute("ref", id)
191 record.appendChild(field)
192 elif fields[key]['type'] in ('one2many',):
193 for valitem in (val or []):
194 if valitem[0] in (0,1):
195 if key in model_pool._columns:
196 fname = model_pool._columns[key]._fields_id
198 fname = model_pool._inherit_fields[key][2]._fields_id
199 valitem[2][fname] = record_id
200 newid,update = self._get_id(cr, uid, fields[key]['relation'], valitem[1])
202 newid = self._create_id(cr, uid, fields[key]['relation'], valitem[2])
203 self.ids[(fields[key]['relation'], valitem[1])] = newid
205 childrecord, update = self._create_record(cr, uid, doc, fields[key]['relation'],valitem[2], newid)
206 noupdate = noupdate or update
207 record_list += childrecord
210 elif fields[key]['type'] in ('many2many',):
212 for valitem in (val or []):
214 for id2 in valitem[2]:
215 id,update = self._get_id(cr, uid, fields[key]['relation'], id2)
216 self.ids[(fields[key]['relation'],id2)] = id
217 noupdate = noupdate or update
219 field = doc.createElement('field')
220 field.setAttribute("name", key)
221 field.setAttribute("eval", "[(6,0,["+','.join(map(lambda x: "ref('%s')" % (x,), res))+'])]')
222 record.appendChild(field)
224 field = doc_createXElement(doc, 'field')
225 field.setAttribute("name", key)
226 field.appendChild(doc.createTextNode(val))
227 record.appendChild(field)
229 return record_list, noupdate
231 def _create_yaml_record(self, cr, uid, model, data, record_id):
232 record={'model': model, 'id': str(record_id)}
234 model_pool = self.pool.get(model)
235 data_pool = self.pool.get('ir.model.data')
237 lids = data_pool.search(cr, uid, [('model','=',model)])
238 res = data_pool.read(cr, uid, lids[:1], ['module'])
241 self.depends[res[0]['module']]=True
242 fields = model_pool.fields_get(cr, uid)
244 defaults[model] = model_pool.default_get(cr, uid, data)
245 for key,val in data.items():
246 if ((key in defaults[model]) and (val == defaults[model][key])) and not(fields[key].get('required',False)):
248 if not (val or (fields[key]['type']=='boolean')):
250 elif fields[key]['type'] in ('boolean',):
254 elif fields[key]['type'] in ('integer','float'):
256 elif fields[key]['type'] in ('many2one',):
257 if type(val) in (type(''), type(u'')):
260 id, update = self._get_id(cr, uid, fields[key]['relation'], val)
262 elif fields[key]['type'] in ('one2many',):
264 for valitem in (val or []):
265 if valitem[0] in (0,1):
266 if key in model_pool._columns:
267 fname = model_pool._columns[key]._fields_id
269 fname = model_pool._inherit_fields[key][2]._fields_id
270 valitem[2][fname] = record_id
271 newid,update = self._get_id(cr, uid, fields[key]['relation'], valitem[1])
273 newid = self._create_id(cr, uid, fields[key]['relation'], valitem[2])
274 self.ids[(fields[key]['relation'], valitem[1])] = newid
275 childrecord = self._create_yaml_record(cr, uid, fields[key]['relation'],valitem[2], newid)
276 items[0].append(childrecord['attrs'])
278 elif fields[key]['type'] in ('many2many',):
279 if (key in defaults[model]) and (val[0][2] == defaults[model][key]):
282 for valitem in (val or []):
284 for id2 in valitem[2]:
285 id,update = self._get_id(cr, uid, fields[key]['relation'], id2)
286 self.ids[(fields[key]['relation'],id2)] = id
292 val=val.replace('"','\'')
296 attrs[key]=tools.ustr(val)
297 record['attrs'] = attrs
300 def get_copy_data(self, cr, uid, model, id, result):
302 obj=self.pool.get(model)
303 data=obj.read(cr, uid,[id])
304 if type(data)==type([]):
310 mod_fields = obj.fields_get(cr, uid)
311 for f in filter(lambda a: isinstance(obj._columns[a], fields.function)\
312 and (not obj._columns[a].store),obj._columns):
315 for key,val in data.items():
316 if result.has_key(key):
318 if mod_fields[key]['type'] == 'many2one':
319 if type(data[key])==type(True) or type(data[key])==type(1):
320 result[key]=data[key]
324 result[key]=data[key][0]
326 elif mod_fields[key]['type'] in ('one2many',):
327 # continue # due to this start stop recording will not record one2many field
328 rel = mod_fields[key]['relation']
331 for rel_id in data[key]:
333 res.append(self.get_copy_data(cr, uid,rel,rel_id,{}))
337 result[key]=data[key]
339 elif mod_fields[key]['type'] == 'many2many':
340 result[key]=[(6,0,data[key])]
343 result[key]=data[key]
344 for k,v in obj._inherits.items():
348 def _create_function(self, cr, uid, doc, model, name, record_id):
349 record = doc.createElement('function')
350 record.setAttribute("name", name)
351 record.setAttribute("model", model)
352 record_list = [record]
354 value = doc.createElement('value')
355 value.setAttribute('eval', '[ref(\'%s\')]' % (record_id, ))
356 value.setAttribute('model', model)
358 record.appendChild(value)
359 return record_list, False
361 def _generate_object_xml(self, cr, uid, rec, recv, doc, result=None):
366 id,update = self._get_id(cr, uid, rec[3], id)
367 noupdate = noupdate or update
370 record,update = self._create_record(cr, uid, doc, rec[3], rec[6], id)
371 noupdate = noupdate or update
372 record_list += record
374 elif rec[4] in ('menu_create',):
376 id,update = self._get_id(cr, uid, rec[3], id)
377 noupdate = noupdate or update
380 record,update = self._create_function(cr, uid, doc, rec[3], rec[4], id)
381 noupdate = noupdate or update
382 record_list += record
384 elif rec[3]=='create':
385 id = self._create_id(cr, uid, rec[2],rec[4])
386 record,noupdate = self._create_record(cr, uid, doc, rec[2], rec[4], id)
387 self.ids[(rec[2], result)] = id
388 record_list += record
391 data=self.get_copy_data(cr,uid,rec[2],rec[4],rec[5])
392 copy_rec=(rec[0],rec[1],rec[2],rec[3],rec[4],data,rec[5])
394 rec_data=[(self.recording_data[0][0],rec,self.recording_data[0][2],self.recording_data[0][3])]
395 self.recording_data=rec_data
396 id = self._create_id(cr, uid, rec[2],rec[5])
397 record,noupdate = self._create_record(cr, uid, doc, rec[2], rec[5], id)
398 self.ids[(rec[2], result)] = id
399 record_list += record
401 return record_list,noupdate
403 def _generate_object_yaml(self, cr, uid, rec, result=None):
404 if self.mode=="create":
405 id = self._create_id(cr, uid, rec[2],rec[4])
406 self.ids[(rec[2], result)] = id
407 record = self._create_yaml_record(cr, uid, rec[2], rec[4], id)
409 if self.mode=="workflow":
410 id,update = self._get_id(cr, uid, rec[2], rec[4])
412 data['model'] = rec[2]
413 data['action'] = rec[3]
416 if self.mode=="write":
417 id,update = self._get_id(cr, uid, rec[2],rec[4][0])
418 record = self._create_yaml_record(cr, uid, rec[2], rec[5], id)
420 data=self.get_copy_data(cr,uid,rec[2],rec[4],rec[5])
421 copy_rec=(rec[0],rec[1],rec[2],rec[3],rec[4],data,rec[5])
423 rec_data=[(self.recording_data[0][0],rec,self.recording_data[0][2],self.recording_data[0][3])]
424 self.recording_data=rec_data
425 id = self._create_id(cr, uid, rec[2],rec[5])
426 record = self._create_yaml_record(cr, uid, str(rec[2]), rec[5], id)
427 self.ids[(rec[2], result)] = id
430 def _generate_assert_xml(self, rec, doc):
433 def generate_xml(self, cr, uid):
434 # Create the minidom document
435 if len(self.recording_data):
437 doc = minidom.Document()
438 terp = doc.createElement("openerp")
439 doc.appendChild(terp)
440 for rec in self.recording_data:
441 if rec[0]=='workflow':
442 rec_id,noupdate = self._get_id(cr, uid, rec[1][2], rec[1][4])
445 data = doc.createElement("data")
446 terp.appendChild(data)
447 wkf = doc.createElement('workflow')
448 data.appendChild(wkf)
449 wkf.setAttribute("model", rec[1][2])
450 wkf.setAttribute("action", rec[1][3])
452 data.setAttribute("noupdate", "1")
453 wkf.setAttribute("ref", rec_id)
455 res_list,noupdate = self._generate_object_xml(cr, uid, rec[1], rec[2], doc, rec[3])
456 data = doc.createElement("data")
458 data.setAttribute("noupdate", "1")
460 terp.appendChild(data)
462 data.appendChild(res)
463 elif rec[0]=='assert':
465 return doc.toprettyxml(indent="\t").encode('utf-8')
467 def generate_yaml(self, cr, uid):
469 if len(self.recording_data):
472 for rec in self.recording_data:
473 if rec[1][3] == 'create':
475 elif rec[1][3] == 'write':
477 elif rec[1][3] == 'copy':
479 elif rec[0] == 'workflow':
483 if self.mode == "workflow":
484 record= self._generate_object_yaml(cr, uid, rec[1],rec[0])
485 yaml_file+="!comment Performing a workflow action %s on module %s"%(record['action'], record['model']) + '''\n'''
486 object=yaml.load(unicode('''\n !workflow %s \n'''%record,'iso-8859-1'))
487 yaml_file += str(object) + '''\n\n'''
489 record= self._generate_object_yaml(cr, uid, rec[1],rec[3])
490 yaml_file+="!comment Creating an %s record"%(record['model']) + '''\n'''
491 object= yaml.load(unicode('''\n !record %s \n'''%record,'iso-8859-1'))
492 yaml_file += str(object) + '''\n'''
493 attrs=yaml.dump(object.attrs, default_flow_style=False)
494 yaml_file += attrs + '''\n\n'''
497 for line in yaml_file.split('\n'):
498 line=line.replace("''","'")
499 if line.find('!record') == 0:
500 line = "- \n" + " " + line
501 elif line.find('!workflow') == 0:
502 line = "- \n" + " " + line
503 elif line.find('!comment') == 0:
504 line=line.replace('!comment','- \n ')
505 elif line.find('- -') != -1:
506 line=line.replace('- -',' -')
510 yaml_result += line + '''\n'''
515 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: