Merge lp:~therp-nl/openupgrade-server/5.0-use_orm into lp:openupgrade-server/5.0

Proposed by Stefan Rijnhart (Opener)
Status: Merged
Merged at revision: 2176
Proposed branch: lp:~therp-nl/openupgrade-server/5.0-use_orm
Merge into: lp:openupgrade-server/5.0
Diff against target: 1699 lines (+1461/-53)
23 files modified
bin/addons/__init__.py (+135/-52)
bin/addons/base/ir/ir_model.py (+6/-0)
bin/addons/openupgrade_records/__init__.py (+2/-0)
bin/addons/openupgrade_records/__openerp__.py (+63/-0)
bin/addons/openupgrade_records/__terp__.py (+63/-0)
bin/addons/openupgrade_records/model/__init__.py (+6/-0)
bin/addons/openupgrade_records/model/analysis_wizard.py (+178/-0)
bin/addons/openupgrade_records/model/comparison_config.py (+98/-0)
bin/addons/openupgrade_records/model/generate_records_wizard.py (+90/-0)
bin/addons/openupgrade_records/model/install_all_wizard.py (+113/-0)
bin/addons/openupgrade_records/model/openupgrade_record.py (+111/-0)
bin/addons/openupgrade_records/security/ir.model.access.csv (+3/-0)
bin/addons/openupgrade_records/view/analysis_wizard.xml (+30/-0)
bin/addons/openupgrade_records/view/comparison_config.xml (+62/-0)
bin/addons/openupgrade_records/view/generate_records_wizard.xml (+52/-0)
bin/addons/openupgrade_records/view/install_all_wizard.xml (+54/-0)
bin/addons/openupgrade_records/view/openupgrade_record.xml (+65/-0)
bin/openupgrade/doc/readme.txt (+2/-0)
bin/openupgrade/openupgrade.py (+259/-0)
bin/openupgrade/openupgrade_log.py (+56/-0)
bin/openupgrade/openupgrade_tools.py (+8/-0)
bin/tools/convert.py (+3/-0)
bin/tools/sql.py (+2/-1)
To merge this branch: bzr merge lp:~therp-nl/openupgrade-server/5.0-use_orm
Reviewer Review Type Date Requested Status
OpenUpgrade Committers Pending
Review via email: mp+105195@code.launchpad.net

Description of the change

This merge constitutes the refactoring of the database layout analysis and the inclusion of the analysis files for 5.0, as described here:

