credativ team mailing list archive
-
credativ team
-
Mailing list archive
-
Message #04830
[Merge] lp:~therp-nl/openupgrade-server/5.0-use_orm into lp:openupgrade-server/5.0
Stefan Rijnhart (Therp) has proposed merging lp:~therp-nl/openupgrade-server/5.0-use_orm into lp:openupgrade-server/5.0.
Requested reviews:
OpenUpgrade Committers (openupgrade-committers)
For more details, see:
https://code.launchpad.net/~therp-nl/openupgrade-server/5.0-use_orm/+merge/105195
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
--
https://code.launchpad.net/~therp-nl/openupgrade-server/5.0-use_orm/+merge/105195
Your team OpenUpgrade Committers is requested to review the proposed merge of lp:~therp-nl/openupgrade-server/5.0-use_orm into lp:openupgrade-server/5.0.
=== modified file 'bin/addons/__init__.py'
--- bin/addons/__init__.py 2011-11-30 22:57:11 +0000
+++ bin/addons/__init__.py 2012-05-09 12:36:18 +0000
@@ -41,6 +41,15 @@
logger = netsvc.Logger()
+### OpenUpgrade
+def table_exists(cr, table):
+ """ Check whether a certain table or view exists """
+ cr.execute(
+ 'SELECT count(relname) FROM pg_class WHERE relname = %s',
+ (table,))
+ return cr.fetchone()[0] == 1
+### End of OpenUpgrade
+
_ad = os.path.abspath(opj(tools.config['root_path'], 'addons')) # default addons path (base)
ad = os.path.abspath(tools.config['addons_path']) # alternate addons path
@@ -571,12 +580,133 @@
modobj = None
import string
+
+ local_registry = {}
def get_repr(properties, type='val'):
+ """
+ OpenUpgrade: Return the string representation of the model or field
+ for logging purposes
+ """
if type == 'key':
props = ['model', 'field']
elif type == 'val':
- props = ['type', 'isfunction', 'relation', 'required', 'selection_keys', 'req_default', 'inherits']
- return ','.join(["\"" + string.replace(properties[prop], '\"', '\'') + "\"" for prop in props])
+ props = [
+ 'type', 'isfunction', 'relation', 'required', 'selection_keys',
+ 'req_default', 'inherits'
+ ]
+ return ','.join([
+ '\"' + string.replace(
+ string.replace(
+ properties[prop], '\"', '\''), '\n','')
+ + '\"' for prop in props
+ ])
+
+ def log_model(model):
+ """
+ OpenUpgrade: Store the characteristics of the BaseModel and its fields
+ in the local registry, so that we can compare changes with the
+ main registry
+ """
+
+ # persistent models only
+ if isinstance(model, osv.osv.osv_memory):
+ return
+
+ model_registry = local_registry.setdefault(
+ model._name, {})
+ if model._inherits:
+ model_registry['_inherits'] = {'_inherits': unicode(model._inherits)}
+ for k, v in model._columns.items():
+ properties = {
+ 'type': v._type,
+ 'isfunction': (
+ isinstance(v, osv.fields.function) and 'function' or ''),
+ 'relation': (
+ v._type in ('many2many', 'many2one','one2many')
+ and v._obj or ''
+ ),
+ 'required': v.required and 'required' or '',
+ 'selection_keys': '',
+ 'req_default': '',
+ 'inherits': '',
+ }
+ if v._type == 'selection':
+ if hasattr(v.selection, "__iter__"):
+ properties['selection_keys'] = unicode(
+ sorted([x[0] for x in v.selection]))
+ else:
+ properties['selection_keys'] = 'function'
+ if v.required and k in model._defaults:
+ if isinstance(model._defaults[k], types.FunctionType):
+ # todo: in OpenERP 5 (and in 6 as well),
+ # literals are wrapped in a lambda function.
+ properties['req_default'] = 'function'
+ else:
+ properties['req_default'] = unicode(model._defaults[k])
+ for key, value in properties.items():
+ if value:
+ model_registry.setdefault(k, {})[key] = value
+
+ def get_record_id(cr, module, model, field, mode):
+ """
+ OpenUpgrade: get or create the id from the record table matching
+ the key parameter values
+ """
+ cr.execute(
+ "SELECT id FROM openupgrade_record "
+ "WHERE module = %s AND model = %s AND "
+ "field = %s AND mode = %s AND type = %s",
+ (module, model, field, mode, 'field')
+ )
+ record = cr.fetchone()
+ if record:
+ return record[0]
+ cr.execute(
+ "INSERT INTO openupgrade_record "
+ "(module, model, field, mode, type) "
+ "VALUES (%s, %s, %s, %s, %s)",
+ (module, model, field, mode, 'field')
+ )
+ cr.execute(
+ "SELECT id FROM openupgrade_record "
+ "WHERE module = %s AND model = %s AND "
+ "field = %s AND mode = %s AND type = %s",
+ (module, model, field, mode, 'field')
+ )
+ return cr.fetchone()[0]
+
+ def compare_registries(cr, module):
+ """
+ OpenUpgrade: Compare the local registry with the global registry,
+ log any differences and merge the local registry with
+ the global one.
+ """
+ if not table_exists(cr, 'openupgrade_record'):
+ return
+ for model, fields in local_registry.items():
+ registry.setdefault(model, {})
+ for field, attributes in fields.items():
+ old_field = registry[model].setdefault(field, {})
+ mode = old_field and 'modify' or 'create'
+ record_id = False
+ for key, value in attributes.items():
+ if key not in old_field or old_field[key] != value:
+ if not record_id:
+ record_id = get_record_id(
+ cr, module, model, field, mode)
+ cr.execute(
+ "SELECT id FROM openupgrade_attribute "
+ "WHERE name = %s AND value = %s AND "
+ "record_id = %s",
+ (key, value, record_id)
+ )
+ if not cr.fetchone():
+ cr.execute(
+ "INSERT INTO openupgrade_attribute "
+ "(name, value, record_id) VALUES (%s, %s, %s)",
+ (key, value, record_id)
+ )
+ old_field[key] = value
for package in graph:
logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading objects' % package.name)
@@ -584,53 +714,10 @@
register_class(package.name)
modules = pool.instanciate(package.name, cr)
- logger.notifyChannel('OpenUpgrade_FIELD', netsvc.LOG_INFO, 'module %s' % (package.name))
local_registry = {}
- for orm_object in osv.orm.orm:
- if orm_object._inherits:
- properties = {
- 'model': orm_object._name,
- 'field': '_inherits',
- 'type': '',
- 'isfunction': '',
- 'relation': '',
- 'required': '',
- 'selection_keys': '',
- 'req_default': '',
- 'inherits': unicode(orm_object._inherits),
- }
- local_registry[get_repr(properties, 'key')] = get_repr(properties)
- for k,v in orm_object._columns.items():
- properties = {
- 'model': orm_object._name,
- 'field': k,
- 'type': v._type,
- 'isfunction': isinstance(v, osv.fields.function) and 'function' or '',
- 'relation': v._type in ('many2many', 'many2one','one2many') and v._obj or '',
- 'required': v.required and 'required' or '',
- 'selection_keys': '',
- 'req_default': '',
- 'inherits': '',
- }
- if v._type == 'selection':
- if hasattr(v.selection, "__iter__"):
- properties['selection_keys'] = unicode(sorted([x[0] for x in v.selection]))
- else:
- properties['selection_keys'] = 'function'
- if v.required and k in orm_object._defaults:
- if isinstance(orm_object._defaults[k], types.FunctionType):
- properties['req_default'] = 'function'
- else:
- properties['req_default'] = unicode(orm_object._defaults[k])
- local_registry[get_repr(properties, 'key')] = get_repr(properties)
- for key in sorted(local_registry.keys()):
- if key in registry:
- if registry[key] != local_registry[key]:
- logger.notifyChannel('OpenUpgrade_FIELD', netsvc.LOG_INFO, '"%s","modify",%s,%s' % (package.name, key, local_registry[key]))
- else:
- logger.notifyChannel('OpenUpgrade_FIELD', netsvc.LOG_INFO, '"%s","create",%s,%s' % (package.name, key, local_registry[key]))
- registry[key] = local_registry[key]
-
+ for model in modules:
+ log_model(model)
+ compare_registries(cr, package.name)
if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'):
init_module_objects(cr, package.name, modules)
cr.commit()
@@ -712,10 +799,6 @@
delattr(package, kind)
statusi += 1
- cr.execute('select model, name from ir_model_data where module=%s order by model, name', (package.name,))
- for res in cr.fetchall():
- xmlid_repr = ','.join(["\"" + string.replace(property, '\"', '\'') + "\"" for property in (res[0], res[1], package.name)])
- logger.notifyChannel('OpenUpgrade_XMLID', netsvc.LOG_INFO, xmlid_repr)
cr.execute('select model from ir_model where state=%s', ('manual',))
for model in cr.dictfetchall():
=== modified file 'bin/addons/base/ir/ir_model.py'
--- bin/addons/base/ir/ir_model.py 2010-05-18 09:30:44 +0000
+++ bin/addons/base/ir/ir_model.py 2012-05-09 12:36:18 +0000
@@ -31,6 +31,8 @@
from tools.translate import _
import pooler
+from openupgrade import openupgrade_log
+
def _get_fields_type(self, cr, uid, context=None):
cr.execute('select distinct ttype,ttype from ir_model_fields')
return cr.fetchall()
@@ -459,6 +461,10 @@
return id
def _update(self,cr, uid, model, module, values, xml_id=False, store=True, noupdate=False, mode='init', res_id=False, context=None):
+ #OpenUpgrade: log entry (used in csv import)
+ if xml_id:
+ openupgrade_log.log_xml_id(cr, module, xml_id)
+
warning = True
model_obj = self.pool.get(model)
if not context:
=== added directory 'bin/addons/openupgrade_records'
=== added file 'bin/addons/openupgrade_records/__init__.py'
--- bin/addons/openupgrade_records/__init__.py 1970-01-01 00:00:00 +0000
+++ bin/addons/openupgrade_records/__init__.py 2012-05-09 12:36:18 +0000
@@ -0,0 +1,2 @@
+import model
+import lib
=== added file 'bin/addons/openupgrade_records/__openerp__.py'
--- bin/addons/openupgrade_records/__openerp__.py 1970-01-01 00:00:00 +0000
+++ bin/addons/openupgrade_records/__openerp__.py 2012-05-09 12:36:18 +0000
@@ -0,0 +1,63 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# This module Copyright (C) 2012 OpenUpgrade community
+# https://launchpad.net/~openupgrade-committers
+#
+# Contributors:
+# Therp BV <http://therp.nl>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+
+{
+ 'name': 'OpenUpgrade Records',
+ 'version': '0.2',
+ 'category': 'Normal',
+ 'description': """Allow OpenUpgrade records to be
+stored in the database and compare with other servers.
+
+This module depends on OpenERP client lib:
+
+ easy_install openerp-client-lib
+
+""",
+ 'author': 'OpenUpgrade Community',
+ 'maintainer': 'OpenUpgrade Community',
+ 'contributors': ['Therp BV'],
+ 'website': 'https://launchpad.net/~openupgrade-committers',
+ 'depends': [],
+ 'init_xml': [],
+ 'update_xml': [
+ 'view/openupgrade_record.xml',
+ 'view/comparison_config.xml',
+ 'view/analysis_wizard.xml',
+ 'view/generate_records_wizard.xml',
+ 'view/install_all_wizard.xml',
+ 'security/ir.model.access.csv',
+ ],
+ 'demo_xml': [
+ ],
+ 'test': [
+ ],
+ 'installable': True,
+ 'auto_install': False,
+ 'external_dependencies': {
+ 'python' : ['openerplib'],
+ },
+}
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
=== added file 'bin/addons/openupgrade_records/__terp__.py'
--- bin/addons/openupgrade_records/__terp__.py 1970-01-01 00:00:00 +0000
+++ bin/addons/openupgrade_records/__terp__.py 2012-05-09 12:36:18 +0000
@@ -0,0 +1,63 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# This module Copyright (C) 2012 OpenUpgrade community
+# https://launchpad.net/~openupgrade-committers
+#
+# Contributors:
+# Therp BV <http://therp.nl>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+
+{
+ 'name': 'OpenUpgrade Records',
+ 'version': '0.2',
+ 'category': 'Normal',
+ 'description': """Allow OpenUpgrade records to be
+stored in the database and compare with other servers.
+
+This module depends on OpenERP client lib:
+
+ easy_install openerp-client-lib
+
+""",
+ 'author': 'OpenUpgrade Community',
+ 'maintainer': 'OpenUpgrade Community',
+ 'contributors': ['Therp BV'],
+ 'website': 'https://launchpad.net/~openupgrade-committers',
+ 'depends': [],
+ 'init_xml': [],
+ 'update_xml': [
+ 'view/openupgrade_record.xml',
+ 'view/comparison_config.xml',
+ 'view/analysis_wizard.xml',
+ 'view/generate_records_wizard.xml',
+ 'view/install_all_wizard.xml',
+ 'security/ir.model.access.csv',
+ ],
+ 'demo_xml': [
+ ],
+ 'test': [
+ ],
+ 'installable': True,
+ 'auto_install': False,
+ 'external_dependencies': {
+ 'python' : ['openerplib'],
+ },
+}
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
=== added directory 'bin/addons/openupgrade_records/model'
=== added file 'bin/addons/openupgrade_records/model/__init__.py'
--- bin/addons/openupgrade_records/model/__init__.py 1970-01-01 00:00:00 +0000
+++ bin/addons/openupgrade_records/model/__init__.py 2012-05-09 12:36:18 +0000
@@ -0,0 +1,6 @@
+import openupgrade_record
+import comparison_config
+import analysis_wizard
+import generate_records_wizard
+import install_all_wizard
+
=== added file 'bin/addons/openupgrade_records/model/analysis_wizard.py'
--- bin/addons/openupgrade_records/model/analysis_wizard.py 1970-01-01 00:00:00 +0000
+++ bin/addons/openupgrade_records/model/analysis_wizard.py 2012-05-09 12:36:18 +0000
@@ -0,0 +1,178 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# This module Copyright (C) 2012 OpenUpgrade community
+# https://launchpad.net/~openupgrade-committers
+#
+# Contributors:
+# Therp BV <http://therp.nl>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import os
+from osv import osv, fields
+
+try:
+ from openerp.addons.openupgrade_records.lib import compare
+ from openerp.openupgrade_records.lib import apriori
+ from openerp.addons import get_module_path
+except ImportError:
+ from openupgrade_records.lib import compare
+ from openupgrade_records.lib import apriori
+ from addons import get_module_path
+
+class openupgrade_analysis_wizard(osv.osv_memory):
+ _name = 'openupgrade.analysis.wizard'
+ _description = 'OpenUpgrade Analysis Wizard'
+ _columns = {
+ 'server_config': fields.many2one(
+ 'openupgrade.comparison.config',
+ 'Configuration', required=True),
+ 'state': fields.selection(
+ [('init', 'Init'), ('ready', 'Ready')], 'State',
+ readonly=True),
+ 'log': fields.text('Log'),
+ 'write': fields.boolean(
+ 'Write files',
+ help='Write analysis files to the module directories'
+ ),
+ }
+ _defaults = {
+ 'state': lambda *a: 'init',
+ 'write': lambda *a: True,
+ }
+
+ def get_communication(self, cr, uid, ids, context=None):
+ """
+ Retrieve both sets of database representations,
+ perform the comparison and register the resulting
+ change set
+ """
+ def write_file(
+ module, version, contents, filename='openupgrade_analysis.txt'):
+ module_path = get_module_path(module)
+ if not module_path:
+ return "ERROR: could not find module path:\n"
+ full_path = os.path.join(
+ module_path, 'migrations', version)
+ if not os.path.exists(full_path):
+ try:
+ os.makedirs(full_path)
+ except os.error:
+ return "ERROR: could not create migrations directory:\n"
+ logfile = os.path.join(full_path, filename)
+ try:
+ f = open(logfile, 'w')
+ except Exception:
+ return "ERROR: could not open file %s for writing:\n" % logfile
+ f.write(contents)
+ f.close()
+ return None
+
+ wizard = self.browse(cr, uid, ids[0], context=context)
+ # Retrieve connection and access methods
+ conf_obj = self.pool.get('openupgrade.comparison.config')
+ connection = conf_obj.get_connection(
+ cr, uid, [wizard.server_config.id], context=context)
+ remote_record_obj = connection.get_model('openupgrade.record')
+ local_record_obj = self.pool.get('openupgrade.record')
+
+ # Retrieve field representations and compare
+ remote_records = remote_record_obj.field_dump(context)
+ local_records = local_record_obj.field_dump(cr, uid, context)
+ res = compare.compare_sets(remote_records, local_records)
+
+ # Retrieve xml id representations and compare
+ fields = ['module', 'model', 'name']
+ local_xml_record_ids = local_record_obj.search(
+ cr, uid, [('type', '=', 'xmlid')])
+ remote_xml_record_ids = remote_record_obj.search(
+ [('type', '=', 'xmlid')])
+ local_xml_records = [
+ dict([(field, x[field]) for field in fields])
+ for x in local_record_obj.read(
+ cr, uid, local_xml_record_ids, fields)
+ ]
+ remote_xml_records = [
+ dict([(field, x[field]) for field in fields])
+ for x in remote_record_obj.read(
+ remote_xml_record_ids, fields)
+ ]
+ res_xml = compare.compare_xml_sets(
+ remote_xml_records, local_xml_records)
+
+ # reorder and output the result
+ keys = list(set(res.keys() + res_xml.keys()))
+ keys.remove('general')
+ keys = ['general'] + keys
+ module_obj = self.pool.get('ir.module.module')
+ module_ids = module_obj.search(
+ cr, uid, [('state', '=', 'installed')])
+ modules = dict([(x['name'], x) for x in module_obj.read(cr, uid, module_ids)])
+ general = ''
+ for key in keys:
+ contents = "---%s---\n" % key
+ if key in res:
+ contents += '\n'.join([unicode(line) for line in sorted(res[key])])
+ if res[key]:
+ contents += '\n'
+ if key in res_xml:
+ contents += '\n'.join([unicode(line) for line in sorted(res_xml[key])])
+ if res_xml[key]:
+ contents += '\n'
+ if key == 'general':
+ general += contents
+ continue
+ if key not in modules:
+ general += (
+ "ERROR: module not in list of installed modules:\n"
+ + contents)
+ continue
+ if wizard.write:
+ error = write_file(
+ key, modules[key]['installed_version'], contents)
+ if error:
+ general += error
+ general += contents
+ else:
+ general += contents
+
+ # Store the general log in as many places as possible ;-)
+ if wizard.write and 'base' in modules:
+ write_file(
+ 'base', modules['base']['installed_version'], general,
+ 'openupgrade_general_log.txt')
+ self.pool.get('openupgrade.comparison.config').write(
+ cr, uid, wizard.server_config.id,
+ {'last_log': general})
+ self.write(cr, uid, ids, {'state': 'ready', 'log': general})
+
+ result = {
+ 'name': self._description,
+ 'view_type': 'form',
+ 'view_mode': 'form',
+ 'res_model': 'openupgrade.analysis.wizard',
+ 'domain': [],
+ 'context': context,
+ 'type': 'ir.actions.act_window',
+ #'target': 'new',
+ 'res_id': ids[0],
+ }
+ return result
+
+openupgrade_analysis_wizard()
+
=== added file 'bin/addons/openupgrade_records/model/comparison_config.py'
--- bin/addons/openupgrade_records/model/comparison_config.py 1970-01-01 00:00:00 +0000
+++ bin/addons/openupgrade_records/model/comparison_config.py 2012-05-09 12:36:18 +0000
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# This module Copyright (C) 2012 OpenUpgrade community
+# https://launchpad.net/~openupgrade-committers
+#
+# Contributors:
+# Therp BV <http://therp.nl>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from osv import osv, fields
+import openerplib
+from tools.translate import _
+
+class openupgrade_comparison_config(osv.osv):
+ _name = 'openupgrade.comparison.config'
+ _columns = {
+ 'name': fields.char('Name', size=64),
+ 'server': fields.char('Server', size=64, required=True),
+ 'port': fields.integer('Port', required=True),
+ 'protocol': fields.selection(
+ [('http://', 'XML-RPC')],
+ # ('https://', 'XML-RPC Secure')], not supported by libopenerp
+ 'Protocol', required=True),
+ 'database': fields.char('Database', size=64, required=True),
+ 'username': fields.char('Username', size=24, required=True),
+ 'password': fields.char('Password', size=24, required=True, password=True),
+ 'last_log': fields.text('Last log'),
+ }
+ _defaults = {
+ 'port': lambda *a: 8069,
+ 'protocol': lambda *a: 'http://',
+ }
+
+ def get_connection(self, cr, uid, ids, context=None):
+ if not ids:
+ raise osv.except_osv(
+ _("Cannot connect"), _("Invalid id passed."))
+ conf = self.read(cr, uid, ids[0], context=None)
+ return openerplib.get_connection(
+ hostname=conf['server'],
+ database=conf['database'],
+ login=conf['username'],
+ password=conf['password'],
+ port=conf['port'],
+ )
+
+ def test_connection(self, cr, uid, ids, context=None):
+ try:
+ connection = self.get_connection(cr, uid, [ids[0]], context)
+ user_model = connection.get_model("res.users")
+ ids = user_model.search([("login", "=", "admin")])
+ user_info = user_model.read(ids[0], ["name"])
+ except Exception, e:
+ raise osv.except_osv(
+ _("Connection failed."), unicode(e))
+ raise osv.except_osv(
+ _("Connection succesful."),
+ _("%s is connected.") % user_info["name"]
+ )
+
+ def analyze(self, cr, uid, ids, context=None):
+ """
+ Run the analysis wizard
+ """
+ wizard_obj = self.pool.get('openupgrade.analysis.wizard')
+ wizard_id = wizard_obj.create(
+ cr, uid, {'server_config': ids[0]}, context)
+ result = {
+ 'name': wizard_obj._description,
+ 'view_type': 'form',
+ 'view_mode': 'form',
+ 'res_model': 'openupgrade.analysis.wizard',
+ 'domain': [],
+ 'context': context,
+ 'type': 'ir.actions.act_window',
+ 'target': 'new',
+ 'res_id': wizard_id,
+ 'nodestroy': True,
+ }
+ return result
+
+openupgrade_comparison_config()
=== added file 'bin/addons/openupgrade_records/model/generate_records_wizard.py'
--- bin/addons/openupgrade_records/model/generate_records_wizard.py 1970-01-01 00:00:00 +0000
+++ bin/addons/openupgrade_records/model/generate_records_wizard.py 2012-05-09 12:36:18 +0000
@@ -0,0 +1,90 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# This module Copyright (C) 2012 OpenUpgrade community
+# https://launchpad.net/~openupgrade-committers
+#
+# Contributors:
+# Therp BV <http://therp.nl>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import os
+from osv import osv, fields
+import pooler
+try:
+ from openerp.openupgrade import openupgrade_tools
+except ImportError:
+ from openupgrade import openupgrade_tools
+
+class generate_records_wizard(osv.osv_memory):
+ _name = 'openupgrade.generate.records.wizard'
+ _description = 'OpenUpgrade Generate Records Wizard'
+ _columns = {
+ 'state': fields.selection([('init', 'init'), ('ready', 'ready')], 'State'),
+ }
+ _defaults = {
+ 'state': lambda *a: 'init',
+ }
+
+ def generate(self, cr, uid, ids, context=None):
+ """
+ Main wizard step. Make sure that all modules are up-to-date,
+ then reinitialize all installed modules.
+ Equivalent of running the server with '-d <database> --init all'
+
+ The goal of this is to fill the records table.
+
+ TODO: update module list and versions, then update all modules?
+ """
+ # Truncate the records table
+ if (openupgrade_tools.table_exists(cr, 'openupgrade_attribute') and
+ openupgrade_tools.table_exists(cr, 'openupgrade_record')):
+ cr.execute(
+ 'TRUNCATE openupgrade_attribute, openupgrade_record;'
+ )
+
+ # Need to get all modules in state 'installed'
+ module_obj = self.pool.get('ir.module.module')
+ module_ids = module_obj.search(
+ cr, uid, [('state', 'in', ['to install', 'to upgrade'])])
+ if module_ids:
+ cr.commit()
+ _db, pool = pooler.restart_pool(cr.dbname, update_module=True)
+ # Did we succeed above?
+ module_ids = module_obj.search(
+ cr, uid, [('state', 'in', ['to install', 'to upgrade'])])
+ if module_ids:
+ modules = module_obj.read(
+ cr, uid, module_ids, ['name'], context=context)
+ raise except_osv(
+ "Cannot reliably generate records",
+ ("Cannot seem to install or upgrade modules " +
+ ', '.join([x['name'] for x in modules])))
+ # Now reinitialize all installed modules
+ module_ids = module_obj.search(
+ cr, uid, [('state', '=', 'installed')])
+ module_obj.write(
+ cr, uid, module_ids, {'state': 'to install'})
+ cr.commit()
+ _db, pool = pooler.restart_pool(cr.dbname, update_module=True)
+ self.write(cr, uid, ids, {'state': 'ready'})
+ # and we are done
+ return True
+
+generate_records_wizard()
+
=== added file 'bin/addons/openupgrade_records/model/install_all_wizard.py'
--- bin/addons/openupgrade_records/model/install_all_wizard.py 1970-01-01 00:00:00 +0000
+++ bin/addons/openupgrade_records/model/install_all_wizard.py 2012-05-09 12:36:18 +0000
@@ -0,0 +1,113 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# This module Copyright (C) 2012 OpenUpgrade community
+# https://launchpad.net/~openupgrade-committers
+#
+# Contributors:
+# Therp BV <http://therp.nl>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import time
+import os
+from osv import osv, fields
+import pooler
+
+class install_all_wizard(osv.osv_memory):
+ _name = 'openupgrade.install.all.wizard'
+ _description = 'OpenUpgrade Install All Wizard'
+ _columns = {
+ 'state': fields.selection([('init', 'init'), ('ready', 'ready')], 'State', readonly=True),
+ 'to_install': fields.integer('Number of modules to install', readonly=True),
+ }
+ _defaults = {
+ 'state': lambda *a: 'init',
+ }
+
+
+ def default_get(self, cr, uid, fields, context=None):
+ """
+ Update module list and retrieve the number
+ of installable modules
+ """
+ res = super(install_all_wizard, self).default_get(
+ cr, uid, fields, context=None)
+ module_obj = self.pool.get('ir.module.module')
+ update, add = module_obj.update_list(cr, uid,)
+ print "%s modules added" % add
+ module_ids = module_obj.search(
+ cr, uid, [('state', 'not in', ['installed', 'uninstallable', 'unknown'])])
+ res.update(
+ {'to_install': module_ids and len(module_ids) or False}
+ )
+ return res
+
+ def quirk_fiscalyear(self, cr, uid, ids, context=None):
+ """
+ Install account module first and create a fiscal year,
+ in order to prevent "No fiscal year defined" exception
+ during an upgrade or reinstallation of the account module.
+
+ Refer to account_fiscalyear.find(), which is called as
+ a default function by the orm upon module upgrade.
+ """
+ module_obj = self.pool.get('ir.module.module')
+ pool = self.pool
+ # Retrieve status of the account module
+ account_module_id = module_obj.search(
+ cr, uid, [('name', '=', 'account')], context=context)[0]
+ state = module_obj.read(
+ cr, uid, account_module_id, ['state'], context=context)['state']
+ if state != 'installed':
+ # Cancel installation of other modules
+ module_ids = module_obj.search(
+ cr, uid, [('state', '=', 'to install')])
+ module_obj.write(cr, uid, module_ids, {'state': 'uninstalled'})
+ # Mark the module and its dependencies
+ module_obj.button_install(cr, uid, [account_module_id])
+ # Install account module
+ cr.commit()
+ _db, pool = pooler.restart_pool(cr.dbname, update_module=True)
+ # get or create today's fiscal year
+ fy_obj = pool.get('account.fiscalyear')
+ if not fy_obj.find(cr, uid, False, exception=False, context=context):
+ fy_obj.create(cr, uid, {
+ 'name': time.strftime('%Y'),
+ 'code': time.strftime('%Y'),
+ 'date_start': "%s-01-01" % time.strftime('%Y'),
+ 'date_stop': "%s-12-31" % time.strftime('%Y'),
+ })
+
+ def install_all(self, cr, uid, ids, context=None):
+ """
+ Main wizard step. Set all installable modules to install
+ and actually install them.
+ """
+ module_obj = self.pool.get('ir.module.module')
+ module_ids = module_obj.search(
+ cr, uid, [('state', 'not in', ['installed', 'uninstallable', 'unknown'])])
+ if module_ids:
+ module_obj.write(
+ cr, uid, module_ids, {'state': 'to install'})
+ cr.commit()
+ _db, pool = pooler.restart_pool(cr.dbname, update_module=True)
+ self.write(cr, uid, ids, {'state': 'ready'})
+ return True
+
+install_all_wizard()
+
=== added file 'bin/addons/openupgrade_records/model/openupgrade_record.py'
--- bin/addons/openupgrade_records/model/openupgrade_record.py 1970-01-01 00:00:00 +0000
+++ bin/addons/openupgrade_records/model/openupgrade_record.py 2012-05-09 12:36:18 +0000
@@ -0,0 +1,111 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# This module Copyright (C) 2012 OpenUpgrade community
+# https://launchpad.net/~openupgrade-committers
+#
+# Contributors:
+# Therp BV <http://therp.nl>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from osv import osv, fields
+
+# Cannot use forward references in 6.0
+class openupgrade_record(osv.osv):
+ _name = 'openupgrade.record'
+openupgrade_record()
+
+class openupgrade_attribute(osv.osv):
+ _name = 'openupgrade.attribute'
+ _rec_name = 'attribute_id'
+ _columns = {
+ 'name': fields.char(
+ 'Name', size=24,
+ readonly=True,
+ ),
+ 'value': fields.char(
+ 'Value',
+ size=4096,
+ readonly=True,
+ ),
+ 'record_id': fields.many2one(
+ 'openupgrade.record', ondelete='CASCADE',
+ readonly=True,
+ ),
+ }
+openupgrade_attribute()
+
+class openupgrade_record(osv.osv):
+ _inherit = 'openupgrade.record'
+
+ _columns = {
+ 'name': fields.char('Name', size=256, readonly=True),
+ 'module': fields.char('Module', size=128, readonly=True),
+ 'model': fields.char('Model', size=128, readonly=True),
+ 'field': fields.char('Field', size=128, readonly=True),
+ 'mode': fields.selection(
+ [('create', 'Create'), ('modify', 'Modify')],
+ 'Mode',
+ help='Set to Create if a field is newly created '
+ 'in this module. If this module modifies an attribute of an '
+ 'exting field, set to Modify.',
+ readonly=True,
+ ),
+ 'type': fields.selection(
+ [('field', 'Field'), ('xmlid', 'XML ID')],
+ 'Type',
+ readonly=True,
+ ),
+ 'attribute_ids': fields.one2many(
+ 'openupgrade.attribute', 'record_id', 'Attributes',
+ readonly=True,
+ ),
+ }
+ def field_dump(self, cr, uid, context=None):
+ keys = [
+ 'module',
+ 'mode',
+ 'model',
+ 'field',
+ 'type',
+ 'isfunction',
+ 'relation',
+ 'required',
+ 'selection_keys',
+ 'req_default',
+ 'inherits',
+ ]
+
+ template = dict([(x, False) for x in keys])
+ ids = self.search(cr, uid, [('type', '=', 'field')], context=context)
+ records = self.browse(cr, uid, ids, context=context)
+ data = []
+ for record in records:
+ repr = template.copy()
+ repr.update({
+ 'module': record.module,
+ 'model': record.model,
+ 'field': record.field,
+ 'mode': record.mode,
+ })
+ repr.update(
+ dict([(x.name, x.value) for x in record.attribute_ids]))
+ data.append(repr)
+ return data
+
+openupgrade_record()
=== added directory 'bin/addons/openupgrade_records/security'
=== added file 'bin/addons/openupgrade_records/security/ir.model.access.csv'
--- bin/addons/openupgrade_records/security/ir.model.access.csv 1970-01-01 00:00:00 +0000
+++ bin/addons/openupgrade_records/security/ir.model.access.csv 2012-05-09 12:36:18 +0000
@@ -0,0 +1,3 @@
+"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
+"access_openupgrade_record","openupgrade.record all","model_openupgrade_record",,1,0,0,0
+"access_openupgrade_attribute","openupgrade.attribute all","model_openupgrade_attribute",,1,0,0,0
=== added directory 'bin/addons/openupgrade_records/view'
=== added file 'bin/addons/openupgrade_records/view/analysis_wizard.xml'
--- bin/addons/openupgrade_records/view/analysis_wizard.xml 1970-01-01 00:00:00 +0000
+++ bin/addons/openupgrade_records/view/analysis_wizard.xml 2012-05-09 12:36:18 +0000
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+ <record id="view_openupgrade_analysis_wizard_form" model="ir.ui.view">
+ <field name="name">view.openupgrade.analysis_wizard.form</field>
+ <field name="model">openupgrade.analysis.wizard</field>
+ <field name="type">form</field>
+ <field name="arch" type="xml">
+ <form string="OpenUpgrade Analysis Wizard">
+ <field name="server_config" readonly="1"/>
+ <field name="state"/>
+ <field name="log" colspan="4"
+ attrs="{'invisible': [('state', '!=', 'ready')]}"/>
+ <field name="write"
+ attrs="{'readonly': [('state', '!=', 'init')]}"/>
+ <button icon="gtk-close"
+ special="cancel"
+ string="Close"
+ />
+ <button icon="gtk-ok"
+ string="Create"
+ name="get_communication"
+ type="object"
+ states="init"
+ />
+ </form>
+ </field>
+ </record>
+ </data>
+</openerp>
=== added file 'bin/addons/openupgrade_records/view/comparison_config.xml'
--- bin/addons/openupgrade_records/view/comparison_config.xml 1970-01-01 00:00:00 +0000
+++ bin/addons/openupgrade_records/view/comparison_config.xml 2012-05-09 12:36:18 +0000
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+ <record id="view_openupgrade_comparison_config_tree" model="ir.ui.view">
+ <field name="name">view.openupgrade.comparison_config.tree</field>
+ <field name="model">openupgrade.comparison.config</field>
+ <field name="type">tree</field>
+ <field name="arch" type="xml">
+ <tree string="OpenUpgrade Comparison Config">
+ <field name="name" select="1"/>
+ <field name="protocol"/>
+ <field name="server" select="1"/>
+ <field name="port" select="1"/>
+ <field name="database" select="1"/>
+ </tree>
+ </field>
+ </record>
+ <record id="view_openupgrade_comparison_config_form" model="ir.ui.view">
+ <field name="name">view.openupgrade.comparison_config.form</field>
+ <field name="model">openupgrade.comparison.config</field>
+ <field name="type">form</field>
+ <field name="arch" type="xml">
+ <form string="OpenUpgrade Comparison Config">
+ <field name="name"/>
+ <field name="protocol"/>
+ <field name="server"/>
+ <field name="port"/>
+ <field name="database"/>
+ <field name="username"/>
+ <field name="password" password="1"/>
+ <button
+ name="test_connection"
+ string="Test Connection"
+ type="object" icon="gtk-network"
+ colspan="2"
+ />
+ <newline/>
+ <button
+ name="analyze"
+ string="Perform Analysis"
+ type="object" icon="gtk-execute"
+ colspan="2"
+ />
+ <separator string="Last log" colspan="4"/>
+ <field name="last_log" nolabel="1" colspan="4"/>
+ </form>
+ </field>
+ </record>
+ <record id="action_openupgrade_comparison_config_tree" model="ir.actions.act_window">
+ <field name="name">OpenUpgrade Comparison Configs</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">openupgrade.comparison.config</field>
+ <field name="view_type">form</field>
+ </record>
+ <menuitem
+ action="action_openupgrade_comparison_config_tree"
+ id="menu_openupgrade_comparison_config"
+ name="Comparison Configurations"
+ parent="menu_openupgrade"
+ />
+ </data>
+</openerp>
=== added file 'bin/addons/openupgrade_records/view/generate_records_wizard.xml'
--- bin/addons/openupgrade_records/view/generate_records_wizard.xml 1970-01-01 00:00:00 +0000
+++ bin/addons/openupgrade_records/view/generate_records_wizard.xml 2012-05-09 12:36:18 +0000
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+ <record id="view_openupgrade_generate_records_wizard_form" model="ir.ui.view">
+ <field name="name">view.openupgrade.generate_records_wizard.form</field>
+ <field name="model">openupgrade.generate.records.wizard</field>
+ <field name="type">form</field>
+ <field name="arch" type="xml">
+ <form string="OpenUpgrade Generate Records Wizard">
+ <group states="init" colspan="4">
+ <label string="This will reinitialize all the modules installed on this database. Do not continue if you use this database in production."
+ />
+ <button icon="gtk-close"
+ special="cancel"
+ string="Cancel"
+ />
+ <button icon="gtk-ok"
+ string="Continue"
+ name="generate"
+ type="object"
+ />
+ </group>
+ <group states="ready" colspan="4">
+ <label string="Modules initialized and records created"
+ />
+ <field name="state" invisible="1"/>
+ <button icon="gtk-close"
+ special="cancel"
+ string="Close"
+ />
+ </group>
+ </form>
+ </field>
+ </record>
+
+ <record id="action_generate_records" model="ir.actions.act_window">
+ <field name="name">Generate Records</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">openupgrade.generate.records.wizard</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">form,tree</field>
+ <field name="target">new</field>
+ </record>
+
+ <menuitem name="Generate Records"
+ id="menu_openupgrade_generate_records"
+ parent="menu_openupgrade"
+ action="action_generate_records"
+ sequence="15"/>
+
+ </data>
+</openerp>
=== added file 'bin/addons/openupgrade_records/view/install_all_wizard.xml'
--- bin/addons/openupgrade_records/view/install_all_wizard.xml 1970-01-01 00:00:00 +0000
+++ bin/addons/openupgrade_records/view/install_all_wizard.xml 2012-05-09 12:36:18 +0000
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+ <record id="view_openupgrade_install_all_wizard_form" model="ir.ui.view">
+ <field name="name">view.openupgrade.install_all_wizard.form</field>
+ <field name="model">openupgrade.install.all.wizard</field>
+ <field name="type">form</field>
+ <field name="arch" type="xml">
+ <form string="OpenUpgrade Install All Modules Wizard">
+ <group states="init" colspan="4">
+ <label string="This will install all modules on the database. Do not continue if you use this database in production." colspan="4"
+ />
+ <field name="to_install"/>
+ <newline/>
+ <button icon="gtk-close"
+ special="cancel"
+ string="Cancel"
+ />
+ <button icon="gtk-ok"
+ string="Continue"
+ name="install_all"
+ type="object"
+ />
+ </group>
+ <group states="ready" colspan="4">
+ <label string="Modules installed"
+ />
+ <field name="state" invisible="1"/>
+ <button icon="gtk-close"
+ special="cancel"
+ string="Close"
+ />
+ </group>
+ </form>
+ </field>
+ </record>
+
+ <record id="action_install_all" model="ir.actions.act_window">
+ <field name="name">Install All Modules</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">openupgrade.install.all.wizard</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">form,tree</field>
+ <field name="target">new</field>
+ </record>
+
+ <menuitem name="Install All Modules"
+ id="menu_openupgrade_install_all"
+ parent="menu_openupgrade"
+ action="action_install_all"
+ sequence="14"/>
+
+ </data>
+</openerp>
=== added file 'bin/addons/openupgrade_records/view/openupgrade_record.xml'
--- bin/addons/openupgrade_records/view/openupgrade_record.xml 1970-01-01 00:00:00 +0000
+++ bin/addons/openupgrade_records/view/openupgrade_record.xml 2012-05-09 12:36:18 +0000
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+ <!-- Top level menu under 'Database structure' -->
+ <menuitem
+ id="menu_openupgrade"
+ name="OpenUpgrade Development"
+ parent="base.menu_administration"
+ sequence="99"
+ />
+ <record id="view_openupgrade_record_tree" model="ir.ui.view">
+ <field name="name">view.openupgrade.record.tree</field>
+ <field name="model">openupgrade.record</field>
+ <field name="type">tree</field>
+ <field name="arch" type="xml">
+ <tree string="OpenUpgrade Records">
+ <field name="module" select="1"/>
+ <field name="model" select="1"/>
+ <field name="field" select="1"/>
+ <field name="name" select="1"/>
+ <field name="type" select="1"/>
+ <field name="mode" select="1"/>
+ </tree>
+ </field>
+ </record>
+ <record id="view_openupgrade_record_form" model="ir.ui.view">
+ <field name="name">view.openupgrade.record.form</field>
+ <field name="model">openupgrade.record</field>
+ <field name="type">form</field>
+ <field name="arch" type="xml">
+ <form string="OpenUpgrade Record">
+ <field name="module" select="1"/>
+ <field name="model" select="1"/>
+ <field name="field" select="1"/>
+ <field name="name" select="1"/>
+ <field name="type" select="1"/>
+ <field name="mode" select="1"/>
+ <separator string="Attributes" colspan="4"/>
+ <field name="attribute_ids" mode="tree,form" nolabel="1" colspan="4">
+ <tree string="Attributes">
+ <field name="name"/>
+ <field name="value"/>
+ </tree>
+ <form string="Attribute">
+ <field name="name"/>
+ <field name="value"/>
+ </form>
+ </field>
+ </form>
+ </field>
+ </record>
+ <record id="action_openupgrade_record_tree" model="ir.actions.act_window">
+ <field name="name">OpenUpgrade Records</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">openupgrade.record</field>
+ <field name="view_type">form</field>
+ </record>
+ <menuitem
+ action="action_openupgrade_record_tree"
+ id="menu_openupgrade_records"
+ name="Records"
+ parent="menu_openupgrade"
+ />
+ </data>
+</openerp>
=== added directory 'bin/openupgrade'
=== added file 'bin/openupgrade/__init__.py'
=== added directory 'bin/openupgrade/doc'
=== added file 'bin/openupgrade/doc/readme.txt'
--- bin/openupgrade/doc/readme.txt 1970-01-01 00:00:00 +0000
+++ bin/openupgrade/doc/readme.txt 2012-05-09 12:36:18 +0000
@@ -0,0 +1,2 @@
+The documentation of this project is currently maintained in the
+6.1 branch. You can also consult it at http://readthedocs.org/docs/openupgrade-server
=== added file 'bin/openupgrade/openupgrade.py'
--- bin/openupgrade/openupgrade.py 1970-01-01 00:00:00 +0000
+++ bin/openupgrade/openupgrade.py 2012-05-09 12:36:18 +0000
@@ -0,0 +1,259 @@
+# -*- coding: utf-8 -*-
+import os
+from osv import osv
+import pooler
+import logging
+import tools
+import openupgrade_tools
+
+logger = logging.getLogger('OpenUpgrade')
+
+__all__ = [
+ 'load_data',
+ 'rename_columns',
+ 'rename_tables',
+ 'drop_columns',
+ 'table_exists',
+ 'column_exists',
+ 'delete_model_workflow',
+ 'set_defaults',
+ 'update_module_names',
+ 'add_ir_model_fields',
+]
+
+def load_data(cr, module_name, filename, idref=None, mode='init'):
+ """
+ Load an xml or csv data file from your post script. The usual case for this is the
+ occurrence of newly added essential or useful data in the module that is
+ marked with "noupdate='1'" and without "forcecreate='1'" so that it will
+ not be loaded by the usual upgrade mechanism. Leaving the 'mode' argument to
+ its default 'init' will load the data from your migration script.
+
+ Theoretically, you could simply load a stock file from the module, but be
+ careful not to reinitialize any data that could have been customized.
+ Preferably, select only the newly added items. Copy these to a file
+ in your migrations directory and load that file.
+ Leave it to the user to actually delete existing resources that are
+ marked with 'noupdate' (other named items will be deleted
+ automatically).
+
+
+ :param module_name: the name of the module
+ :param filename: the path to the filename, relative to the module \
+ directory.
+ :param idref: optional hash with ?id mapping cache?
+ :param mode: one of 'init', 'update', 'demo'. Always use 'init' for adding new items \
+ from files that are marked with 'noupdate'. Defaults to 'init'.
+
+ """
+
+ if idref is None:
+ idref = {}
+ logger.info('%s: loading %s' % (module_name, filename))
+ _, ext = os.path.splitext(filename)
+ pathname = os.path.join(module_name, filename)
+ fp = tools.file_open(pathname)
+ try:
+ if ext == '.csv':
+ noupdate = True
+ tools.convert_csv_import(cr, module_name, pathname, fp.read(), idref, mode, noupdate)
+ else:
+ tools.convert_xml_import(cr, module_name, fp, idref, mode=mode)
+ finally:
+ fp.close()
+
+# for backwards compatibility
+load_xml = load_data
+table_exists = openupgrade_tools.table_exists
+
+def rename_columns(cr, column_spec):
+ """
+ Rename table columns. Typically called in the pre script.
+
+ :param column_spec: a hash with table keys, with lists of tuples as values. \
+ Tuples consist of (old_name, new_name).
+
+ """
+ for table in column_spec.keys():
+ for (old, new) in column_spec[table]:
+ logger.info("table %s, column %s: renaming to %s",
+ table, old, new)
+ cr.execute('ALTER TABLE "%s" RENAME "%s" TO "%s"' % (table, old, new,))
+
+def rename_tables(cr, table_spec):
+ """
+ Rename tables. Typically called in the pre script.
+ :param column_spec: a list of tuples (old table name, new table name).
+
+ """
+ for (old, new) in table_spec:
+ logger.info("table %s: renaming to %s",
+ old, new)
+ cr.execute('ALTER TABLE "%s" RENAME TO "%s"' % (old, new,))
+
+def rename_models(cr, model_spec):
+ """
+ Rename models. Typically called in the pre script.
+ :param column_spec: a list of tuples (old table name, new table name).
+
+ Use case: if a model changes name, but still implements equivalent
+ functionality you will want to update references in for instance
+ relation fields.
+
+ """
+ for (old, new) in model_spec:
+ logger.info("model %s: renaming to %s",
+ old, new)
+ cr.execute('UPDATE ir_model_fields SET relation = %s '
+ 'WHERE relation = %s', (new, old,))
+
+def drop_columns(cr, column_spec):
+ """
+ Drop columns but perform an additional check if a column exists.
+ This covers the case of function fields that may or may not be stored.
+ Consider that this may not be obvious: an additional module can govern
+ a function fields' store properties.
+
+ :param column_spec: a list of (table, column) tuples
+ """
+ for (table, column) in column_spec:
+ logger.info("table %s: drop column %s",
+ table, column)
+ if column_exists(cr, table, column):
+ cr.execute('ALTER TABLE "%s" DROP COLUMN "%s"' %
+ (table, column))
+ else:
+ logger.warn("table %s: column %s did not exist",
+ table, column)
+
+def delete_model_workflow(cr, model):
+ """
+ Forcefully remove active workflows for obsolete models,
+ to prevent foreign key issues when the orm deletes the model.
+ """
+ logged_query(
+ cr,
+ "DELETE FROM wkf_workitem WHERE act_id in "
+ "( SELECT wkf_activity.id "
+ " FROM wkf_activity, wkf "
+ " WHERE wkf_id = wkf.id AND "
+ " wkf.osv = %s"
+ ")", (model,))
+ logged_query(
+ cr,
+ "DELETE FROM wkf WHERE osv = %s", (model,))
+
+def set_defaults(cr, pool, default_spec, force=False):
+ """
+ Set default value. Useful for fields that are newly required. Uses orm, so
+ call from the post script.
+
+ :param default_spec: a hash with model names as keys. Values are lists of \
+ tuples (field, value). None as a value has a special meaning: it assigns \
+ the default value. If this value is provided by a function, the function is \
+ called as the user that created the resource.
+ :param force: overwrite existing values. To be used for assigning a non- \
+ default value (presumably in the case of a new column). The ORM assigns \
+ the default value as declared in the model in an earlier stage of the \
+ process. Beware of issues with resources loaded from new data that \
+ actually do require the model's default, in combination with the post \
+ script possible being run multiple times.
+ """
+
+ def write_value(ids, field, value):
+ logger.info("model %s, field %s: setting default value of %d resources to %s",
+ model, field, len(ids), unicode(value))
+ obj.write(cr, 1, ids, {field: value})
+
+ for model in default_spec.keys():
+ obj = pool.get(model)
+ if not obj:
+ raise osv.except_osv("Migration: error setting default, no such model: %s" % model, "")
+
+ for field, value in default_spec[model]:
+ domain = not force and [(field, '=', False)] or []
+ ids = obj.search(cr, 1, domain)
+ if not ids:
+ continue
+ if value is None:
+ # Set the value by calling the _defaults of the object.
+ # Typically used for company_id on various models, and in that
+ # case the result depends on the user associated with the object.
+ # We retrieve create_uid for this purpose and need to call the _defaults
+ # function per resource. Otherwise, write all resources at once.
+ if field in obj._defaults:
+ if not callable(obj._defaults[field]):
+ write_value(ids, field, obj._defaults[field])
+ else:
+ # existence users is covered by foreign keys, so this is not needed
+ # 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" %
+ # (obj._table, obj._table, obj._table, obj._table, tuple(ids),))
+ cr.execute("SELECT id, COALESCE(create_uid, 1) FROM %s " % obj._table + "WHERE id in %s", (tuple(ids),))
+ fetchdict = dict(cr.fetchall())
+ for id in ids:
+ write_value([id], field, obj._defaults[field](obj, cr, fetchdict.get(id, 1), None))
+ if id not in fetchdict:
+ logger.info("model %s, field %s, id %d: no create_uid defined or user does not exist anymore",
+ model, field, id)
+ else:
+ error = ("OpenUpgrade: error setting default, field %s with "
+ "None default value not in %s' _defaults" % (
+ field, model))
+ logger.error(error)
+ # this exeption seems to get lost in a higher up try block
+ osv.except_osv("OpenUpgrade", error)
+ else:
+ write_value(ids, field, value)
+
+def logged_query(cr, query, args=None):
+ if args is None:
+ args = []
+ res = cr.execute(query, args)
+ logger.debug('Running %s', query)
+ if not res:
+ query = query % args
+ logger.warn('No rows affected for query "%s"', query)
+ return res
+
+def column_exists(cr, table, column):
+ """ Check whether a certain column exists """
+ cr.execute(
+ 'SELECT count(attname) FROM pg_attribute '
+ 'WHERE attrelid = '
+ '( SELECT oid FROM pg_class WHERE relname = %s ) '
+ 'AND attname = %s',
+ (table, column));
+ return cr.fetchone()[0] == 1
+
+def update_module_names(cr, namespec):
+ """
+ Deal with changed module names of certified modules
+ in order to prevent 'certificate not unique' error,
+ as well as updating the module reference in the
+ XML id.
+
+ :param namespec: tuple of (old name, new name)
+ """
+ for (old_name, new_name) in namespec:
+ query = ("UPDATE ir_module_module SET name = %s "
+ "WHERE name = %s")
+ logged_query(cr, query, (new_name, old_name))
+ query = ("UPDATE ir_model_data SET module = %s "
+ "WHERE module = %s ")
+ logged_query(cr, query, (new_name, old_name))
+
+def add_ir_model_fields(cr, columnspec):
+ """
+ Typically, new columns on ir_model_fields need to be added in a very
+ early stage in the upgrade process of the base module, in raw sql
+ as they need to be in place before any model gets initialized.
+ Do not use for fields with additional SQL constraints, such as a
+ reference to another table or the cascade constraint, but craft your
+ own statement taking them into account.
+
+ :param columnspec: tuple of (column name, column type)
+ """
+ for column in columnspec:
+ query = 'ALTER TABLE ir_model_fields ADD COLUMN %s %s' % (
+ column)
+ logged_query(cr, query, [])
=== added file 'bin/openupgrade/openupgrade_log.py'
--- bin/openupgrade/openupgrade_log.py 1970-01-01 00:00:00 +0000
+++ bin/openupgrade/openupgrade_log.py 2012-05-09 12:36:18 +0000
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+from openupgrade_tools import table_exists
+
+def log_xml_id(cr, module, xml_id):
+ """
+ Log xml_ids at load time in the records table.
+ Called from openerp/tools/convert.py:xml_import._test_xml_id()
+
+ # Catcha's
+ - The module needs to be loaded with 'init', or the calling method
+ won't be called. This can be brought about by installing the
+ module or updating the 'state' field of the module to 'to install'
+ or call the server with '--init <module>' and the database argument.
+
+ - Do you get the right results immediately when installing the module?
+ No, sorry. This method retrieves the model from the ir_model_table, but when
+ the xml id is encountered for the first time, this method is called
+ before the item is present in this table. Therefore, you will not
+ get any meaningful results until the *second* time that you 'init'
+ the module.
+
+ - The good news is that the openupgrade_records module that comes
+ with this distribution allows you to deal with all of this with
+ one click on the menu item Settings -> Customizations ->
+ Database Structure -> OpenUpgrade -> Generate Records
+
+ - You cannot reinitialize the modules in your production database
+ and expect to keep working on it happily ever after. Do not perform
+ this routine on your production database.
+
+ :param module: The module that contains the xml_id
+ :param xml_id: the xml_id, with or without 'module.' prefix
+ """
+ if not table_exists(cr, 'openupgrade_record'):
+ return
+ if not '.' in xml_id:
+ xml_id = '%s.%s' % (module, xml_id)
+ cr.execute(
+ "SELECT model FROM ir_model_data "
+ "WHERE module = %s AND name = %s",
+ xml_id.split('.'))
+ record = cr.fetchone()
+ if not record:
+ #print "Cannot find xml_id %s" % xml_id
+ return
+ else:
+ cr.execute(
+ "SELECT id FROM openupgrade_record "
+ "WHERE module=%s AND model=%s AND name=%s AND type=%s",
+ (module, record[0], xml_id, 'xmlid'))
+ if not cr.fetchone():
+ cr.execute(
+ "INSERT INTO openupgrade_record "
+ "(module, model, name, type) values(%s, %s, %s, %s)",
+ (module, record[0], xml_id, 'xmlid'))
+
=== added file 'bin/openupgrade/openupgrade_tools.py'
--- bin/openupgrade/openupgrade_tools.py 1970-01-01 00:00:00 +0000
+++ bin/openupgrade/openupgrade_tools.py 2012-05-09 12:36:18 +0000
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+def table_exists(cr, table):
+ """ Check whether a certain table or view exists """
+ cr.execute(
+ 'SELECT count(relname) FROM pg_class WHERE relname = %s',
+ (table,))
+ return cr.fetchone()[0] == 1
+
=== modified file 'bin/tools/convert.py'
--- bin/tools/convert.py 2010-12-17 12:06:16 +0000
+++ bin/tools/convert.py 2012-05-09 12:36:18 +0000
@@ -42,6 +42,8 @@
from tools.safe_eval import safe_eval as eval
+from openupgrade import openupgrade_log
+
class ConvertError(Exception):
def __init__(self, doc, orig_excpt):
self.d = doc
@@ -244,6 +246,7 @@
if len(id) > 64:
self.logger.notifyChannel('init', netsvc.LOG_ERROR, 'id: %s is to long (max: 64)'% (id,))
+ openupgrade_log.log_xml_id(self.cr, self.module, xml_id)
def _tag_delete(self, cr, rec, data_node=None):
d_model = rec.get("model",'')
=== modified file 'bin/tools/sql.py'
--- bin/tools/sql.py 2009-01-04 22:13:29 +0000
+++ bin/tools/sql.py 2012-05-09 12:36:18 +0000
@@ -23,7 +23,8 @@
def drop_view_if_exists(cr, viewname):
cr.execute("select count(1) from pg_class where relkind=%s and relname=%s", ('v', viewname,))
if cr.fetchone()[0]:
- cr.execute("DROP view %s" % (viewname,))
+ # OpenUpgrade: add CASCADE
+ cr.execute("DROP view %s CASCADE" % (viewname,))
cr.commit()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
Follow ups