[IMP] moved openerpweb into base.common to be renamed web.commom
[odoo/odoo.git] / addons / base_module_record / base_module_record.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 #
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.
11 #
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.
16 #
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/>.
19 #
20 ##############################################################################
21
22 from xml.dom import minidom
23 from osv import fields,osv
24 import pooler
25 import string
26 import tools
27
28
29 class xElement(minidom.Element):
30     """dom.Element with compact print
31     The Element in minidom has a problem: if printed, adds whitespace
32     around the text nodes. The standard will not ignore that whitespace.
33     This class simply prints the contained nodes in their compact form, w/o
34     added spaces.
35     """
36     def writexml(self, writer, indent="", addindent="", newl=""):
37         writer.write(indent)
38         minidom.Element.writexml(self, writer, indent='', addindent='', newl='')
39         writer.write(newl)
40
41 def doc_createXElement(xdoc, tagName):
42         e = xElement(tagName)
43         e.ownerDocument = xdoc
44         return e
45
46 import yaml
47 from tools import yaml_tag # This import is not unused! Do not remove!
48 # Please do not override yaml_tag here: modify it in server bin/tools/yaml_tag.py
49
50 class base_module_record(osv.osv):
51     _name = "ir.module.record"
52     _columns = {
53
54     }
55     def __init__(self, *args, **kwargs):
56         self.recording = 0
57         self.recording_data = []
58         self.depends = {}
59         super(base_module_record, self).__init__(*args, **kwargs)
60
61     # To Be Improved
62     def _create_id(self, cr, uid, model, data):
63         i = 0
64         while True:
65             try:
66                 name = filter(lambda x: x in string.letters, (data.get('name','') or '').lower())
67             except:
68                 name=''
69 #            name=data.get('name','') or ''.lower()
70             val = model.replace('.','_')+'_'+ name + str(i)
71             i+=1
72             if val not in self.ids.values():
73                 break
74         return val
75
76     def _get_id(self, cr, uid, model, id):
77         if type(id)==type(()):
78             id=id[0]
79         if (model,id) in self.ids:
80             res_id = self.ids[(model,id)]
81             return res_id, False
82         dt = self.pool.get('ir.model.data')
83         dtids = dt.search(cr, uid, [('model','=',model), ('res_id','=',id)])
84         if not dtids:
85             return False, None
86         obj = dt.browse(cr, uid, dtids[0])
87         self.depends[obj.module] = True
88         return obj.module+'.'+obj.name, obj.noupdate
89     
90     def _create_record(self, cr, uid, doc, model, data, record_id, noupdate=False):
91         data_pool = self.pool.get('ir.model.data')
92         model_pool = self.pool.get(model)
93         
94         record = doc.createElement('record')
95         record.setAttribute("id", record_id)
96         record.setAttribute("model", model)
97         record_list = [record]
98         
99         lids  = data_pool.search(cr, uid, [('model','=',model)])
100         res = data_pool.read(cr, uid, lids[:1], ['module'])
101         if res:
102             self.depends[res[0]['module']]=True
103         fields = model_pool.fields_get(cr, uid)
104         for key,val in data.items():
105             if not (val or (fields[key]['type']=='boolean')):
106                 continue
107             if fields[key]['type'] in ('integer','float'):
108                 field = doc.createElement('field')
109                 field.setAttribute("name", key)
110                 field.setAttribute("eval", val and str(val) or 'False' )
111                 record.appendChild(field)
112             elif fields[key]['type'] in ('boolean',):
113                 field = doc.createElement('field')
114                 field.setAttribute("name", key)
115                 field.setAttribute("eval", val and '1' or '0' )
116                 record.appendChild(field)
117             elif fields[key]['type'] in ('many2one',):
118                 field = doc.createElement('field')
119                 field.setAttribute("name", key)
120                 if type(val) in (type(''),type(u'')):
121                     id = val
122                 else:
123                     id,update = self._get_id(cr, uid, fields[key]['relation'], val)
124                     noupdate = noupdate or update
125                 if not id:
126                     relation_pool = self.pool.get(fields[key]['relation'])
127                     
128                     field.setAttribute("model", fields[key]['relation'])
129                     fld_nm = relation_pool._rec_name
130                     name = relation_pool.read(cr, uid, val,[fld_nm])[fld_nm] or False
131                     field.setAttribute("search", str([(str(fld_nm) ,'=', name)]))
132                 else:
133                     field.setAttribute("ref", id)
134                 record.appendChild(field)
135             elif fields[key]['type'] in ('one2many',):
136                 for valitem in (val or []):
137                     if valitem[0] in (0,1):
138                         if key in model_pool._columns:
139                             model_pool._columns[key]._fields_id
140                         else:
141                             model_pool._inherit_fields[key][2]._fields_id
142                         if valitem[0] == 0:
143                             newid = self._create_id(cr, uid, fields[key]['relation'], valitem[2])
144                             valitem[1]=newid
145                         else:
146                             newid,update = self._get_id(cr, uid, fields[key]['relation'], valitem[1])
147                             if not newid:
148                                 newid = self._create_id(cr, uid, fields[key]['relation'], valitem[2])
149                                 valitem[1]=newid
150                         self.ids[(fields[key]['relation'], valitem[1])] = newid
151                         
152                         childrecord, update = self._create_record(cr, uid, doc, fields[key]['relation'],valitem[2], newid)
153                         noupdate = noupdate or update
154                         record_list += childrecord
155                     else:
156                         pass
157             elif fields[key]['type'] in ('many2many',):
158                 res = []
159                 for valitem in (val or []):
160                     if valitem[0]==6:
161                         for id2 in valitem[2]:
162                             id,update = self._get_id(cr, uid, fields[key]['relation'], id2)
163                             self.ids[(fields[key]['relation'],id2)] = id
164                             noupdate = noupdate or update
165                             res.append(id)
166                         field = doc.createElement('field')
167                         field.setAttribute("name", key)
168                         field.setAttribute("eval", "[(6,0,["+','.join(map(lambda x: "ref('%s')" % (x,), res))+'])]')
169                         record.appendChild(field)
170             else:
171                 field = doc_createXElement(doc, 'field')
172                 field.setAttribute("name", key)
173                 field.appendChild(doc.createTextNode(val))
174                 record.appendChild(field)
175
176         return record_list, noupdate
177
178     def _create_yaml_record(self, cr, uid, model, data, record_id):
179         record={'model': model, 'id': str(record_id)}
180         
181         model_pool = self.pool.get(model)
182         data_pool = self.pool.get('ir.model.data')
183         lids  = data_pool.search(cr, uid, [('model','=',model)])
184         
185         res = data_pool.read(cr, uid, lids[:1], ['module'])
186         attrs={}
187         if res:
188             self.depends[res[0]['module']]=True
189         fields = model_pool.fields_get(cr, uid)
190         defaults={}
191         try:
192             defaults[model] = model_pool.default_get(cr, uid, data)
193         except:
194             defaults[model]={}
195         for key,val in data.items():  
196             if ((key in defaults[model]) and (val ==  defaults[model][key])) and not(fields[key].get('required',False)):
197                 continue
198             if fields[key]['type'] in ('integer','float'):
199                 if not val:
200                     val=0.0
201                 attrs[key] = val
202             elif not (val or (fields[key]['type']=='function')):
203                 continue
204             elif fields[key]['type'] in ('boolean',):
205                 if not val:
206                     continue
207                 attrs[key] = val
208             elif fields[key]['type'] in ('many2one',):
209                 if type(val) in (type(''), type(u'')):
210                     id = val
211                 else:
212                     id, update = self._get_id(cr, uid, fields[key]['relation'], val)
213                 attrs[key] = str(id)
214             elif fields[key]['type'] in ('one2many',):
215                 items=[[]]
216                 for valitem in (val or []):
217                     if valitem[0] in (0,1):
218                         if key in model_pool._columns:
219                             fname = model_pool._columns[key]._fields_id
220                         else:
221                             fname = model_pool._inherit_fields[key][2]._fields_id
222                         del valitem[2][fname] #delete parent_field from child's fields list
223                         
224                         childrecord = self._create_yaml_record(cr, uid, fields[key]['relation'],valitem[2], None)
225                         items[0].append(childrecord['attrs'])
226                 attrs[key] = items
227             elif fields[key]['type'] in ('many2many',):
228                 if (key in defaults[model]) and (val[0][2] ==  defaults[model][key]):
229                     continue
230                 res = []
231                 for valitem in (val or []):
232                     if valitem[0]==6:
233                         for id2 in valitem[2]:
234                             id,update = self._get_id(cr, uid, fields[key]['relation'], id2)
235                             self.ids[(fields[key]['relation'],id2)] = id
236                             res.append(str(id))
237                         m2m=[res]
238                 if m2m[0]:
239                     attrs[key] = m2m
240             else:
241                 try:
242                     attrs[key]=str(val)
243                 except:
244                     attrs[key]=tools.ustr(val)
245                 attrs[key]=attrs[key].replace('"','\'')
246         record['attrs'] = attrs
247         return record
248
249     def get_copy_data(self, cr, uid, model, id, result):
250         res = []
251         obj=self.pool.get(model)
252         data=obj.read(cr, uid,[id])
253         if type(data)==type([]):
254             del data[0]['id']
255             data=data[0]
256         else:
257             del data['id']
258
259         mod_fields = obj.fields_get(cr, uid)
260         for f in filter(lambda a: isinstance(obj._columns[a], fields.function)\
261                     and (not obj._columns[a].store),obj._columns):
262             del data[f]
263             
264         for key,val in data.items():
265             if result.has_key(key):
266                 continue
267             if mod_fields[key]['type'] == 'many2one':
268                 if type(data[key])==type(True) or type(data[key])==type(1):
269                     result[key]=data[key]
270                 elif not data[key]:
271                     result[key] = False
272                 else:
273                     result[key]=data[key][0]
274
275             elif mod_fields[key]['type'] in ('one2many',):
276 #                continue # due to this start stop recording will not record one2many field
277                 rel = mod_fields[key]['relation']
278                 if len(data[key]):
279                     res1=[]
280                     for rel_id in data[key]:
281                         res=[0,0]
282                         res.append(self.get_copy_data(cr, uid,rel,rel_id,{}))
283                         res1.append(res)
284                     result[key]=res1
285                 else:
286                     result[key]=data[key]
287
288             elif mod_fields[key]['type'] == 'many2many':
289                 result[key]=[(6,0,data[key])]
290
291             else:
292                 result[key]=data[key]
293         for k,v in obj._inherits.items():
294             del result[v]
295         return result
296
297     def _create_function(self, cr, uid, doc, model, name, record_id):
298         record = doc.createElement('function')
299         record.setAttribute("name", name)
300         record.setAttribute("model", model)
301         record_list = [record]
302
303         value = doc.createElement('value')
304         value.setAttribute('eval', '[ref(\'%s\')]' % (record_id, ))
305         value.setAttribute('model', model)
306
307         record.appendChild(value)
308         return record_list, False
309
310     def _generate_object_xml(self, cr, uid, rec, recv, doc, result=None):
311         record_list = []
312         noupdate = False
313         if rec[3]=='write':
314             for id in rec[4]:
315                 id,update = self._get_id(cr, uid, rec[2], id)
316                 noupdate = noupdate or update
317                 if not id:
318                     continue
319                 record,update = self._create_record(cr, uid, doc, rec[2], rec[5], id)
320                 noupdate = noupdate or update
321                 record_list += record
322                 
323         elif rec[4] in ('menu_create',):
324             for id in rec[5]:
325                 id,update = self._get_id(cr, uid, rec[3], id)
326                 noupdate = noupdate or update
327                 if not id:
328                     continue
329                 record,update = self._create_function(cr, uid, doc, rec[3], rec[4], id)
330                 noupdate = noupdate or update
331                 record_list += record
332
333         elif rec[3]=='create':
334             id = self._create_id(cr, uid, rec[2],rec[4])
335             record,noupdate = self._create_record(cr, uid, doc, rec[2], rec[4], id)
336             self.ids[(rec[2], result)] = id
337             record_list += record
338
339         elif rec[3]=='copy':
340             data=self.get_copy_data(cr,uid,rec[2],rec[4],rec[5])
341             copy_rec=(rec[0],rec[1],rec[2],rec[3],rec[4],data,rec[5])
342             rec=copy_rec
343             rec_data=[(self.recording_data[0][0],rec,self.recording_data[0][2],self.recording_data[0][3])]
344             self.recording_data=rec_data
345             id = self._create_id(cr, uid, rec[2],rec[5])
346             record,noupdate = self._create_record(cr, uid, doc, rec[2], rec[5], id)
347             self.ids[(rec[2], result)] = id
348             record_list += record
349
350         return record_list,noupdate
351
352     def _generate_object_yaml(self, cr, uid, rec, result=None):
353         if self.mode=="create":
354             yml_id = self._create_id(cr, uid, rec[2],rec[4])
355             self.ids[(rec[2], result)] = yml_id
356             record = self._create_yaml_record(cr, uid, rec[2], rec[4], yml_id)
357             return record
358         if self.mode=="workflow":
359             id,update = self._get_id(cr, uid, rec[2], rec[4])
360             data = {}
361             data['model'] = rec[2]
362             data['action'] = rec[3]
363             data['ref'] = id
364             return data
365         if self.mode=="write":
366             id,update = self._get_id(cr, uid, rec[2],rec[4][0])
367             record = self._create_yaml_record(cr, uid, rec[2], rec[5], id)
368             return record
369         data=self.get_copy_data(cr,uid,rec[2],rec[4],rec[5])
370         copy_rec=(rec[0],rec[1],rec[2],rec[3],rec[4],data,rec[5])
371         rec=copy_rec
372         rec_data=[(self.recording_data[0][0],rec,self.recording_data[0][2],self.recording_data[0][3])]
373         self.recording_data=rec_data
374         id = self._create_id(cr, uid, rec[2],rec[5])
375         record = self._create_yaml_record(cr, uid, str(rec[2]), rec[5], id)
376         self.ids[(rec[2], result)] = id
377         return record
378
379     def _generate_function_yaml(self, cr, uid, args):
380         db, uid, model, action, ids, context = args
381         temp_context = context.copy()
382         active_id = temp_context['active_id']
383         active_model = temp_context['active_model']
384         active_id, update = self._get_id(cr, uid, active_model, active_id)
385         if not active_id:
386             active_id = 1
387         rec_id, noupdate = self._get_id(cr, uid, model, ids[0])
388         temp_context['active_id'] = "ref('%s')"%unicode(active_id)
389         temp_context['active_ids'][0] = "ref('%s')"%str(active_id)
390         function={}
391         function['model'] = model
392         function['action'] = action
393         attrs = "self.%s(cr, uid, [ref('%s')], {" %(action, rec_id, )
394         for k, v in temp_context.iteritems():
395             if isinstance(v, str):
396                 f= "'"+k+"': "+"'%s'"%v + ", "
397             else:
398                 v=str(v).replace('"', '')
399                 f= "'"+k+"': "+"%s"%v + ", "
400             attrs = attrs + f
401         attrs=str(attrs)+'})'
402         function['attrs'] = attrs
403         return function
404             
405     def _generate_assert_xml(self, rec, doc):
406         pass
407
408     def generate_xml(self, cr, uid):
409         # Create the minidom document
410         if len(self.recording_data):
411             self.ids = {}
412             doc = minidom.Document()
413             terp = doc.createElement("openerp")
414             doc.appendChild(terp)
415             for rec in self.recording_data:
416                 if rec[0]=='workflow':
417                     rec_id,noupdate = self._get_id(cr, uid, rec[1][2], rec[1][4])
418                     if not rec_id:
419                         continue
420                     data = doc.createElement("data")
421                     terp.appendChild(data)
422                     wkf = doc.createElement('workflow')
423                     data.appendChild(wkf)
424                     wkf.setAttribute("model", rec[1][2])
425                     wkf.setAttribute("action", rec[1][3])
426                     if noupdate:
427                         data.setAttribute("noupdate", "1")
428                     wkf.setAttribute("ref", rec_id)
429                 if rec[0]=='query':
430                     res_list,noupdate = self._generate_object_xml(cr, uid, rec[1], rec[2], doc, rec[3])
431                     data = doc.createElement("data")
432                     if noupdate:
433                         data.setAttribute("noupdate", "1")
434                     if res_list:
435                         terp.appendChild(data)
436                     for res in res_list:
437                         data.appendChild(res)
438                 elif rec[0]=='assert':
439                         pass
440             return doc.toprettyxml(indent="\t").encode('utf-8')
441
442     def generate_yaml(self, cr, uid):
443         self.ids = {}
444         if len(self.recording_data):
445             yaml_file='''\n'''
446     
447             for rec in self.recording_data:
448                 if rec[1][3] == 'create':
449                     self.mode="create"
450                 elif rec[1][3] == 'write':
451                     self.mode="write"
452                 elif rec[1][3] == 'copy':
453                     self.mode="copy"
454                 elif rec[0] == 'workflow':
455                     self.mode="workflow"
456                 elif rec[0] == 'osv_memory_action':
457                     self.mode='osv_memory_action'
458                 else:
459                     continue
460                 if self.mode == "workflow":
461                     record = self._generate_object_yaml(cr, uid, rec[1],rec[0])
462                     yaml_file += "!comment Performing a workflow action %s on module %s"%(record['action'], record['model']) + '''\n'''
463                     object = yaml.load(unicode('''\n !workflow %s \n'''%record,'iso-8859-1'))
464                     yaml_file += str(object) + '''\n\n'''
465                 elif self.mode == 'osv_memory_action':
466                     osv_action = self._generate_function_yaml(cr, uid, rec[1])
467                     yaml_file += "!comment Performing an osv_memory action %s on module %s"%(osv_action['action'], osv_action['model']) + '''\n'''
468                     osv_action = yaml.load(unicode('''\n !python %s \n'''%osv_action,'iso-8859-1'))
469                     yaml_file += str(osv_action) + '''\n'''
470                     attrs = yaml.dump(osv_action.attrs, default_flow_style=False)
471                     attrs = attrs.replace("''", '"')
472                     attrs = attrs.replace("'", '')
473                     yaml_file += attrs + '''\n\n'''
474                 else:
475                     record = self._generate_object_yaml(cr, uid, rec[1], rec[3])
476                     if self.mode == "create" or self.mode == "copy":
477                         yaml_file += "!comment Creating a %s record"%(record['model']) + '''\n'''
478                     else:
479                         yaml_file += "!comment Modifying a %s record"%(record['model']) + '''\n'''
480                     object = yaml.load(unicode('''\n !record %s \n'''%record,'iso-8859-1'))
481                     yaml_file += str(object) + '''\n'''
482                     attrs = yaml.dump(object.attrs, default_flow_style=False)
483                     yaml_file += attrs + '''\n\n'''
484                     
485         yaml_result=''''''
486         for line in yaml_file.split('\n'):
487             line=line.replace("''","'")
488             if (line.find('!record') == 0) or (line.find('!workflow') == 0) or (line.find('!python') == 0):
489                 line = "- \n" + "  " + line
490             elif line.find('!comment') == 0:
491                 line=line.replace('!comment','- \n ')   
492             elif line.find('- -') != -1:
493                 line=line.replace('- -','  -')
494                 line = "    " + line
495             else:
496                 line = "    " + line
497             yaml_result += line + '''\n'''
498             
499         return yaml_result
500
501 base_module_record()
502 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
503