https://lists.launchpad.net/openupgrade-drivers/msg00001.html

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/addons/__init__.py'
2--- bin/addons/__init__.py 2011-11-30 22:57:11 +0000
3+++ bin/addons/__init__.py 2012-05-09 12:36:18 +0000
4@@ -41,6 +41,15 @@
5
6 logger = netsvc.Logger()
7
8+### OpenUpgrade
9+def table_exists(cr, table):
10+ """ Check whether a certain table or view exists """
11+ cr.execute(
12+ 'SELECT count(relname) FROM pg_class WHERE relname = %s',
13+ (table,))
14+ return cr.fetchone()[0] == 1
15+### End of OpenUpgrade
16+
17 _ad = os.path.abspath(opj(tools.config['root_path'], 'addons')) # default addons path (base)
18 ad = os.path.abspath(tools.config['addons_path']) # alternate addons path
19
20@@ -571,12 +580,133 @@
21 modobj = None
22
23 import string
24+
25+ local_registry = {}
26 def get_repr(properties, type='val'):
27+ """
28+ OpenUpgrade: Return the string representation of the model or field
29+ for logging purposes
30+ """
31 if type == 'key':
32 props = ['model', 'field']
33 elif type == 'val':
34- props = ['type', 'isfunction', 'relation', 'required', 'selection_keys', 'req_default', 'inherits']
35- return ','.join(["\"" + string.replace(properties[prop], '\"', '\'') + "\"" for prop in props])
36+ props = [
37+ 'type', 'isfunction', 'relation', 'required', 'selection_keys',
38+ 'req_default', 'inherits'
39+ ]
40+ return ','.join([
41+ '\"' + string.replace(
42+ string.replace(
43+ properties[prop], '\"', '\''), '\n','')
44+ + '\"' for prop in props
45+ ])
46+
47+ def log_model(model):
48+ """
49+ OpenUpgrade: Store the characteristics of the BaseModel and its fields
50+ in the local registry, so that we can compare changes with the
51+ main registry
52+ """
53+
54+ # persistent models only
55+ if isinstance(model, osv.osv.osv_memory):
56+ return
57+
58+ model_registry = local_registry.setdefault(
59+ model._name, {})
60+ if model._inherits:
61+ model_registry['_inherits'] = {'_inherits': unicode(model._inherits)}
62+ for k, v in model._columns.items():
63+ properties = {
64+ 'type': v._type,
65+ 'isfunction': (
66+ isinstance(v, osv.fields.function) and 'function' or ''),
67+ 'relation': (
68+ v._type in ('many2many', 'many2one','one2many')
69+ and v._obj or ''
70+ ),
71+ 'required': v.required and 'required' or '',
72+ 'selection_keys': '',
73+ 'req_default': '',
74+ 'inherits': '',
75+ }
76+ if v._type == 'selection':
77+ if hasattr(v.selection, "__iter__"):
78+ properties['selection_keys'] = unicode(
79+ sorted([x[0] for x in v.selection]))
80+ else:
81+ properties['selection_keys'] = 'function'
82+ if v.required and k in model._defaults:
83+ if isinstance(model._defaults[k], types.FunctionType):
84+ # todo: in OpenERP 5 (and in 6 as well),
85+ # literals are wrapped in a lambda function.
86+ properties['req_default'] = 'function'
87+ else:
88+ properties['req_default'] = unicode(model._defaults[k])
89+ for key, value in properties.items():
90+ if value:
91+ model_registry.setdefault(k, {})[key] = value
92+
93+ def get_record_id(cr, module, model, field, mode):
94+ """
95+ OpenUpgrade: get or create the id from the record table matching
96+ the key parameter values
97+ """
98+ cr.execute(
99+ "SELECT id FROM openupgrade_record "
100+ "WHERE module = %s AND model = %s AND "
101+ "field = %s AND mode = %s AND type = %s",
102+ (module, model, field, mode, 'field')
103+ )
104+ record = cr.fetchone()
105+ if record:
106+ return record[0]
107+ cr.execute(
108+ "INSERT INTO openupgrade_record "
109+ "(module, model, field, mode, type) "
110+ "VALUES (%s, %s, %s, %s, %s)",
111+ (module, model, field, mode, 'field')
112+ )
113+ cr.execute(
114+ "SELECT id FROM openupgrade_record "
115+ "WHERE module = %s AND model = %s AND "
116+ "field = %s AND mode = %s AND type = %s",
117+ (module, model, field, mode, 'field')
118+ )
119+ return cr.fetchone()[0]
120+
121+ def compare_registries(cr, module):
122+ """
123+ OpenUpgrade: Compare the local registry with the global registry,
124+ log any differences and merge the local registry with
125+ the global one.
126+ """
127+ if not table_exists(cr, 'openupgrade_record'):
128+ return
129+ for model, fields in local_registry.items():
130+ registry.setdefault(model, {})
131+ for field, attributes in fields.items():
132+ old_field = registry[model].setdefault(field, {})
133+ mode = old_field and 'modify' or 'create'
134+ record_id = False
135+ for key, value in attributes.items():
136+ if key not in old_field or old_field[key] != value:
137+ if not record_id:
138+ record_id = get_record_id(
139+ cr, module, model, field, mode)
140+ cr.execute(
141+ "SELECT id FROM openupgrade_attribute "
142+ "WHERE name = %s AND value = %s AND "
143+ "record_id = %s",
144+ (key, value, record_id)
145+ )
146+ if not cr.fetchone():
147+ cr.execute(
148+ "INSERT INTO openupgrade_attribute "
149+ "(name, value, record_id) VALUES (%s, %s, %s)",
150+ (key, value, record_id)
151+ )
152+ old_field[key] = value
153
154 for package in graph:
155 logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading objects' % package.name)
156@@ -584,53 +714,10 @@
157 register_class(package.name)
158 modules = pool.instanciate(package.name, cr)
159
160- logger.notifyChannel('OpenUpgrade_FIELD', netsvc.LOG_INFO, 'module %s' % (package.name))
161 local_registry = {}
162- for orm_object in osv.orm.orm:
163- if orm_object._inherits:
164- properties = {
165- 'model': orm_object._name,
166- 'field': '_inherits',
167- 'type': '',
168- 'isfunction': '',
169- 'relation': '',
170- 'required': '',
171- 'selection_keys': '',
172- 'req_default': '',
173- 'inherits': unicode(orm_object._inherits),
174- }
175- local_registry[get_repr(properties, 'key')] = get_repr(properties)
176- for k,v in orm_object._columns.items():
177- properties = {
178- 'model': orm_object._name,
179- 'field': k,
180- 'type': v._type,
181- 'isfunction': isinstance(v, osv.fields.function) and 'function' or '',
182- 'relation': v._type in ('many2many', 'many2one','one2many') and v._obj or '',
183- 'required': v.required and 'required' or '',
184- 'selection_keys': '',
185- 'req_default': '',
186- 'inherits': '',
187- }
188- if v._type == 'selection':
189- if hasattr(v.selection, "__iter__"):
190- properties['selection_keys'] = unicode(sorted([x[0] for x in v.selection]))
191- else:
192- properties['selection_keys'] = 'function'
193- if v.required and k in orm_object._defaults:
194- if isinstance(orm_object._defaults[k], types.FunctionType):
195- properties['req_default'] = 'function'
196- else:
197- properties['req_default'] = unicode(orm_object._defaults[k])
198- local_registry[get_repr(properties, 'key')] = get_repr(properties)
199- for key in sorted(local_registry.keys()):
200- if key in registry:
201- if registry[key] != local_registry[key]:
202- logger.notifyChannel('OpenUpgrade_FIELD', netsvc.LOG_INFO, '"%s","modify",%s,%s' % (package.name, key, local_registry[key]))
203- else:
204- logger.notifyChannel('OpenUpgrade_FIELD', netsvc.LOG_INFO, '"%s","create",%s,%s' % (package.name, key, local_registry[key]))
205- registry[key] = local_registry[key]
206-
207+ for model in modules:
208+ log_model(model)
209+ compare_registries(cr, package.name)
210 if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'):
211 init_module_objects(cr, package.name, modules)
212 cr.commit()
213@@ -712,10 +799,6 @@
214 delattr(package, kind)
215
216 statusi += 1
217- cr.execute('select model, name from ir_model_data where module=%s order by model, name', (package.name,))
218- for res in cr.fetchall():
219- xmlid_repr = ','.join(["\"" + string.replace(property, '\"', '\'') + "\"" for property in (res[0], res[1], package.name)])
220- logger.notifyChannel('OpenUpgrade_XMLID', netsvc.LOG_INFO, xmlid_repr)
221
222 cr.execute('select model from ir_model where state=%s', ('manual',))
223 for model in cr.dictfetchall():
224
225=== modified file 'bin/addons/base/ir/ir_model.py'
226--- bin/addons/base/ir/ir_model.py 2010-05-18 09:30:44 +0000
227+++ bin/addons/base/ir/ir_model.py 2012-05-09 12:36:18 +0000
228@@ -31,6 +31,8 @@
229 from tools.translate import _
230 import pooler
231
232+from openupgrade import openupgrade_log
233+
234 def _get_fields_type(self, cr, uid, context=None):
235 cr.execute('select distinct ttype,ttype from ir_model_fields')
236 return cr.fetchall()
237@@ -459,6 +461,10 @@
238 return id
239
240 def _update(self,cr, uid, model, module, values, xml_id=False, store=True, noupdate=False, mode='init', res_id=False, context=None):
241+ #OpenUpgrade: log entry (used in csv import)
242+ if xml_id:
243+ openupgrade_log.log_xml_id(cr, module, xml_id)
244+
245 warning = True
246 model_obj = self.pool.get(model)
247 if not context:
248
249=== added directory 'bin/addons/openupgrade_records'
250=== added file 'bin/addons/openupgrade_records/__init__.py'
251--- bin/addons/openupgrade_records/__init__.py 1970-01-01 00:00:00 +0000
252+++ bin/addons/openupgrade_records/__init__.py 2012-05-09 12:36:18 +0000
253@@ -0,0 +1,2 @@
254+import model
255+import lib
256
257=== added file 'bin/addons/openupgrade_records/__openerp__.py'
258--- bin/addons/openupgrade_records/__openerp__.py 1970-01-01 00:00:00 +0000
259+++ bin/addons/openupgrade_records/__openerp__.py 2012-05-09 12:36:18 +0000
260@@ -0,0 +1,63 @@
261+# -*- coding: utf-8 -*-
262+##############################################################################
263+#
264+# OpenERP, Open Source Management Solution
265+# This module Copyright (C) 2012 OpenUpgrade community
266+# https://launchpad.net/~openupgrade-committers
267+#
268+# Contributors:
269+# Therp BV <http://therp.nl>
270+#
271+# This program is free software: you can redistribute it and/or modify
272+# it under the terms of the GNU Affero General Public License as
273+# published by the Free Software Foundation, either version 3 of the
274+# License, or (at your option) any later version.
275+#
276+# This program is distributed in the hope that it will be useful,
277+# but WITHOUT ANY WARRANTY; without even the implied warranty of
278+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
279+# GNU Affero General Public License for more details.
280+#
281+# You should have received a copy of the GNU Affero General Public License
282+# along with this program. If not, see <http://www.gnu.org/licenses/>.
283+#
284+##############################################################################
285+
286+
287+{
288+ 'name': 'OpenUpgrade Records',
289+ 'version': '0.2',
290+ 'category': 'Normal',
291+ 'description': """Allow OpenUpgrade records to be
292+stored in the database and compare with other servers.
293+
294+This module depends on OpenERP client lib:
295+
296+ easy_install openerp-client-lib
297+
298+""",
299+ 'author': 'OpenUpgrade Community',
300+ 'maintainer': 'OpenUpgrade Community',
301+ 'contributors': ['Therp BV'],
302+ 'website': 'https://launchpad.net/~openupgrade-committers',
303+ 'depends': [],
304+ 'init_xml': [],
305+ 'update_xml': [
306+ 'view/openupgrade_record.xml',
307+ 'view/comparison_config.xml',
308+ 'view/analysis_wizard.xml',
309+ 'view/generate_records_wizard.xml',
310+ 'view/install_all_wizard.xml',
311+ 'security/ir.model.access.csv',
312+ ],
313+ 'demo_xml': [
314+ ],
315+ 'test': [
316+ ],
317+ 'installable': True,
318+ 'auto_install': False,
319+ 'external_dependencies': {
320+ 'python' : ['openerplib'],
321+ },
322+}
323+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
324
325=== added file 'bin/addons/openupgrade_records/__terp__.py'
326--- bin/addons/openupgrade_records/__terp__.py 1970-01-01 00:00:00 +0000
327+++ bin/addons/openupgrade_records/__terp__.py 2012-05-09 12:36:18 +0000
328@@ -0,0 +1,63 @@
329+# -*- coding: utf-8 -*-
330+##############################################################################
331+#
332+# OpenERP, Open Source Management Solution
333+# This module Copyright (C) 2012 OpenUpgrade community
334+# https://launchpad.net/~openupgrade-committers
335+#
336+# Contributors:
337+# Therp BV <http://therp.nl>
338+#
339+# This program is free software: you can redistribute it and/or modify
340+# it under the terms of the GNU Affero General Public License as
341+# published by the Free Software Foundation, either version 3 of the
342+# License, or (at your option) any later version.
343+#
344+# This program is distributed in the hope that it will be useful,
345+# but WITHOUT ANY WARRANTY; without even the implied warranty of
346+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
347+# GNU Affero General Public License for more details.
348+#
349+# You should have received a copy of the GNU Affero General Public License
350+# along with this program. If not, see <http://www.gnu.org/licenses/>.
351+#
352+##############################################################################
353+
354+
355+{
356+ 'name': 'OpenUpgrade Records',
357+ 'version': '0.2',
358+ 'category': 'Normal',
359+ 'description': """Allow OpenUpgrade records to be
360+stored in the database and compare with other servers.
361+
362+This module depends on OpenERP client lib:
363+
364+ easy_install openerp-client-lib
365+
366+""",
367+ 'author': 'OpenUpgrade Community',
368+ 'maintainer': 'OpenUpgrade Community',
369+ 'contributors': ['Therp BV'],
370+ 'website': 'https://launchpad.net/~openupgrade-committers',
371+ 'depends': [],
372+ 'init_xml': [],
373+ 'update_xml': [
374+ 'view/openupgrade_record.xml',
375+ 'view/comparison_config.xml',
376+ 'view/analysis_wizard.xml',
377+ 'view/generate_records_wizard.xml',
378+ 'view/install_all_wizard.xml',
379+ 'security/ir.model.access.csv',
380+ ],
381+ 'demo_xml': [
382+ ],
383+ 'test': [
384+ ],
385+ 'installable': True,
386+ 'auto_install': False,
387+ 'external_dependencies': {
388+ 'python' : ['openerplib'],
389+ },
390+}
391+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
392
393=== added directory 'bin/addons/openupgrade_records/model'
394=== added file 'bin/addons/openupgrade_records/model/__init__.py'
395--- bin/addons/openupgrade_records/model/__init__.py 1970-01-01 00:00:00 +0000
396+++ bin/addons/openupgrade_records/model/__init__.py 2012-05-09 12:36:18 +0000
397@@ -0,0 +1,6 @@
398+import openupgrade_record
399+import comparison_config
400+import analysis_wizard
401+import generate_records_wizard
402+import install_all_wizard
403+
404
405=== added file 'bin/addons/openupgrade_records/model/analysis_wizard.py'
406--- bin/addons/openupgrade_records/model/analysis_wizard.py 1970-01-01 00:00:00 +0000
407+++ bin/addons/openupgrade_records/model/analysis_wizard.py 2012-05-09 12:36:18 +0000
408@@ -0,0 +1,178 @@
409+# -*- coding: utf-8 -*-
410+##############################################################################
411+#
412+# OpenERP, Open Source Management Solution
413+# This module Copyright (C) 2012 OpenUpgrade community
414+# https://launchpad.net/~openupgrade-committers
415+#
416+# Contributors:
417+# Therp BV <http://therp.nl>
418+#
419+# This program is free software: you can redistribute it and/or modify
420+# it under the terms of the GNU Affero General Public License as
421+# published by the Free Software Foundation, either version 3 of the
422+# License, or (at your option) any later version.
423+#
424+# This program is distributed in the hope that it will be useful,
425+# but WITHOUT ANY WARRANTY; without even the implied warranty of
426+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
427+# GNU Affero General Public License for more details.
428+#
429+# You should have received a copy of the GNU Affero General Public License
430+# along with this program. If not, see <http://www.gnu.org/licenses/>.
431+#
432+##############################################################################
433+
434+import os
435+from osv import osv, fields
436+
437+try:
438+ from openerp.addons.openupgrade_records.lib import compare
439+ from openerp.openupgrade_records.lib import apriori
440+ from openerp.addons import get_module_path
441+except ImportError:
442+ from openupgrade_records.lib import compare
443+ from openupgrade_records.lib import apriori
444+ from addons import get_module_path
445+
446+class openupgrade_analysis_wizard(osv.osv_memory):
447+ _name = 'openupgrade.analysis.wizard'
448+ _description = 'OpenUpgrade Analysis Wizard'
449+ _columns = {
450+ 'server_config': fields.many2one(
451+ 'openupgrade.comparison.config',
452+ 'Configuration', required=True),
453+ 'state': fields.selection(
454+ [('init', 'Init'), ('ready', 'Ready')], 'State',
455+ readonly=True),
456+ 'log': fields.text('Log'),
457+ 'write': fields.boolean(
458+ 'Write files',
459+ help='Write analysis files to the module directories'
460+ ),
461+ }
462+ _defaults = {
463+ 'state': lambda *a: 'init',
464+ 'write': lambda *a: True,
465+ }
466+
467+ def get_communication(self, cr, uid, ids, context=None):
468+ """
469+ Retrieve both sets of database representations,
470+ perform the comparison and register the resulting
471+ change set
472+ """
473+ def write_file(
474+ module, version, contents, filename='openupgrade_analysis.txt'):
475+ module_path = get_module_path(module)
476+ if not module_path:
477+ return "ERROR: could not find module path:\n"
478+ full_path = os.path.join(
479+ module_path, 'migrations', version)
480+ if not os.path.exists(full_path):
481+ try:
482+ os.makedirs(full_path)
483+ except os.error:
484+ return "ERROR: could not create migrations directory:\n"
485+ logfile = os.path.join(full_path, filename)
486+ try:
487+ f = open(logfile, 'w')
488+ except Exception:
489+ return "ERROR: could not open file %s for writing:\n" % logfile
490+ f.write(contents)
491+ f.close()
492+ return None
493+
494+ wizard = self.browse(cr, uid, ids[0], context=context)
495+ # Retrieve connection and access methods
496+ conf_obj = self.pool.get('openupgrade.comparison.config')
497+ connection = conf_obj.get_connection(
498+ cr, uid, [wizard.server_config.id], context=context)
499+ remote_record_obj = connection.get_model('openupgrade.record')
500+ local_record_obj = self.pool.get('openupgrade.record')
501+
502+ # Retrieve field representations and compare
503+ remote_records = remote_record_obj.field_dump(context)
504+ local_records = local_record_obj.field_dump(cr, uid, context)
505+ res = compare.compare_sets(remote_records, local_records)
506+
507+ # Retrieve xml id representations and compare
508+ fields = ['module', 'model', 'name']
509+ local_xml_record_ids = local_record_obj.search(
510+ cr, uid, [('type', '=', 'xmlid')])
511+ remote_xml_record_ids = remote_record_obj.search(
512+ [('type', '=', 'xmlid')])
513+ local_xml_records = [
514+ dict([(field, x[field]) for field in fields])
515+ for x in local_record_obj.read(
516+ cr, uid, local_xml_record_ids, fields)
517+ ]
518+ remote_xml_records = [
519+ dict([(field, x[field]) for field in fields])
520+ for x in remote_record_obj.read(
521+ remote_xml_record_ids, fields)
522+ ]
523+ res_xml = compare.compare_xml_sets(
524+ remote_xml_records, local_xml_records)
525+
526+ # reorder and output the result
527+ keys = list(set(res.keys() + res_xml.keys()))
528+ keys.remove('general')
529+ keys = ['general'] + keys
530+ module_obj = self.pool.get('ir.module.module')
531+ module_ids = module_obj.search(
532+ cr, uid, [('state', '=', 'installed')])
533+ modules = dict([(x['name'], x) for x in module_obj.read(cr, uid, module_ids)])
534+ general = ''
535+ for key in keys:
536+ contents = "---%s---\n" % key
537+ if key in res:
538+ contents += '\n'.join([unicode(line) for line in sorted(res[key])])
539+ if res[key]:
540+ contents += '\n'
541+ if key in res_xml:
542+ contents += '\n'.join([unicode(line) for line in sorted(res_xml[key])])
543+ if res_xml[key]:
544+ contents += '\n'
545+ if key == 'general':
546+ general += contents
547+ continue
548+ if key not in modules:
549+ general += (
550+ "ERROR: module not in list of installed modules:\n"
551+ + contents)
552+ continue
553+ if wizard.write:
554+ error = write_file(
555+ key, modules[key]['installed_version'], contents)
556+ if error:
557+ general += error
558+ general += contents
559+ else:
560+ general += contents
561+
562+ # Store the general log in as many places as possible ;-)
563+ if wizard.write and 'base' in modules:
564+ write_file(
565+ 'base', modules['base']['installed_version'], general,
566+ 'openupgrade_general_log.txt')
567+ self.pool.get('openupgrade.comparison.config').write(
568+ cr, uid, wizard.server_config.id,
569+ {'last_log': general})
570+ self.write(cr, uid, ids, {'state': 'ready', 'log': general})
571+
572+ result = {
573+ 'name': self._description,
574+ 'view_type': 'form',
575+ 'view_mode': 'form',
576+ 'res_model': 'openupgrade.analysis.wizard',
577+ 'domain': [],
578+ 'context': context,
579+ 'type': 'ir.actions.act_window',
580+ #'target': 'new',
581+ 'res_id': ids[0],
582+ }
583+ return result
584+
585+openupgrade_analysis_wizard()
586+
587
588=== added file 'bin/addons/openupgrade_records/model/comparison_config.py'
589--- bin/addons/openupgrade_records/model/comparison_config.py 1970-01-01 00:00:00 +0000
590+++ bin/addons/openupgrade_records/model/comparison_config.py 2012-05-09 12:36:18 +0000
591@@ -0,0 +1,98 @@
592+# -*- coding: utf-8 -*-
593+##############################################################################
594+#
595+# OpenERP, Open Source Management Solution
596+# This module Copyright (C) 2012 OpenUpgrade community
597+# https://launchpad.net/~openupgrade-committers
598+#
599+# Contributors:
600+# Therp BV <http://therp.nl>
601+#
602+# This program is free software: you can redistribute it and/or modify
603+# it under the terms of the GNU Affero General Public License as
604+# published by the Free Software Foundation, either version 3 of the
605+# License, or (at your option) any later version.
606+#
607+# This program is distributed in the hope that it will be useful,
608+# but WITHOUT ANY WARRANTY; without even the implied warranty of
609+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
610+# GNU Affero General Public License for more details.
611+#
612+# You should have received a copy of the GNU Affero General Public License
613+# along with this program. If not, see <http://www.gnu.org/licenses/>.
614+#
615+##############################################################################
616+
617+from osv import osv, fields
618+import openerplib
619+from tools.translate import _
620+
621+class openupgrade_comparison_config(osv.osv):
622+ _name = 'openupgrade.comparison.config'
623+ _columns = {
624+ 'name': fields.char('Name', size=64),
625+ 'server': fields.char('Server', size=64, required=True),
626+ 'port': fields.integer('Port', required=True),
627+ 'protocol': fields.selection(
628+ [('http://', 'XML-RPC')],
629+ # ('https://', 'XML-RPC Secure')], not supported by libopenerp
630+ 'Protocol', required=True),
631+ 'database': fields.char('Database', size=64, required=True),
632+ 'username': fields.char('Username', size=24, required=True),
633+ 'password': fields.char('Password', size=24, required=True, password=True),
634+ 'last_log': fields.text('Last log'),
635+ }
636+ _defaults = {
637+ 'port': lambda *a: 8069,
638+ 'protocol': lambda *a: 'http://',
639+ }
640+
641+ def get_connection(self, cr, uid, ids, context=None):
642+ if not ids:
643+ raise osv.except_osv(
644+ _("Cannot connect"), _("Invalid id passed."))
645+ conf = self.read(cr, uid, ids[0], context=None)
646+ return openerplib.get_connection(
647+ hostname=conf['server'],
648+ database=conf['database'],
649+ login=conf['username'],
650+ password=conf['password'],
651+ port=conf['port'],
652+ )
653+
654+ def test_connection(self, cr, uid, ids, context=None):
655+ try:
656+ connection = self.get_connection(cr, uid, [ids[0]], context)
657+ user_model = connection.get_model("res.users")
658+ ids = user_model.search([("login", "=", "admin")])
659+ user_info = user_model.read(ids[0], ["name"])
660+ except Exception, e:
661+ raise osv.except_osv(
662+ _("Connection failed."), unicode(e))
663+ raise osv.except_osv(
664+ _("Connection succesful."),
665+ _("%s is connected.") % user_info["name"]
666+ )
667+
668+ def analyze(self, cr, uid, ids, context=None):
669+ """
670+ Run the analysis wizard
671+ """
672+ wizard_obj = self.pool.get('openupgrade.analysis.wizard')
673+ wizard_id = wizard_obj.create(
674+ cr, uid, {'server_config': ids[0]}, context)
675+ result = {
676+ 'name': wizard_obj._description,
677+ 'view_type': 'form',
678+ 'view_mode': 'form',
679+ 'res_model': 'openupgrade.analysis.wizard',
680+ 'domain': [],
681+ 'context': context,
682+ 'type': 'ir.actions.act_window',
683+ 'target': 'new',
684+ 'res_id': wizard_id,
685+ 'nodestroy': True,
686+ }
687+ return result
688+
689+openupgrade_comparison_config()
690
691=== added file 'bin/addons/openupgrade_records/model/generate_records_wizard.py'
692--- bin/addons/openupgrade_records/model/generate_records_wizard.py 1970-01-01 00:00:00 +0000
693+++ bin/addons/openupgrade_records/model/generate_records_wizard.py 2012-05-09 12:36:18 +0000
694@@ -0,0 +1,90 @@
695+# -*- coding: utf-8 -*-
696+##############################################################################
697+#
698+# OpenERP, Open Source Management Solution
699+# This module Copyright (C) 2012 OpenUpgrade community
700+# https://launchpad.net/~openupgrade-committers
701+#
702+# Contributors:
703+# Therp BV <http://therp.nl>
704+#
705+# This program is free software: you can redistribute it and/or modify
706+# it under the terms of the GNU Affero General Public License as
707+# published by the Free Software Foundation, either version 3 of the
708+# License, or (at your option) any later version.
709+#
710+# This program is distributed in the hope that it will be useful,
711+# but WITHOUT ANY WARRANTY; without even the implied warranty of
712+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
713+# GNU Affero General Public License for more details.
714+#
715+# You should have received a copy of the GNU Affero General Public License
716+# along with this program. If not, see <http://www.gnu.org/licenses/>.
717+#
718+##############################################################################
719+
720+import os
721+from osv import osv, fields
722+import pooler
723+try:
724+ from openerp.openupgrade import openupgrade_tools
725+except ImportError:
726+ from openupgrade import openupgrade_tools
727+
728+class generate_records_wizard(osv.osv_memory):
729+ _name = 'openupgrade.generate.records.wizard'
730+ _description = 'OpenUpgrade Generate Records Wizard'
731+ _columns = {
732+ 'state': fields.selection([('init', 'init'), ('ready', 'ready')], 'State'),
733+ }
734+ _defaults = {
735+ 'state': lambda *a: 'init',
736+ }
737+
738+ def generate(self, cr, uid, ids, context=None):
739+ """
740+ Main wizard step. Make sure that all modules are up-to-date,
741+ then reinitialize all installed modules.
742+ Equivalent of running the server with '-d <database> --init all'
743+
744+ The goal of this is to fill the records table.
745+
746+ TODO: update module list and versions, then update all modules?
747+ """
748+ # Truncate the records table
749+ if (openupgrade_tools.table_exists(cr, 'openupgrade_attribute') and
750+ openupgrade_tools.table_exists(cr, 'openupgrade_record')):
751+ cr.execute(
752+ 'TRUNCATE openupgrade_attribute, openupgrade_record;'
753+ )
754+
755+ # Need to get all modules in state 'installed'
756+ module_obj = self.pool.get('ir.module.module')
757+ module_ids = module_obj.search(
758+ cr, uid, [('state', 'in', ['to install', 'to upgrade'])])
759+ if module_ids:
760+ cr.commit()
761+ _db, pool = pooler.restart_pool(cr.dbname, update_module=True)
762+ # Did we succeed above?
763+ module_ids = module_obj.search(
764+ cr, uid, [('state', 'in', ['to install', 'to upgrade'])])
765+ if module_ids:
766+ modules = module_obj.read(
767+ cr, uid, module_ids, ['name'], context=context)
768+ raise except_osv(
769+ "Cannot reliably generate records",
770+ ("Cannot seem to install or upgrade modules " +
771+ ', '.join([x['name'] for x in modules])))
772+ # Now reinitialize all installed modules
773+ module_ids = module_obj.search(
774+ cr, uid, [('state', '=', 'installed')])
775+ module_obj.write(
776+ cr, uid, module_ids, {'state': 'to install'})
777+ cr.commit()
778+ _db, pool = pooler.restart_pool(cr.dbname, update_module=True)
779+ self.write(cr, uid, ids, {'state': 'ready'})
780+ # and we are done
781+ return True
782+
783+generate_records_wizard()
784+
785
786=== added file 'bin/addons/openupgrade_records/model/install_all_wizard.py'
787--- bin/addons/openupgrade_records/model/install_all_wizard.py 1970-01-01 00:00:00 +0000
788+++ bin/addons/openupgrade_records/model/install_all_wizard.py 2012-05-09 12:36:18 +0000
789@@ -0,0 +1,113 @@
790+# -*- coding: utf-8 -*-
791+##############################################################################
792+#
793+# OpenERP, Open Source Management Solution
794+# This module Copyright (C) 2012 OpenUpgrade community
795+# https://launchpad.net/~openupgrade-committers
796+#
797+# Contributors:
798+# Therp BV <http://therp.nl>
799+#
800+# This program is free software: you can redistribute it and/or modify
801+# it under the terms of the GNU Affero General Public License as
802+# published by the Free Software Foundation, either version 3 of the
803+# License, or (at your option) any later version.
804+#
805+# This program is distributed in the hope that it will be useful,
806+# but WITHOUT ANY WARRANTY; without even the implied warranty of
807+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
808+# GNU Affero General Public License for more details.
809+#
810+# You should have received a copy of the GNU Affero General Public License
811+# along with this program. If not, see <http://www.gnu.org/licenses/>.
812+#
813+##############################################################################
814+
815+import time
816+import os
817+from osv import osv, fields
818+import pooler
819+
820+class install_all_wizard(osv.osv_memory):
821+ _name = 'openupgrade.install.all.wizard'
822+ _description = 'OpenUpgrade Install All Wizard'
823+ _columns = {
824+ 'state': fields.selection([('init', 'init'), ('ready', 'ready')], 'State', readonly=True),
825+ 'to_install': fields.integer('Number of modules to install', readonly=True),
826+ }
827+ _defaults = {
828+ 'state': lambda *a: 'init',
829+ }
830+
831+
832+ def default_get(self, cr, uid, fields, context=None):
833+ """
834+ Update module list and retrieve the number
835+ of installable modules
836+ """
837+ res = super(install_all_wizard, self).default_get(
838+ cr, uid, fields, context=None)
839+ module_obj = self.pool.get('ir.module.module')
840+ update, add = module_obj.update_list(cr, uid,)
841+ print "%s modules added" % add
842+ module_ids = module_obj.search(
843+ cr, uid, [('state', 'not in', ['installed', 'uninstallable', 'unknown'])])
844+ res.update(
845+ {'to_install': module_ids and len(module_ids) or False}
846+ )
847+ return res
848+
849+ def quirk_fiscalyear(self, cr, uid, ids, context=None):
850+ """
851+ Install account module first and create a fiscal year,
852+ in order to prevent "No fiscal year defined" exception
853+ during an upgrade or reinstallation of the account module.
854+
855+ Refer to account_fiscalyear.find(), which is called as
856+ a default function by the orm upon module upgrade.
857+ """
858+ module_obj = self.pool.get('ir.module.module')
859+ pool = self.pool
860+ # Retrieve status of the account module
861+ account_module_id = module_obj.search(
862+ cr, uid, [('name', '=', 'account')], context=context)[0]
863+ state = module_obj.read(
864+ cr, uid, account_module_id, ['state'], context=context)['state']
865+ if state != 'installed':
866+ # Cancel installation of other modules
867+ module_ids = module_obj.search(
868+ cr, uid, [('state', '=', 'to install')])
869+ module_obj.write(cr, uid, module_ids, {'state': 'uninstalled'})
870+ # Mark the module and its dependencies
871+ module_obj.button_install(cr, uid, [account_module_id])
872+ # Install account module
873+ cr.commit()
874+ _db, pool = pooler.restart_pool(cr.dbname, update_module=True)
875+ # get or create today's fiscal year
876+ fy_obj = pool.get('account.fiscalyear')
877+ if not fy_obj.find(cr, uid, False, exception=False, context=context):
878+ fy_obj.create(cr, uid, {
879+ 'name': time.strftime('%Y'),
880+ 'code': time.strftime('%Y'),
881+ 'date_start': "%s-01-01" % time.strftime('%Y'),
882+ 'date_stop': "%s-12-31" % time.strftime('%Y'),
883+ })
884+
885+ def install_all(self, cr, uid, ids, context=None):
886+ """
887+ Main wizard step. Set all installable modules to install
888+ and actually install them.
889+ """
890+ module_obj = self.pool.get('ir.module.module')
891+ module_ids = module_obj.search(
892+ cr, uid, [('state', 'not in', ['installed', 'uninstallable', 'unknown'])])
893+ if module_ids:
894+ module_obj.write(
895+ cr, uid, module_ids, {'state': 'to install'})
896+ cr.commit()
897+ _db, pool = pooler.restart_pool(cr.dbname, update_module=True)
898+ self.write(cr, uid, ids, {'state': 'ready'})
899+ return True
900+
901+install_all_wizard()
902+
903
904=== added file 'bin/addons/openupgrade_records/model/openupgrade_record.py'
905--- bin/addons/openupgrade_records/model/openupgrade_record.py 1970-01-01 00:00:00 +0000
906+++ bin/addons/openupgrade_records/model/openupgrade_record.py 2012-05-09 12:36:18 +0000
907@@ -0,0 +1,111 @@
908+# -*- coding: utf-8 -*-
909+##############################################################################
910+#
911+# OpenERP, Open Source Management Solution
912+# This module Copyright (C) 2012 OpenUpgrade community
913+# https://launchpad.net/~openupgrade-committers
914+#
915+# Contributors:
916+# Therp BV <http://therp.nl>
917+#
918+# This program is free software: you can redistribute it and/or modify
919+# it under the terms of the GNU Affero General Public License as
920+# published by the Free Software Foundation, either version 3 of the
921+# License, or (at your option) any later version.
922+#
923+# This program is distributed in the hope that it will be useful,
924+# but WITHOUT ANY WARRANTY; without even the implied warranty of
925+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
926+# GNU Affero General Public License for more details.
927+#
928+# You should have received a copy of the GNU Affero General Public License
929+# along with this program. If not, see <http://www.gnu.org/licenses/>.
930+#
931+##############################################################################
932+
933+from osv import osv, fields
934+
935+# Cannot use forward references in 6.0
936+class openupgrade_record(osv.osv):
937+ _name = 'openupgrade.record'
938+openupgrade_record()
939+
940+class openupgrade_attribute(osv.osv):
941+ _name = 'openupgrade.attribute'
942+ _rec_name = 'attribute_id'
943+ _columns = {
944+ 'name': fields.char(
945+ 'Name', size=24,
946+ readonly=True,
947+ ),
948+ 'value': fields.char(
949+ 'Value',
950+ size=4096,
951+ readonly=True,
952+ ),
953+ 'record_id': fields.many2one(
954+ 'openupgrade.record', ondelete='CASCADE',
955+ readonly=True,
956+ ),
957+ }
958+openupgrade_attribute()
959+
960+class openupgrade_record(osv.osv):
961+ _inherit = 'openupgrade.record'
962+
963+ _columns = {
964+ 'name': fields.char('Name', size=256, readonly=True),
965+ 'module': fields.char('Module', size=128, readonly=True),
966+ 'model': fields.char('Model', size=128, readonly=True),
967+ 'field': fields.char('Field', size=128, readonly=True),
968+ 'mode': fields.selection(
969+ [('create', 'Create'), ('modify', 'Modify')],
970+ 'Mode',
971+ help='Set to Create if a field is newly created '
972+ 'in this module. If this module modifies an attribute of an '
973+ 'exting field, set to Modify.',
974+ readonly=True,
975+ ),
976+ 'type': fields.selection(
977+ [('field', 'Field'), ('xmlid', 'XML ID')],
978+ 'Type',
979+ readonly=True,
980+ ),
981+ 'attribute_ids': fields.one2many(
982+ 'openupgrade.attribute', 'record_id', 'Attributes',
983+ readonly=True,
984+ ),
985+ }
986+ def field_dump(self, cr, uid, context=None):
987+ keys = [
988+ 'module',
989+ 'mode',
990+ 'model',
991+ 'field',
992+ 'type',
993+ 'isfunction',
994+ 'relation',
995+ 'required',
996+ 'selection_keys',
997+ 'req_default',
998+ 'inherits',
999+ ]
1000+
1001+ template = dict([(x, False) for x in keys])
1002+ ids = self.search(cr, uid, [('type', '=', 'field')], context=context)
1003+ records = self.browse(cr, uid, ids, context=context)
1004+ data = []
1005+ for record in records:
1006+ repr = template.copy()
1007+ repr.update({
1008+ 'module': record.module,
1009+ 'model': record.model,
1010+ 'field': record.field,
1011+ 'mode': record.mode,
1012+ })
1013+ repr.update(
1014+ dict([(x.name, x.value) for x in record.attribute_ids]))
1015+ data.append(repr)
1016+ return data
1017+
1018+openupgrade_record()
1019
1020=== added directory 'bin/addons/openupgrade_records/security'
1021=== added file 'bin/addons/openupgrade_records/security/ir.model.access.csv'
1022--- bin/addons/openupgrade_records/security/ir.model.access.csv 1970-01-01 00:00:00 +0000
1023+++ bin/addons/openupgrade_records/security/ir.model.access.csv 2012-05-09 12:36:18 +0000
1024@@ -0,0 +1,3 @@
1025+"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
1026+"access_openupgrade_record","openupgrade.record all","model_openupgrade_record",,1,0,0,0
1027+"access_openupgrade_attribute","openupgrade.attribute all","model_openupgrade_attribute",,1,0,0,0
1028
1029=== added directory 'bin/addons/openupgrade_records/view'
1030=== added file 'bin/addons/openupgrade_records/view/analysis_wizard.xml'
1031--- bin/addons/openupgrade_records/view/analysis_wizard.xml 1970-01-01 00:00:00 +0000
1032+++ bin/addons/openupgrade_records/view/analysis_wizard.xml 2012-05-09 12:36:18 +0000
1033@@ -0,0 +1,30 @@
1034+<?xml version="1.0" encoding="utf-8"?>
1035+<openerp>
1036+ <data>
1037+ <record id="view_openupgrade_analysis_wizard_form" model="ir.ui.view">
1038+ <field name="name">view.openupgrade.analysis_wizard.form</field>
1039+ <field name="model">openupgrade.analysis.wizard</field>
1040+ <field name="type">form</field>
1041+ <field name="arch" type="xml">
1042+ <form string="OpenUpgrade Analysis Wizard">
1043+ <field name="server_config" readonly="1"/>
1044+ <field name="state"/>
1045+ <field name="log" colspan="4"
1046+ attrs="{'invisible': [('state', '!=', 'ready')]}"/>
1047+ <field name="write"
1048+ attrs="{'readonly': [('state', '!=', 'init')]}"/>
1049+ <button icon="gtk-close"
1050+ special="cancel"
1051+ string="Close"
1052+ />
1053+ <button icon="gtk-ok"
1054+ string="Create"
1055+ name="get_communication"
1056+ type="object"
1057+ states="init"
1058+ />
1059+ </form>
1060+ </field>
1061+ </record>
1062+ </data>
1063+</openerp>
1064
1065=== added file 'bin/addons/openupgrade_records/view/comparison_config.xml'
1066--- bin/addons/openupgrade_records/view/comparison_config.xml 1970-01-01 00:00:00 +0000
1067+++ bin/addons/openupgrade_records/view/comparison_config.xml 2012-05-09 12:36:18 +0000
1068@@ -0,0 +1,62 @@
1069+<?xml version="1.0" encoding="utf-8"?>
1070+<openerp>
1071+ <data>
1072+ <record id="view_openupgrade_comparison_config_tree" model="ir.ui.view">
1073+ <field name="name">view.openupgrade.comparison_config.tree</field>
1074+ <field name="model">openupgrade.comparison.config</field>
1075+ <field name="type">tree</field>
1076+ <field name="arch" type="xml">
1077+ <tree string="OpenUpgrade Comparison Config">
1078+ <field name="name" select="1"/>
1079+ <field name="protocol"/>
1080+ <field name="server" select="1"/>
1081+ <field name="port" select="1"/>
1082+ <field name="database" select="1"/>
1083+ </tree>
1084+ </field>
1085+ </record>
1086+ <record id="view_openupgrade_comparison_config_form" model="ir.ui.view">
1087+ <field name="name">view.openupgrade.comparison_config.form</field>
1088+ <field name="model">openupgrade.comparison.config</field>
1089+ <field name="type">form</field>
1090+ <field name="arch" type="xml">
1091+ <form string="OpenUpgrade Comparison Config">
1092+ <field name="name"/>
1093+ <field name="protocol"/>
1094+ <field name="server"/>
1095+ <field name="port"/>
1096+ <field name="database"/>
1097+ <field name="username"/>
1098+ <field name="password" password="1"/>
1099+ <button
1100+ name="test_connection"
1101+ string="Test Connection"
1102+ type="object" icon="gtk-network"
1103+ colspan="2"
1104+ />
1105+ <newline/>
1106+ <button
1107+ name="analyze"
1108+ string="Perform Analysis"
1109+ type="object" icon="gtk-execute"
1110+ colspan="2"
1111+ />
1112+ <separator string="Last log" colspan="4"/>
1113+ <field name="last_log" nolabel="1" colspan="4"/>
1114+ </form>
1115+ </field>
1116+ </record>
1117+ <record id="action_openupgrade_comparison_config_tree" model="ir.actions.act_window">
1118+ <field name="name">OpenUpgrade Comparison Configs</field>
1119+ <field name="type">ir.actions.act_window</field>
1120+ <field name="res_model">openupgrade.comparison.config</field>
1121+ <field name="view_type">form</field>
1122+ </record>
1123+ <menuitem
1124+ action="action_openupgrade_comparison_config_tree"
1125+ id="menu_openupgrade_comparison_config"
1126+ name="Comparison Configurations"
1127+ parent="menu_openupgrade"
1128+ />
1129+ </data>
1130+</openerp>
1131
1132=== added file 'bin/addons/openupgrade_records/view/generate_records_wizard.xml'
1133--- bin/addons/openupgrade_records/view/generate_records_wizard.xml 1970-01-01 00:00:00 +0000
1134+++ bin/addons/openupgrade_records/view/generate_records_wizard.xml 2012-05-09 12:36:18 +0000
1135@@ -0,0 +1,52 @@
1136+<?xml version="1.0" encoding="utf-8"?>
1137+<openerp>
1138+ <data>
1139+ <record id="view_openupgrade_generate_records_wizard_form" model="ir.ui.view">
1140+ <field name="name">view.openupgrade.generate_records_wizard.form</field>
1141+ <field name="model">openupgrade.generate.records.wizard</field>
1142+ <field name="type">form</field>
1143+ <field name="arch" type="xml">
1144+ <form string="OpenUpgrade Generate Records Wizard">
1145+ <group states="init" colspan="4">
1146+ <label string="This will reinitialize all the modules installed on this database. Do not continue if you use this database in production."
1147+ />
1148+ <button icon="gtk-close"
1149+ special="cancel"
1150+ string="Cancel"
1151+ />
1152+ <button icon="gtk-ok"
1153+ string="Continue"
1154+ name="generate"
1155+ type="object"
1156+ />
1157+ </group>
1158+ <group states="ready" colspan="4">
1159+ <label string="Modules initialized and records created"
1160+ />
1161+ <field name="state" invisible="1"/>
1162+ <button icon="gtk-close"
1163+ special="cancel"
1164+ string="Close"
1165+ />
1166+ </group>
1167+ </form>
1168+ </field>
1169+ </record>
1170+
1171+ <record id="action_generate_records" model="ir.actions.act_window">
1172+ <field name="name">Generate Records</field>
1173+ <field name="type">ir.actions.act_window</field>
1174+ <field name="res_model">openupgrade.generate.records.wizard</field>
1175+ <field name="view_type">form</field>
1176+ <field name="view_mode">form,tree</field>
1177+ <field name="target">new</field>
1178+ </record>
1179+
1180+ <menuitem name="Generate Records"
1181+ id="menu_openupgrade_generate_records"
1182+ parent="menu_openupgrade"
1183+ action="action_generate_records"
1184+ sequence="15"/>
1185+
1186+ </data>
1187+</openerp>
1188
1189=== added file 'bin/addons/openupgrade_records/view/install_all_wizard.xml'
1190--- bin/addons/openupgrade_records/view/install_all_wizard.xml 1970-01-01 00:00:00 +0000
1191+++ bin/addons/openupgrade_records/view/install_all_wizard.xml 2012-05-09 12:36:18 +0000
1192@@ -0,0 +1,54 @@
1193+<?xml version="1.0" encoding="utf-8"?>
1194+<openerp>
1195+ <data>
1196+ <record id="view_openupgrade_install_all_wizard_form" model="ir.ui.view">
1197+ <field name="name">view.openupgrade.install_all_wizard.form</field>
1198+ <field name="model">openupgrade.install.all.wizard</field>
1199+ <field name="type">form</field>
1200+ <field name="arch" type="xml">
1201+ <form string="OpenUpgrade Install All Modules Wizard">
1202+ <group states="init" colspan="4">
1203+ <label string="This will install all modules on the database. Do not continue if you use this database in production." colspan="4"
1204+ />
1205+ <field name="to_install"/>
1206+ <newline/>
1207+ <button icon="gtk-close"
1208+ special="cancel"
1209+ string="Cancel"
1210+ />
1211+ <button icon="gtk-ok"
1212+ string="Continue"
1213+ name="install_all"
1214+ type="object"
1215+ />
1216+ </group>
1217+ <group states="ready" colspan="4">
1218+ <label string="Modules installed"
1219+ />
1220+ <field name="state" invisible="1"/>
1221+ <button icon="gtk-close"
1222+ special="cancel"
1223+ string="Close"
1224+ />
1225+ </group>
1226+ </form>
1227+ </field>
1228+ </record>
1229+
1230+ <record id="action_install_all" model="ir.actions.act_window">
1231+ <field name="name">Install All Modules</field>
1232+ <field name="type">ir.actions.act_window</field>
1233+ <field name="res_model">openupgrade.install.all.wizard</field>
1234+ <field name="view_type">form</field>
1235+ <field name="view_mode">form,tree</field>
1236+ <field name="target">new</field>
1237+ </record>
1238+
1239+ <menuitem name="Install All Modules"
1240+ id="menu_openupgrade_install_all"
1241+ parent="menu_openupgrade"
1242+ action="action_install_all"
1243+ sequence="14"/>
1244+
1245+ </data>
1246+</openerp>
1247
1248=== added file 'bin/addons/openupgrade_records/view/openupgrade_record.xml'
1249--- bin/addons/openupgrade_records/view/openupgrade_record.xml 1970-01-01 00:00:00 +0000
1250+++ bin/addons/openupgrade_records/view/openupgrade_record.xml 2012-05-09 12:36:18 +0000
1251@@ -0,0 +1,65 @@
1252+<?xml version="1.0" encoding="utf-8"?>
1253+<openerp>
1254+ <data>
1255+ <!-- Top level menu under 'Database structure' -->
1256+ <menuitem
1257+ id="menu_openupgrade"
1258+ name="OpenUpgrade Development"
1259+ parent="base.menu_administration"
1260+ sequence="99"
1261+ />
1262+ <record id="view_openupgrade_record_tree" model="ir.ui.view">
1263+ <field name="name">view.openupgrade.record.tree</field>
1264+ <field name="model">openupgrade.record</field>
1265+ <field name="type">tree</field>
1266+ <field name="arch" type="xml">
1267+ <tree string="OpenUpgrade Records">
1268+ <field name="module" select="1"/>
1269+ <field name="model" select="1"/>
1270+ <field name="field" select="1"/>
1271+ <field name="name" select="1"/>
1272+ <field name="type" select="1"/>
1273+ <field name="mode" select="1"/>
1274+ </tree>
1275+ </field>
1276+ </record>
1277+ <record id="view_openupgrade_record_form" model="ir.ui.view">
1278+ <field name="name">view.openupgrade.record.form</field>
1279+ <field name="model">openupgrade.record</field>
1280+ <field name="type">form</field>
1281+ <field name="arch" type="xml">
1282+ <form string="OpenUpgrade Record">
1283+ <field name="module" select="1"/>
1284+ <field name="model" select="1"/>
1285+ <field name="field" select="1"/>
1286+ <field name="name" select="1"/>
1287+ <field name="type" select="1"/>
1288+ <field name="mode" select="1"/>
1289+ <separator string="Attributes" colspan="4"/>
1290+ <field name="attribute_ids" mode="tree,form" nolabel="1" colspan="4">
1291+ <tree string="Attributes">
1292+ <field name="name"/>
1293+ <field name="value"/>
1294+ </tree>
1295+ <form string="Attribute">
1296+ <field name="name"/>
1297+ <field name="value"/>
1298+ </form>
1299+ </field>
1300+ </form>
1301+ </field>
1302+ </record>
1303+ <record id="action_openupgrade_record_tree" model="ir.actions.act_window">
1304+ <field name="name">OpenUpgrade Records</field>
1305+ <field name="type">ir.actions.act_window</field>
1306+ <field name="res_model">openupgrade.record</field>
1307+ <field name="view_type">form</field>
1308+ </record>
1309+ <menuitem
1310+ action="action_openupgrade_record_tree"
1311+ id="menu_openupgrade_records"
1312+ name="Records"
1313+ parent="menu_openupgrade"
1314+ />
1315+ </data>
1316+</openerp>
1317
1318=== added directory 'bin/openupgrade'
1319=== added file 'bin/openupgrade/__init__.py'
1320=== added directory 'bin/openupgrade/doc'
1321=== added file 'bin/openupgrade/doc/readme.txt'
1322--- bin/openupgrade/doc/readme.txt 1970-01-01 00:00:00 +0000
1323+++ bin/openupgrade/doc/readme.txt 2012-05-09 12:36:18 +0000
1324@@ -0,0 +1,2 @@
1325+The documentation of this project is currently maintained in the
1326+6.1 branch. You can also consult it at http://readthedocs.org/docs/openupgrade-server
1327
1328=== added file 'bin/openupgrade/openupgrade.py'
1329--- bin/openupgrade/openupgrade.py 1970-01-01 00:00:00 +0000
1330+++ bin/openupgrade/openupgrade.py 2012-05-09 12:36:18 +0000
1331@@ -0,0 +1,259 @@
1332+# -*- coding: utf-8 -*-
1333+import os
1334+from osv import osv
1335+import pooler
1336+import logging
1337+import tools
1338+import openupgrade_tools
1339+
1340+logger = logging.getLogger('OpenUpgrade')
1341+
1342+__all__ = [
1343+ 'load_data',
1344+ 'rename_columns',
1345+ 'rename_tables',
1346+ 'drop_columns',
1347+ 'table_exists',
1348+ 'column_exists',
1349+ 'delete_model_workflow',
1350+ 'set_defaults',
1351+ 'update_module_names',
1352+ 'add_ir_model_fields',
1353+]
1354+
1355+def load_data(cr, module_name, filename, idref=None, mode='init'):
1356+ """
1357+ Load an xml or csv data file from your post script. The usual case for this is the
1358+ occurrence of newly added essential or useful data in the module that is
1359+ marked with "noupdate='1'" and without "forcecreate='1'" so that it will
1360+ not be loaded by the usual upgrade mechanism. Leaving the 'mode' argument to
1361+ its default 'init' will load the data from your migration script.
1362+
1363+ Theoretically, you could simply load a stock file from the module, but be
1364+ careful not to reinitialize any data that could have been customized.
1365+ Preferably, select only the newly added items. Copy these to a file
1366+ in your migrations directory and load that file.
1367+ Leave it to the user to actually delete existing resources that are
1368+ marked with 'noupdate' (other named items will be deleted
1369+ automatically).
1370+
1371+
1372+ :param module_name: the name of the module
1373+ :param filename: the path to the filename, relative to the module \
1374+ directory.
1375+ :param idref: optional hash with ?id mapping cache?
1376+ :param mode: one of 'init', 'update', 'demo'. Always use 'init' for adding new items \
1377+ from files that are marked with 'noupdate'. Defaults to 'init'.
1378+
1379+ """
1380+
1381+ if idref is None:
1382+ idref = {}
1383+ logger.info('%s: loading %s' % (module_name, filename))
1384+ _, ext = os.path.splitext(filename)
1385+ pathname = os.path.join(module_name, filename)
1386+ fp = tools.file_open(pathname)
1387+ try:
1388+ if ext == '.csv':
1389+ noupdate = True
1390+ tools.convert_csv_import(cr, module_name, pathname, fp.read(), idref, mode, noupdate)
1391+ else:
1392+ tools.convert_xml_import(cr, module_name, fp, idref, mode=mode)
1393+ finally:
1394+ fp.close()
1395+
1396+# for backwards compatibility
1397+load_xml = load_data
1398+table_exists = openupgrade_tools.table_exists
1399+
1400+def rename_columns(cr, column_spec):
1401+ """
1402+ Rename table columns. Typically called in the pre script.
1403+
1404+ :param column_spec: a hash with table keys, with lists of tuples as values. \
1405+ Tuples consist of (old_name, new_name).
1406+
1407+ """
1408+ for table in column_spec.keys():
1409+ for (old, new) in column_spec[table]:
1410+ logger.info("table %s, column %s: renaming to %s",
1411+ table, old, new)
1412+ cr.execute('ALTER TABLE "%s" RENAME "%s" TO "%s"' % (table, old, new,))
1413+
1414+def rename_tables(cr, table_spec):
1415+ """
1416+ Rename tables. Typically called in the pre script.
1417+ :param column_spec: a list of tuples (old table name, new table name).
1418+
1419+ """
1420+ for (old, new) in table_spec:
1421+ logger.info("table %s: renaming to %s",
1422+ old, new)
1423+ cr.execute('ALTER TABLE "%s" RENAME TO "%s"' % (old, new,))
1424+
1425+def rename_models(cr, model_spec):
1426+ """
1427+ Rename models. Typically called in the pre script.
1428+ :param column_spec: a list of tuples (old table name, new table name).
1429+
1430+ Use case: if a model changes name, but still implements equivalent
1431+ functionality you will want to update references in for instance
1432+ relation fields.
1433+
1434+ """
1435+ for (old, new) in model_spec:
1436+ logger.info("model %s: renaming to %s",
1437+ old, new)
1438+ cr.execute('UPDATE ir_model_fields SET relation = %s '
1439+ 'WHERE relation = %s', (new, old,))
1440+
1441+def drop_columns(cr, column_spec):
1442+ """
1443+ Drop columns but perform an additional check if a column exists.
1444+ This covers the case of function fields that may or may not be stored.
1445+ Consider that this may not be obvious: an additional module can govern
1446+ a function fields' store properties.
1447+
1448+ :param column_spec: a list of (table, column) tuples
1449+ """
1450+ for (table, column) in column_spec:
1451+ logger.info("table %s: drop column %s",
1452+ table, column)
1453+ if column_exists(cr, table, column):
1454+ cr.execute('ALTER TABLE "%s" DROP COLUMN "%s"' %
1455+ (table, column))
1456+ else:
1457+ logger.warn("table %s: column %s did not exist",
1458+ table, column)
1459+
1460+def delete_model_workflow(cr, model):
1461+ """
1462+ Forcefully remove active workflows for obsolete models,
1463+ to prevent foreign key issues when the orm deletes the model.
1464+ """
1465+ logged_query(
1466+ cr,
1467+ "DELETE FROM wkf_workitem WHERE act_id in "
1468+ "( SELECT wkf_activity.id "
1469+ " FROM wkf_activity, wkf "
1470+ " WHERE wkf_id = wkf.id AND "
1471+ " wkf.osv = %s"
1472+ ")", (model,))
1473+ logged_query(
1474+ cr,
1475+ "DELETE FROM wkf WHERE osv = %s", (model,))
1476+
1477+def set_defaults(cr, pool, default_spec, force=False):
1478+ """
1479+ Set default value. Useful for fields that are newly required. Uses orm, so
1480+ call from the post script.
1481+
1482+ :param default_spec: a hash with model names as keys. Values are lists of \
1483+ tuples (field, value). None as a value has a special meaning: it assigns \
1484+ the default value. If this value is provided by a function, the function is \
1485+ called as the user that created the resource.
1486+ :param force: overwrite existing values. To be used for assigning a non- \
1487+ default value (presumably in the case of a new column). The ORM assigns \
1488+ the default value as declared in the model in an earlier stage of the \
1489+ process. Beware of issues with resources loaded from new data that \
1490+ actually do require the model's default, in combination with the post \
1491+ script possible being run multiple times.
1492+ """
1493+
1494+ def write_value(ids, field, value):
1495+ logger.info("model %s, field %s: setting default value of %d resources to %s",
1496+ model, field, len(ids), unicode(value))
1497+ obj.write(cr, 1, ids, {field: value})
1498+
1499+ for model in default_spec.keys():
1500+ obj = pool.get(model)
1501+ if not obj:
1502+ raise osv.except_osv("Migration: error setting default, no such model: %s" % model, "")
1503+
1504+ for field, value in default_spec[model]:
1505+ domain = not force and [(field, '=', False)] or []
1506+ ids = obj.search(cr, 1, domain)
1507+ if not ids:
1508+ continue
1509+ if value is None:
1510+ # Set the value by calling the _defaults of the object.
1511+ # Typically used for company_id on various models, and in that
1512+ # case the result depends on the user associated with the object.
1513+ # We retrieve create_uid for this purpose and need to call the _defaults
1514+ # function per resource. Otherwise, write all resources at once.
1515+ if field in obj._defaults:
1516+ if not callable(obj._defaults[field]):
1517+ write_value(ids, field, obj._defaults[field])
1518+ else:
1519+ # existence users is covered by foreign keys, so this is not needed
1520+ # cr.execute("SELECT %s.id, res_users.id FROM %s LEFT OUTER JOIN res_users ON (%s.create_uid = res_users.id) WHERE %s.id IN %s" %
1521+ # (obj._table, obj._table, obj._table, obj._table, tuple(ids),))
1522+ cr.execute("SELECT id, COALESCE(create_uid, 1) FROM %s " % obj._table + "WHERE id in %s", (tuple(ids),))
1523+ fetchdict = dict(cr.fetchall())
1524+ for id in ids:
1525+ write_value([id], field, obj._defaults[field](obj, cr, fetchdict.get(id, 1), None))
1526+ if id not in fetchdict:
1527+ logger.info("model %s, field %s, id %d: no create_uid defined or user does not exist anymore",
1528+ model, field, id)
1529+ else:
1530+ error = ("OpenUpgrade: error setting default, field %s with "
1531+ "None default value not in %s' _defaults" % (
1532+ field, model))
1533+ logger.error(error)
1534+ # this exeption seems to get lost in a higher up try block
1535+ osv.except_osv("OpenUpgrade", error)
1536+ else:
1537+ write_value(ids, field, value)
1538+
1539+def logged_query(cr, query, args=None):
1540+ if args is None:
1541+ args = []
1542+ res = cr.execute(query, args)
1543+ logger.debug('Running %s', query)
1544+ if not res:
1545+ query = query % args
1546+ logger.warn('No rows affected for query "%s"', query)
1547+ return res
1548+
1549+def column_exists(cr, table, column):
1550+ """ Check whether a certain column exists """
1551+ cr.execute(
1552+ 'SELECT count(attname) FROM pg_attribute '
1553+ 'WHERE attrelid = '
1554+ '( SELECT oid FROM pg_class WHERE relname = %s ) '
1555+ 'AND attname = %s',
1556+ (table, column));
1557+ return cr.fetchone()[0] == 1
1558+
1559+def update_module_names(cr, namespec):
1560+ """
1561+ Deal with changed module names of certified modules
1562+ in order to prevent 'certificate not unique' error,
1563+ as well as updating the module reference in the
1564+ XML id.
1565+
1566+ :param namespec: tuple of (old name, new name)
1567+ """
1568+ for (old_name, new_name) in namespec:
1569+ query = ("UPDATE ir_module_module SET name = %s "
1570+ "WHERE name = %s")
1571+ logged_query(cr, query, (new_name, old_name))
1572+ query = ("UPDATE ir_model_data SET module = %s "
1573+ "WHERE module = %s ")
1574+ logged_query(cr, query, (new_name, old_name))
1575+
1576+def add_ir_model_fields(cr, columnspec):
1577+ """
1578+ Typically, new columns on ir_model_fields need to be added in a very
1579+ early stage in the upgrade process of the base module, in raw sql
1580+ as they need to be in place before any model gets initialized.
1581+ Do not use for fields with additional SQL constraints, such as a
1582+ reference to another table or the cascade constraint, but craft your
1583+ own statement taking them into account.
1584+
1585+ :param columnspec: tuple of (column name, column type)
1586+ """
1587+ for column in columnspec:
1588+ query = 'ALTER TABLE ir_model_fields ADD COLUMN %s %s' % (
1589+ column)
1590+ logged_query(cr, query, [])
1591
1592=== added file 'bin/openupgrade/openupgrade_log.py'
1593--- bin/openupgrade/openupgrade_log.py 1970-01-01 00:00:00 +0000
1594+++ bin/openupgrade/openupgrade_log.py 2012-05-09 12:36:18 +0000
1595@@ -0,0 +1,56 @@
1596+# -*- coding: utf-8 -*-
1597+from openupgrade_tools import table_exists
1598+
1599+def log_xml_id(cr, module, xml_id):
1600+ """
1601+ Log xml_ids at load time in the records table.
1602+ Called from openerp/tools/convert.py:xml_import._test_xml_id()
1603+
1604+ # Catcha's
1605+ - The module needs to be loaded with 'init', or the calling method
1606+ won't be called. This can be brought about by installing the
1607+ module or updating the 'state' field of the module to 'to install'
1608+ or call the server with '--init <module>' and the database argument.
1609+
1610+ - Do you get the right results immediately when installing the module?
1611+ No, sorry. This method retrieves the model from the ir_model_table, but when
1612+ the xml id is encountered for the first time, this method is called
1613+ before the item is present in this table. Therefore, you will not
1614+ get any meaningful results until the *second* time that you 'init'
1615+ the module.
1616+
1617+ - The good news is that the openupgrade_records module that comes
1618+ with this distribution allows you to deal with all of this with
1619+ one click on the menu item Settings -> Customizations ->
1620+ Database Structure -> OpenUpgrade -> Generate Records
1621+
1622+ - You cannot reinitialize the modules in your production database
1623+ and expect to keep working on it happily ever after. Do not perform
1624+ this routine on your production database.
1625+
1626+ :param module: The module that contains the xml_id
1627+ :param xml_id: the xml_id, with or without 'module.' prefix
1628+ """
1629+ if not table_exists(cr, 'openupgrade_record'):
1630+ return
1631+ if not '.' in xml_id:
1632+ xml_id = '%s.%s' % (module, xml_id)
1633+ cr.execute(
1634+ "SELECT model FROM ir_model_data "
1635+ "WHERE module = %s AND name = %s",
1636+ xml_id.split('.'))
1637+ record = cr.fetchone()
1638+ if not record:
1639+ #print "Cannot find xml_id %s" % xml_id
1640+ return
1641+ else:
1642+ cr.execute(
1643+ "SELECT id FROM openupgrade_record "
1644+ "WHERE module=%s AND model=%s AND name=%s AND type=%s",
1645+ (module, record[0], xml_id, 'xmlid'))
1646+ if not cr.fetchone():
1647+ cr.execute(
1648+ "INSERT INTO openupgrade_record "
1649+ "(module, model, name, type) values(%s, %s, %s, %s)",
1650+ (module, record[0], xml_id, 'xmlid'))
1651+
1652
1653=== added file 'bin/openupgrade/openupgrade_tools.py'
1654--- bin/openupgrade/openupgrade_tools.py 1970-01-01 00:00:00 +0000
1655+++ bin/openupgrade/openupgrade_tools.py 2012-05-09 12:36:18 +0000
1656@@ -0,0 +1,8 @@
1657+# -*- coding: utf-8 -*-
1658+def table_exists(cr, table):
1659+ """ Check whether a certain table or view exists """
1660+ cr.execute(
1661+ 'SELECT count(relname) FROM pg_class WHERE relname = %s',
1662+ (table,))
1663+ return cr.fetchone()[0] == 1
1664+
1665
1666=== modified file 'bin/tools/convert.py'
1667--- bin/tools/convert.py 2010-12-17 12:06:16 +0000
1668+++ bin/tools/convert.py 2012-05-09 12:36:18 +0000
1669@@ -42,6 +42,8 @@
1670
1671 from tools.safe_eval import safe_eval as eval
1672
1673+from openupgrade import openupgrade_log
1674+
1675 class ConvertError(Exception):
1676 def __init__(self, doc, orig_excpt):
1677 self.d = doc
1678@@ -244,6 +246,7 @@
1679
1680 if len(id) > 64:
1681 self.logger.notifyChannel('init', netsvc.LOG_ERROR, 'id: %s is to long (max: 64)'% (id,))
1682+ openupgrade_log.log_xml_id(self.cr, self.module, xml_id)
1683
1684 def _tag_delete(self, cr, rec, data_node=None):
1685 d_model = rec.get("model",'')
1686
1687=== modified file 'bin/tools/sql.py'
1688--- bin/tools/sql.py 2009-01-04 22:13:29 +0000
1689+++ bin/tools/sql.py 2012-05-09 12:36:18 +0000
1690@@ -23,7 +23,8 @@
1691 def drop_view_if_exists(cr, viewname):
1692 cr.execute("select count(1) from pg_class where relkind=%s and relname=%s", ('v', viewname,))
1693 if cr.fetchone()[0]:
1694- cr.execute("DROP view %s" % (viewname,))
1695+ # OpenUpgrade: add CASCADE
1696+ cr.execute("DROP view %s CASCADE" % (viewname,))
1697 cr.commit()
1698
1699 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

Subscribers

People subscribed via source and target branches