[FIX] Pass wrong id when calling browse on object base.synchro.server
[odoo/odoo.git] / addons / base_synchro / wizard / base_synchro.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 import osv
22 from datetime import date
23 import time
24 import pooler
25 import xmlrpclib
26 import re
27 import tools
28 import threading
29 from osv import osv, fields
30
31 class RPCProxyOne(object):
32     def __init__(self, server, ressource):
33         self.server = server
34         local_url = 'http://%s:%d/xmlrpc/common'%(server.server_url,server.server_port)
35         rpc = xmlrpclib.ServerProxy(local_url)
36         self.uid = rpc.login(server.server_db, server.login, server.password)
37         local_url = 'http://%s:%d/xmlrpc/object'%(server.server_url,server.server_port)
38         self.rpc = xmlrpclib.ServerProxy(local_url)
39         self.ressource = ressource
40     def __getattr__(self, name):
41         return lambda cr, uid, *args, **kwargs: self.rpc.execute(self.server.server_db, self.uid, self.server.password, self.ressource, name, *args, **kwargs)
42
43 class RPCProxy(object):
44     def __init__(self, server):
45         self.server = server
46     def get(self, ressource):
47         return RPCProxyOne(self.server, ressource)
48
49 class base_synchro(osv.osv_memory):
50     """Base Synchronization """
51     _name = 'base.synchro'
52
53     _columns = {
54     'server_url': fields.many2one('base.synchro.server', "Server URL", required=True),
55     'user_id': fields.many2one('res.users', "Send Result To",),
56     }
57
58     _defaults = {
59         'user_id': lambda self,cr,uid,context: uid,
60         }
61
62     start_date = time.strftime('%Y-%m-%d, %Hh %Mm %Ss')
63     report = []
64     report_total = 0
65     report_create = 0
66     report_write = 0
67
68     def synchronize(self, cr, uid, server, object, context=None):
69         pool = pooler.get_pool(cr.dbname)
70         self.meta = {}
71         ids = []
72         pool1 = RPCProxy(server)
73         pool2 = pool
74         #try:
75         if object.action in ('d','b'):
76             ids = pool1.get('base.synchro.obj').get_ids(cr, uid,
77                 object.model_id.model,
78                 object.synchronize_date,
79                 eval(object.domain),
80                 {'action':'d'}
81             )
82         if object.action in ('u','b'):
83             ids += pool2.get('base.synchro.obj').get_ids(cr, uid,
84                 object.model_id.model,
85                 object.synchronize_date,
86                 eval(object.domain),
87                 {'action':'u'}
88             )
89         ids.sort()
90         iii = 0
91         for dt, id, action in ids:
92             print 'Process', dt, id, action
93             iii +=1
94             if action=='u':
95                 pool_src = pool2
96                 pool_dest = pool1
97             else:
98                 pool_src = pool1
99                 pool_dest = pool2
100             print 'Read', object.model_id.model, id
101             fields = False
102             if object.model_id.model=='crm.case.history':
103                 fields = ['email','description','log_id']
104             value = pool_src.get(object.model_id.model).read(cr, uid, [id], fields)[0]
105             value = self.data_transform(cr, uid, pool_src, pool_dest, object.model_id.model, value, action)
106             id2 = self.get_id(cr, uid, object.id, id, action, context)
107             #
108             # Transform value
109             #
110             #tid=pool_dest.get(object.model_id.model).name_search(cr, uid, value['name'],[],'=',)
111             if not (iii%50):
112                 print 'Record', iii
113
114             # Filter fields to not sync
115             for field in object.avoid_ids:
116                 if field.name in value:
117                     del value[field.name]
118
119             if id2:
120                 #try:
121                 pool_dest.get(object.model_id.model).write(cr, uid, [id2], value)
122                 #except Exception, e:
123                 #self.report.append('ERROR: Unable to update record ['+str(id2)+']:'+str(value.get('name', '?')))
124                 self.report_total+=1
125                 self.report_write+=1
126             else:
127                 print value
128                 idnew = pool_dest.get(object.model_id.model).create(cr, uid, value)
129                 synid = self.pool.get('base.synchro.obj.line').create(cr, uid, {
130                     'obj_id': object.id,
131                     'local_id': (action=='u') and id or idnew,
132                     'remote_id': (action=='d') and id or idnew
133                 })
134                 self.report_total+=1
135                 self.report_create+=1
136         self.meta = {}
137         return True
138
139     def get_id(self, cr, uid, object_id, id, action, context=None):
140         pool = pooler.get_pool(cr.dbname)
141         line_pool = pool.get('base.synchro.obj.line')
142         field_src = (action=='u') and 'local_id' or 'remote_id'
143         field_dest = (action=='d') and 'local_id' or 'remote_id'
144         rid = line_pool.search(cr, uid, [('obj_id','=',object_id), (field_src,'=',id)], context=context)
145         result = False
146         if rid:
147             result  = line_pool.read(cr, uid, rid, [field_dest], context=context)[0][field_dest]
148         return result
149
150     def relation_transform(self, cr, uid, pool_src, pool_dest, object, id, action, context=None):
151         if not id:
152             return False
153         pool = pooler.get_pool(cr.dbname)
154         cr.execute('''select o.id from base_synchro_obj o left join ir_model m on (o.model_id =m.id) where
155                 m.model=%s and
156                 o.active''', (object,))
157         obj = cr.fetchone()
158         result = False
159         if obj:
160             #
161             # If the object is synchronised and found, set it
162             #
163             result = self.get_id(cr, uid, obj[0], id, action, context)
164         else:
165             #
166             # If not synchronized, try to find it with name_get/name_search
167             #
168             names = pool_src.get(object).name_get(cr, uid, [id])[0][1]
169             res = pool_dest.get(object).name_search(cr, uid, names, [], 'like')
170             if res:
171                 result = res[0][0]
172             else:
173                 # LOG this in the report, better message.
174                 print self.report.append('WARNING: Record "%s" on relation %s not found, set to null.' % (names,object))
175         return result
176
177     #
178     # IN: object and ID
179     # OUT: ID of the remote object computed:
180     #        If object is synchronised, read the sync database
181     #        Otherwise, use the name_search method
182     #
183
184     def data_transform(self, cr, uid, pool_src, pool_dest, object, data, action='u', context=None):
185         self.meta.setdefault(pool_src, {})
186         if not object in self.meta[pool_src]:
187             self.meta[pool_src][object] = pool_src.get(object).fields_get(cr, uid)
188         fields = self.meta[pool_src][object]
189
190         for f in fields:
191             if f not in data:
192                 continue
193             ftype = fields[f]['type']
194
195             if ftype in ('function', 'one2many', 'one2one'):
196                 del data[f]
197             elif ftype == 'many2one':
198                 if data[f]:
199                     df = self.relation_transform(cr, uid, pool_src, pool_dest, fields[f]['relation'], data[f][0], action, context=context)
200                     data[f] = df
201                     if not data[f]:
202                         del data[f]
203             elif ftype == 'many2many':
204                 res = map(lambda x: self.relation_transform(cr, uid, pool_src, pool_dest, fields[f]['relation'], x, action, context), data[f])
205                 data[f] = [(6, 0, res)]
206         del data['id']
207         return data
208
209     #
210     # Find all objects that are created or modified after the synchronize_date
211     # Synchronize these obejcts
212     #
213
214
215     def upload_download(self, cr, uid, ids, context=None):
216         start_date = time.strftime('%Y-%m-%d, %Hh %Mm %Ss')
217         syn_obj = self.browse(cr, uid, ids, context=context)[0]
218         pool = pooler.get_pool(cr.dbname)
219         server = pool.get('base.synchro.server').browse(cr, uid, syn_obj.server_url.id, context=context)
220         for object in server.obj_ids:
221             dt = time.strftime('%Y-%m-%d %H:%M:%S')
222             self.synchronize(cr, uid, server, object, context)
223             if object.action=='b':
224                 time.sleep(1)
225                 dt = time.strftime('%Y-%m-%d %H:%M:%S')
226             self.pool.get('base.synchro.obj').write(cr, uid, [object.id], {'synchronize_date': dt})
227         end_date = time.strftime('%Y-%m-%d, %Hh %Mm %Ss')
228         if syn_obj.user_id:
229             request = pooler.get_pool(cr.dbname).get('res.request')
230             if not self.report:
231                 self.report.append('No exception.')
232             summary = '''Here is the synchronization report:
233
234 Synchronization started: %s
235 Synchronization finnished: %s
236
237 Synchronized records: %d
238 Records updated: %d
239 Records created: %d
240
241 Exceptions:
242             '''% (start_date,end_date,self.report_total, self.report_write,self.report_create)
243             summary += '\n'.join(self.report)
244             request.create(cr, uid, {
245                 'name' : "Synchronization report",
246                 'act_from' : uid,
247                 'act_to' : syn_obj.user_id.id,
248                 'body': summary,
249             })
250             return True
251
252     def upload_download_multi_thread(self, cr, uid, data, context=None):
253         threaded_synchronization = threading.Thread(target=self.upload_download, args=(cr, uid, data, context))
254         threaded_synchronization.run()
255         data_obj = self.pool.get('ir.model.data')
256         id2 = data_obj._get_id(cr, uid, 'base_synchro', 'view_base_synchro_finish')
257         if id2:
258             id2 = data_obj.browse(cr, uid, id2, context=context).res_id
259         return {
260             'view_type': 'form',
261             'view_mode': 'form',
262             'res_model': 'base.synchro',
263             'views': [(id2, 'form')],
264             'view_id': False,
265             'type': 'ir.actions.act_window',
266             'target': 'new',
267         }
268 base_synchro()
269 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: