[REF] rename files, apply OCA convention, remove obsolete 6.1 syntax, make the module installable, add readme folders

pull/2826/head
Sylvain LE GAL 2020-11-06 22:44:54 +01:00 committed by Stefan Rijnhart
parent ed44832568
commit f29ff2daa7
24 changed files with 441 additions and 498 deletions

View File

@ -1,59 +1,8 @@
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png
:target: https://www.gnu.org/licenses/agpl
:alt: License: AGPL-3
================
Upgrade Analysis
================
===============================
OpenUpgrade Database Comparison
===============================
This module provides the tool to generate the database analysis files that indicate how the Odoo data model and module data have changed between two versions of Odoo. Database analysis files for the core modules are included in the OpenUpgrade distribution so as a migration script developer you will not usually need to use this tool yourself. If you do need to run your analysis of a custom set of modules, please refer to the documentation here: https://doc.therp.nl/openupgrade/analysis.html
Installation
============
This module has a python dependency on openerp-client-lib. You need to make this module available in your Python environment, for instance by installing it with the pip tool.
Known issues / Roadmap
======================
* scripts/compare_noupdate_xml_records.py should be integrated in the analysis process (#590)
* Log removed modules in the module that owned them (#468)
* Detect renamed many2many tables (#213)
Bug Tracker
===========
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/openupgrade/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smash it by providing detailed and welcomed feedback.
Images
------
* Odoo Community Association: `Icon <https://odoo-community.org/logo.png>`_.
Contributors
------------
* Stefan Rijnhart <stefan@opener.amsterdam>
* Holger Brunn <hbrunn@therp.nl>
* Pedro M. Baeza <pedro.baeza@gmail.com>
* Ferdinand Gassauer <gass@cc-l-12.chircar.at>
* Florent Xicluna <florent.xicluna@gmail.com>
* Miquel Raïch <miquel.raich@eficent.com>
Maintainer
----------
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
To contribute to this module, please visit https://odoo-community.org.
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

View File

@ -1,2 +1,5 @@
from . import models
from . import wizards
from . import blacklist
from . import apriori
from . import compare

View File

@ -2,18 +2,21 @@
# Copyright 2016 Opener B.V. <https://opener.am>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "OpenUpgrade Records",
"name": "Upgrade Analysis",
"summary": "performs a difference analysis between modules"
" installed on two different Odoo instances",
"version": "14.0.1.0.0",
"category": "Migration",
"author": "Therp BV, Opener B.V., Odoo Community Association (OCA)",
"author": "Therp BV, Opener B.V., GRAP, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/server-tools",
"data": [
"views/openupgrade_record.xml",
"views/comparison_config.xml",
"views/analysis_wizard.xml",
"views/generate_records_wizard.xml",
"views/install_all_wizard.xml",
"security/ir.model.access.csv",
"views/menu.xml",
"views/view_upgrade_comparison_config.xml",
"views/view_upgrade_record.xml",
"wizards/view_upgrade_analysis_wizard.xml",
"wizards/view_upgrade_generate_record_wizard.xml",
"wizards/view_upgrade_install_wizard.xml",
],
"installable": True,
"external_dependencies": {

View File

@ -2,97 +2,12 @@
to help the matching process
"""
renamed_modules = {
# Odoo
'crm_reveal': 'crm_iap_lead',
'document': 'attachment_indexation',
'payment_ogone': 'payment_ingenico',
# OCA/hr
# TODO: Transform possible data
'hr_skill': 'hr_skills'
}
renamed_modules = {}
merged_modules = {
# Odoo
'account_cancel': 'account',
'account_voucher': 'account',
'crm_phone_validation': 'crm',
'decimal_precision': 'base',
'delivery_hs_code': 'delivery',
'hw_scale': 'hw_drivers',
'hw_scanner': 'hw_drivers',
'hw_screen': 'hw_drivers',
'l10n_fr_certification': 'account',
'l10n_fr_sale_closing': 'l10n_fr',
'mrp_bom_cost': 'mrp_account',
'mrp_byproduct': 'mrp',
'payment_stripe_sca': 'payment_stripe',
'stock_zebra': 'stock',
'survey_crm': 'survey',
'test_pylint': 'test_lint',
'web_settings_dashboard': 'base_setup',
'website_crm_phone_validation': 'website_crm',
'website_sale_link_tracker': 'website_sale',
'website_survey': 'survey',
# OCA/account-financial-tools
'account_move_chatter': 'account',
# OCA/account-reconcile
'account_set_reconcilable': 'account',
# OCA/l10n-spain
'l10n_es_aeat_sii': 'l10n_es_aeat_sii_oca',
# OCA/server-backend
'base_suspend_security': 'base',
# OCA/social
'mass_mailing_unique': 'mass_mailing',
# OCA/timesheet
'sale_timesheet_existing_project': 'sale_timesheet',
# OCA/web
'web_favicon': 'base',
'web_widget_color': 'web',
'web_widget_many2many_tags_multi_selection': 'web',
# OCA/website
'website_canonical_url': 'website',
'website_logo': 'website',
}
merged_modules = {}
# only used here for openupgrade_records analysis:
renamed_models = {
# Odoo
'account.register.payments': 'account.payment.register',
'crm.reveal.industry': 'crm.iap.lead.industry',
'crm.reveal.role': 'crm.iap.lead.role',
'crm.reveal.seniority': 'crm.iap.lead.seniority',
'mail.blacklist.mixin': 'mail.thread.blacklist',
'mail.mail.statistics': 'mailing.trace',
'mail.statistics.report': 'mailing.trace.report',
'mail.mass_mailing': 'mailing.mailing',
'mail.mass_mailing.contact': 'mailing.contact',
'mail.mass_mailing.list': 'mailing.list',
'mail.mass_mailing.list_contact_rel': 'mailing.contact.subscription',
'mail.mass_mailing.stage': 'utm.stage',
'mail.mass_mailing.tag': 'utm.tag',
'mail.mass_mailing.test': 'mailing.mailing.test',
'mass.mailing.list.merge': 'mailing.list.merge',
'mass.mailing.schedule.date': 'mailing.mailing.schedule.date',
'mrp.subproduct': 'mrp.bom.byproduct',
'sms.send_sms': 'sms.composer',
'stock.fixed.putaway.strat': 'stock.putaway.rule',
'survey.mail.compose.message': 'survey.invite',
'website.redirect': 'website.rewrite',
# OCA/...
}
# only used here for upgrade_analysis
renamed_models = {}
# only used here for openupgrade_records analysis:
merged_models = {
# Odoo
'account.invoice': 'account.move',
'account.invoice.line': 'account.move.line',
'account.invoice.tax': 'account.move.line',
'account.voucher': 'account.move',
'account.voucher.line': 'account.move.line',
'lunch.order.line': 'lunch.order',
'mail.mass_mailing.campaign': 'utm.campaign',
'slide.category': 'slide.slide',
'survey.page': 'survey.question',
# OCA/...
}
# only used here for upgrade_analysis
merged_models = {}

View File

@ -1,7 +1,7 @@
# coding: utf-8
# Copyright 2011-2015 Therp BV <https://therp.nl>
# Copyright 2015-2016 Opener B.V. <https://opener.am>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
# flake8: noqa: C901
#####################################################################
# library providing a function to analyse two progressive database
@ -11,12 +11,13 @@
import collections
import copy
from odoo.addons.openupgrade_records.lib import apriori
from . import apriori
def module_map(module):
return apriori.renamed_modules.get(
module, apriori.merged_modules.get(module, module))
module, apriori.merged_modules.get(module, module)
)
def model_rename_map(model):
@ -24,8 +25,7 @@ def model_rename_map(model):
def model_map(model):
return apriori.renamed_models.get(
model, apriori.merged_models.get(model, model))
return apriori.renamed_models.get(model, apriori.merged_models.get(model, model))
def inv_model_map(model):
@ -34,12 +34,12 @@ def inv_model_map(model):
IGNORE_FIELDS = [
'create_date',
'create_uid',
'id',
'write_date',
'write_uid',
]
"create_date",
"create_uid",
"id",
"write_date",
"write_uid",
]
def compare_records(dict_old, dict_new, fields):
@ -51,17 +51,19 @@ def compare_records(dict_old, dict_new, fields):
Return True of False.
"""
for field in fields:
if field == 'module':
if module_map(dict_old['module']) != dict_new['module']:
if field == "module":
if module_map(dict_old["module"]) != dict_new["module"]:
return False
elif field == 'model':
if model_rename_map(dict_old['model']) != dict_new['model']:
elif field == "model":
if model_rename_map(dict_old["model"]) != dict_new["model"]:
return False
elif field == 'other_prefix':
if dict_old['module'] != dict_old['prefix'] or \
dict_new['module'] != dict_new['prefix']:
elif field == "other_prefix":
if (
dict_old["module"] != dict_old["prefix"]
or dict_new["module"] != dict_new["prefix"]
):
return False
if dict_old['model'] == 'ir.ui.view':
if dict_old["model"] == "ir.ui.view":
# basically, to avoid the assets_backend case
return False
elif dict_old[field] != dict_new[field]:
@ -80,71 +82,69 @@ def search(item, item_list, fields):
continue
return other
# search for renamed fields
if 'field' in fields:
if "field" in fields:
for other in item_list:
if not item['field'] or item['field'] is not None or \
item['isproperty']:
if not item["field"] or item["field"] is not None or item["isproperty"]:
continue
if compare_records(
dict(item, field=other['field']), other, fields):
if compare_records(dict(item, field=other["field"]), other, fields):
return other
return None
def fieldprint(old, new, field, text, reprs):
fieldrepr = "%s (%s)" % (old['field'], old['type'])
fullrepr = '%-12s / %-24s / %-30s' % (
old['module'], old['model'], fieldrepr)
fieldrepr = "{} ({})".format(old["field"], old["type"])
fullrepr = "{:<12} / {:<24} / {:<30}".format(old["module"], old["model"], fieldrepr)
if not text:
text = "%s is now '%s' ('%s')" % (field, new[field], old[field])
if field == 'relation':
text += ' [nothing to do]'
reprs[module_map(old['module'])].append("%s: %s" % (fullrepr, text))
if field == 'module':
text = "{} is now '{}' ('{}')".format(field, new[field], old[field])
if field == "relation":
text += " [nothing to do]"
reprs[module_map(old["module"])].append("{}: {}".format(fullrepr, text))
if field == "module":
text = "previously in module %s" % old[field]
fullrepr = '%-12s / %-24s / %-30s' % (
new['module'], old['model'], fieldrepr)
reprs[module_map(new['module'])].append("%s: %s" % (fullrepr, text))
fullrepr = "{:<12} / {:<24} / {:<30}".format(
new["module"], old["model"], fieldrepr
)
reprs[module_map(new["module"])].append("{}: {}".format(fullrepr, text))
def report_generic(new, old, attrs, reprs):
for attr in attrs:
if attr == 'required':
if old[attr] != new['required'] and new['required']:
if attr == "required":
if old[attr] != new["required"] and new["required"]:
text = "now required"
if new['req_default']:
text += ', req_default: %s' % new['req_default']
fieldprint(old, new, '', text, reprs)
elif attr == 'stored':
if new["req_default"]:
text += ", req_default: %s" % new["req_default"]
fieldprint(old, new, "", text, reprs)
elif attr == "stored":
if old[attr] != new[attr]:
if new['stored']:
if new["stored"]:
text = "is now stored"
else:
text = "not stored anymore"
fieldprint(old, new, '', text, reprs)
elif attr == 'isfunction':
fieldprint(old, new, "", text, reprs)
elif attr == "isfunction":
if old[attr] != new[attr]:
if new['isfunction']:
if new["isfunction"]:
text = "now a function"
else:
text = "not a function anymore"
fieldprint(old, new, '', text, reprs)
elif attr == 'isproperty':
fieldprint(old, new, "", text, reprs)
elif attr == "isproperty":
if old[attr] != new[attr]:
if new[attr]:
text = "now a property"
else:
text = "not a property anymore"
fieldprint(old, new, '', text, reprs)
elif attr == 'isrelated':
fieldprint(old, new, "", text, reprs)
elif attr == "isrelated":
if old[attr] != new[attr]:
if new[attr]:
text = "now related"
else:
text = "not related anymore"
fieldprint(old, new, '', text, reprs)
fieldprint(old, new, "", text, reprs)
elif old[attr] != new[attr]:
fieldprint(old, new, attr, '', reprs)
fieldprint(old, new, attr, "", reprs)
def compare_sets(old_records, new_records):
@ -160,7 +160,7 @@ def compare_sets(old_records, new_records):
def clean_records(records):
result = []
for record in records:
if record['field'] not in IGNORE_FIELDS:
if record["field"] not in IGNORE_FIELDS:
result.append(record)
return result
@ -168,8 +168,8 @@ def compare_sets(old_records, new_records):
new_records = clean_records(new_records)
origlen = len(old_records)
new_models = set([column['model'] for column in new_records])
old_models = set([column['model'] for column in old_records])
new_models = {column["model"] for column in new_records}
old_models = {column["model"] for column in old_records}
matched_direct = 0
matched_other_module = 0
@ -184,7 +184,7 @@ def compare_sets(old_records, new_records):
non_obsolete_old_records = []
for column in copy.copy(old_records):
if column['model'] in obsolete_models:
if column["model"] in obsolete_models:
in_obsolete_models += 1
else:
non_obsolete_old_records.append(column)
@ -205,152 +205,193 @@ def compare_sets(old_records, new_records):
return count
matched_direct = match(
['module', 'mode', 'model', 'field'],
['relation', 'type', 'selection_keys', 'inherits', 'stored',
'isfunction', 'isrelated', 'required', 'table'])
["module", "mode", "model", "field"],
[
"relation",
"type",
"selection_keys",
"inherits",
"stored",
"isfunction",
"isrelated",
"required",
"table",
],
)
# other module, same type and operation
matched_other_module = match(
['mode', 'model', 'field', 'type'],
['module', 'relation', 'selection_keys', 'inherits', 'stored',
'isfunction', 'isrelated', 'required', 'table'])
["mode", "model", "field", "type"],
[
"module",
"relation",
"selection_keys",
"inherits",
"stored",
"isfunction",
"isrelated",
"required",
"table",
],
)
# other module, same operation, other type
matched_other_type = match(
['mode', 'model', 'field'],
['relation', 'type', 'selection_keys', 'inherits', 'stored',
'isfunction', 'isrelated', 'required', 'table'])
["mode", "model", "field"],
[
"relation",
"type",
"selection_keys",
"inherits",
"stored",
"isfunction",
"isrelated",
"required",
"table",
],
)
printkeys = [
'relation', 'required', 'selection_keys',
'req_default', 'inherits', 'mode', 'attachment',
]
"relation",
"required",
"selection_keys",
"req_default",
"inherits",
"mode",
"attachment",
]
for column in old_records:
# we do not care about removed non stored function fields
if not column['stored'] and (
column['isfunction'] or column['isrelated']):
if not column["stored"] and (column["isfunction"] or column["isrelated"]):
continue
if column['mode'] == 'create':
column['mode'] = ''
if column["mode"] == "create":
column["mode"] = ""
extra_message = ", ".join(
[k + ': ' + str(column[k]) if k != str(column[k]) else k
for k in printkeys if column[k]]
[
k + ": " + str(column[k]) if k != str(column[k]) else k
for k in printkeys
if column[k]
]
)
if extra_message:
extra_message = " " + extra_message
fieldprint(
column, '', '', "DEL" + extra_message, reprs)
fieldprint(column, "", "", "DEL" + extra_message, reprs)
printkeys.extend([
'hasdefault',
])
printkeys.extend(
[
"hasdefault",
]
)
for column in new_records:
# we do not care about newly added non stored function fields
if not column['stored'] and (
column['isfunction'] or column['isrelated']):
if not column["stored"] and (column["isfunction"] or column["isrelated"]):
continue
if column['mode'] == 'create':
column['mode'] = ''
if column["mode"] == "create":
column["mode"] = ""
printkeys_plus = printkeys.copy()
if column['isfunction'] or column['isrelated']:
printkeys_plus.extend(['isfunction', 'isrelated', 'stored'])
if column["isfunction"] or column["isrelated"]:
printkeys_plus.extend(["isfunction", "isrelated", "stored"])
extra_message = ", ".join(
[k + ': ' + str(column[k]) if k != str(column[k]) else k
for k in printkeys_plus if column[k]]
[
k + ": " + str(column[k]) if k != str(column[k]) else k
for k in printkeys_plus
if column[k]
]
)
if extra_message:
extra_message = " " + extra_message
fieldprint(
column, '', '', "NEW" + extra_message, reprs)
fieldprint(column, "", "", "NEW" + extra_message, reprs)
for line in [
"# %d fields matched," % (origlen - len(old_records)),
"# Direct match: %d" % matched_direct,
"# Found in other module: %d" % matched_other_module,
"# Found with different type: %d" % matched_other_type,
"# In obsolete models: %d" % in_obsolete_models,
"# Not matched: %d" % len(old_records),
"# New columns: %d" % len(new_records)
]:
reprs['general'].append(line)
"# %d fields matched," % (origlen - len(old_records)),
"# Direct match: %d" % matched_direct,
"# Found in other module: %d" % matched_other_module,
"# Found with different type: %d" % matched_other_type,
"# In obsolete models: %d" % in_obsolete_models,
"# Not matched: %d" % len(old_records),
"# New columns: %d" % len(new_records),
]:
reprs["general"].append(line)
return reprs
def compare_xml_sets(old_records, new_records):
reprs = collections.defaultdict(list)
def match(match_fields, match_type='direct'):
def match(match_fields, match_type="direct"):
matched_records = []
for column in copy.copy(old_records):
found = search(column, new_records, match_fields)
if found:
old_records.remove(column)
new_records.remove(found)
if match_type != 'direct':
column['old'] = True
found['new'] = True
column[match_type] = found['module']
found[match_type] = column['module']
found['domain'] = column['domain'] != found['domain'] and \
column['domain'] != '[]' and found['domain'] is False
column['domain'] = False
column['noupdate_switched'] = False
found['noupdate_switched'] = \
column['noupdate'] != found['noupdate']
if match_type != 'direct':
if match_type != "direct":
column["old"] = True
found["new"] = True
column[match_type] = found["module"]
found[match_type] = column["module"]
found["domain"] = (
column["domain"] != found["domain"]
and column["domain"] != "[]"
and found["domain"] is False
)
column["domain"] = False
column["noupdate_switched"] = False
found["noupdate_switched"] = column["noupdate"] != found["noupdate"]
if match_type != "direct":
matched_records.append(column)
matched_records.append(found)
elif (match_type == 'direct' and found['domain']) or \
found['noupdate_switched']:
elif (match_type == "direct" and found["domain"]) or found[
"noupdate_switched"
]:
matched_records.append(found)
return matched_records
# direct match
modified_records = match(['module', 'model', 'name'])
modified_records = match(["module", "model", "name"])
# other module, same full xmlid
moved_records = match(['model', 'name'], 'moved')
moved_records = match(["model", "name"], "moved")
# other module, same suffix, other prefix
renamed_records = match(['model', 'suffix', 'other_prefix'], 'renamed')
renamed_records = match(["model", "suffix", "other_prefix"], "renamed")
for record in old_records:
record['old'] = True
record['domain'] = False
record['noupdate_switched'] = False
record["old"] = True
record["domain"] = False
record["noupdate_switched"] = False
for record in new_records:
record['new'] = True
record['domain'] = False
record['noupdate_switched'] = False
record["new"] = True
record["domain"] = False
record["noupdate_switched"] = False
sorted_records = sorted(
old_records + new_records + moved_records + renamed_records +
modified_records,
key=lambda k: (k['model'], 'old' in k, k['name'])
old_records + new_records + moved_records + renamed_records + modified_records,
key=lambda k: (k["model"], "old" in k, k["name"]),
)
for entry in sorted_records:
content = ''
if 'old' in entry:
content = 'DEL %(model)s: %(name)s' % entry
if 'moved' in entry:
content += ' [potentially moved to %(moved)s module]' % entry
elif 'renamed' in entry:
content += ' [renamed to %(renamed)s module]' % entry
elif 'new' in entry:
content = 'NEW %(model)s: %(name)s' % entry
if 'moved' in entry:
content += ' [potentially moved from %(moved)s module]' % entry
elif 'renamed' in entry:
content += ' [renamed from %(renamed)s module]' % entry
if 'old' not in entry and 'new' not in entry:
content = '%(model)s: %(name)s' % entry
if entry['domain']:
content += ' (deleted domain)'
if entry['noupdate']:
content += ' (noupdate)'
if entry['noupdate_switched']:
content += ' (noupdate switched)'
reprs[module_map(entry['module'])].append(content)
content = ""
if "old" in entry:
content = "DEL %(model)s: %(name)s" % entry
if "moved" in entry:
content += " [potentially moved to %(moved)s module]" % entry
elif "renamed" in entry:
content += " [renamed to %(renamed)s module]" % entry
elif "new" in entry:
content = "NEW %(model)s: %(name)s" % entry
if "moved" in entry:
content += " [potentially moved from %(moved)s module]" % entry
elif "renamed" in entry:
content += " [renamed from %(renamed)s module]" % entry
if "old" not in entry and "new" not in entry:
content = "%(model)s: %(name)s" % entry
if entry["domain"]:
content += " (deleted domain)"
if entry["noupdate"]:
content += " (noupdate)"
if entry["noupdate_switched"]:
content += " (noupdate switched)"
reprs[module_map(entry["module"])].append(content)
return reprs
@ -360,79 +401,84 @@ def compare_model_sets(old_records, new_records):
"""
reprs = collections.defaultdict(list)
new_models = {column['model']: column['module'] for column in new_records}
old_models = {column['model']: column['module'] for column in old_records}
new_models = {column["model"]: column["module"] for column in new_records}
old_models = {column["model"]: column["module"] for column in old_records}
obsolete_models = []
for column in copy.copy(old_records):
model = column['model']
model = column["model"]
if model in old_models:
if model not in new_models:
if model_map(model) not in new_models:
obsolete_models.append(model)
text = 'obsolete model %s' % model
if column['model_type']:
text += " [%s]" % column['model_type']
reprs[module_map(column['module'])].append(text)
reprs['general'].append('obsolete model %s [module %s]' % (
model, module_map(column['module'])))
text = "obsolete model %s" % model
if column["model_type"]:
text += " [%s]" % column["model_type"]
reprs[module_map(column["module"])].append(text)
reprs["general"].append(
"obsolete model %s [module %s]"
% (model, module_map(column["module"]))
)
else:
moved_module = ''
if module_map(column['module']) != new_models[model_map(
model)]:
moved_module = ' in module %s' % new_models[model_map(
model)]
text = 'obsolete model %s (renamed to %s%s)' % (
model, model_map(model), moved_module)
if column['model_type']:
text += " [%s]" % column['model_type']
reprs[module_map(column['module'])].append(text)
reprs['general'].append(
'obsolete model %s (renamed to %s) [module %s]' % (
model, model_map(model),
module_map(column['module'])))
moved_module = ""
if module_map(column["module"]) != new_models[model_map(model)]:
moved_module = " in module %s" % new_models[model_map(model)]
text = "obsolete model {} (renamed to {}{})".format(
model,
model_map(model),
moved_module,
)
if column["model_type"]:
text += " [%s]" % column["model_type"]
reprs[module_map(column["module"])].append(text)
reprs["general"].append(
"obsolete model %s (renamed to %s) [module %s]"
% (model, model_map(model), module_map(column["module"]))
)
else:
if module_map(column['module']) != new_models[model]:
text = 'model %s (moved to %s)' % (
model, new_models[model])
if column['model_type']:
text += " [%s]" % column['model_type']
reprs[module_map(column['module'])].append(text)
text = 'model %s (moved from %s)' % (
model, old_models[model])
if column['model_type']:
text += " [%s]" % column['model_type']
if module_map(column["module"]) != new_models[model]:
text = "model {} (moved to {})".format(model, new_models[model])
if column["model_type"]:
text += " [%s]" % column["model_type"]
reprs[module_map(column["module"])].append(text)
text = "model {} (moved from {})".format(model, old_models[model])
if column["model_type"]:
text += " [%s]" % column["model_type"]
for column in copy.copy(new_records):
model = column['model']
model = column["model"]
if model in new_models:
if model not in old_models:
if inv_model_map(model) not in old_models:
text = 'new model %s' % model
if column['model_type']:
text += " [%s]" % column['model_type']
reprs[column['module']].append(text)
reprs['general'].append('new model %s [module %s]' % (
model, column['module']))
text = "new model %s" % model
if column["model_type"]:
text += " [%s]" % column["model_type"]
reprs[column["module"]].append(text)
reprs["general"].append(
"new model {} [module {}]".format(model, column["module"])
)
else:
moved_module = ''
if column['module'] != module_map(old_models[inv_model_map(
model)]):
moved_module = ' in module %s' % old_models[
inv_model_map(model)]
text = 'new model %s (renamed from %s%s)' % (
model, inv_model_map(model), moved_module)
if column['model_type']:
text += " [%s]" % column['model_type']
reprs[column['module']].append(text)
reprs['general'].append(
'new model %s (renamed from %s) [module %s]' % (
model, inv_model_map(model), column['module']))
moved_module = ""
if column["module"] != module_map(old_models[inv_model_map(model)]):
moved_module = (
" in module %s" % old_models[inv_model_map(model)]
)
text = "new model {} (renamed from {}{})".format(
model,
inv_model_map(model),
moved_module,
)
if column["model_type"]:
text += " [%s]" % column["model_type"]
reprs[column["module"]].append(text)
reprs["general"].append(
"new model %s (renamed from %s) [module %s]"
% (model, inv_model_map(model), column["module"])
)
else:
if column['module'] != module_map(old_models[model]):
text = 'model %s (moved from %s)' % (
model, old_models[model])
if column['model_type']:
text += " [%s]" % column['model_type']
reprs[column['module']].append(text)
if column["module"] != module_map(old_models[model]):
text = "model {} (moved from {})".format(model, old_models[model])
if column["model_type"]:
text += " [%s]" % column["model_type"]
reprs[column["module"]].append(text)
return reprs

View File

@ -1,5 +1,3 @@
from . import openupgrade_record
from . import comparison_config
from . import analysis_wizard
from . import generate_records_wizard
from . import install_all_wizard
from . import upgrade_comparison_config
from . import upgrade_attribute
from . import upgrade_record

View File

@ -0,0 +1,20 @@
# Copyright 2011-2015 Therp BV <https://therp.nl>
# Copyright 2016 Opener B.V. <https://opener.am>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class UpgradeAttribute(models.Model):
_name = "upgrade.attribute"
_description = "Upgrade Attribute"
name = fields.Char(readonly=True)
value = fields.Char(readonly=True)
record_id = fields.Many2one(
comodel_name="upgrade.record",
ondelete="CASCADE",
readonly=True,
)

View File

@ -8,25 +8,25 @@ from odoo import fields, models
from odoo.exceptions import UserError
from odoo.tools.translate import _
from ..lib import apriori
from .. import apriori
class OpenupgradeComparisonConfig(models.Model):
_name = "openupgrade.comparison.config"
_description = "OpenUpgrade Comparison Configuration"
class UpgradeComparisonConfig(models.Model):
_name = "upgrade.comparison.config"
_description = "Upgrade Comparison Configuration"
name = fields.Char()
server = fields.Char(required=True)
server = fields.Char(required=True, default="localhost")
port = fields.Integer(required=True, default=8069)
protocol = fields.Selection(
[("http://", "XML-RPC")],
# ('https://', 'XML-RPC Secure')], not supported by libopenerp
required=True,
default="http://",
)
database = fields.Char(required=True)
username = fields.Char(required=True)
password = fields.Char(required=True)
username = fields.Char(required=True, default="admin")
password = fields.Char(required=True, default="admin")
last_log = fields.Text()
def get_connection(self):
@ -51,8 +51,8 @@ class OpenupgradeComparisonConfig(models.Model):
def analyze(self):
""" Run the analysis wizard """
self.ensure_one()
wizard = self.env["openupgrade.analysis.wizard"].create(
{"server_config": self.id}
wizard = self.env["upgrade.analysis.wizard"].create(
{"server_config_id": self.id}
)
return {
"name": wizard._description,

View File

@ -5,27 +5,18 @@
from odoo import api, fields, models
class Attribute(models.Model):
_name = "openupgrade.attribute"
_description = "OpenUpgrade Attribute"
class UpgradeRecord(models.Model):
_name = "upgrade.record"
_description = "Upgrade Record"
name = fields.Char(readonly=True)
value = fields.Char(readonly=True)
record_id = fields.Many2one(
"openupgrade.record",
ondelete="CASCADE",
readonly=True,
)
class Record(models.Model):
_name = "openupgrade.record"
_description = "OpenUpgrade Record"
name = fields.Char(readonly=True)
module = fields.Char(readonly=True)
model = fields.Char(readonly=True)
field = fields.Char(readonly=True)
mode = fields.Selection(
[("create", "Create"), ("modify", "Modify")],
help="Set to Create if a field is newly created "
@ -33,16 +24,26 @@ class Record(models.Model):
"existing field, set to Modify.",
readonly=True,
)
type = fields.Selection( # Uh oh, reserved keyword
type = fields.Selection(
[("field", "Field"), ("xmlid", "XML ID"), ("model", "Model")],
readonly=True,
)
attribute_ids = fields.One2many("openupgrade.attribute", "record_id", readonly=True)
attribute_ids = fields.One2many(
comodel_name="upgrade.attribute", inverse_name="record_id", readonly=True
)
noupdate = fields.Boolean(readonly=True)
domain = fields.Char(readonly=True)
prefix = fields.Char(compute="_compute_prefix_and_suffix")
suffix = fields.Char(compute="_compute_prefix_and_suffix")
model_original_module = fields.Char(compute="_compute_model_original_module")
model_type = fields.Char(compute="_compute_model_type")
@api.depends("name")

View File

@ -0,0 +1,7 @@
* Stefan Rijnhart <stefan@opener.amsterdam>
* Holger Brunn <hbrunn@therp.nl>
* Pedro M. Baeza <pedro.baeza@gmail.com>
* Ferdinand Gassauer <gass@cc-l-12.chircar.at>
* Florent Xicluna <florent.xicluna@gmail.com>
* Miquel Raïch <miquel.raich@eficent.com>
* Sylvain LE GAL <https://twitter.com/legalsylvain>

View File

@ -0,0 +1 @@
This module provides the tool to generate the database analysis files that indicate how the Odoo data model and module data have changed between two versions of Odoo. Database analysis files for the core modules are included in the OpenUpgrade distribution so as a migration script developer you will not usually need to use this tool yourself. If you do need to run your analysis of a custom set of modules, please refer to the documentation here: https://doc.therp.nl/openupgrade/analysis.html

View File

@ -0,0 +1,3 @@
* scripts/compare_noupdate_xml_records.py should be integrated in the analysis process (#590)
* Log removed modules in the module that owned them (#468)
* Detect renamed many2many tables (#213)

View File

View File

@ -1,4 +1,7 @@
"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
"access_openupgrade_comparison_config","openupgrade.comparison.config","model_openupgrade_comparison_config",base.group_system,1,1,1,1
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_upgrade_record,upgrade.record all,model_upgrade_record,,1,0,0,0
access_upgrade_attribute,upgrade.attribute all,model_upgrade_attribute,,1,0,0,0
access_upgrade_comparison_config,upgrade.comparison.config,model_upgrade_comparison_config,base.group_system,1,1,1,1
access_upgrade_analysis_wizard,access_upgrade_analysis_wizard,model_upgrade_analysis_wizard,base.group_system,1,1,1,1
access_upgrade_generate_record_wizard,access_upgrade_generate_record_wizard,model_upgrade_generate_record_wizard,base.group_system,1,1,1,1
access_upgrade_install_wizard,access_upgrade_install_wizard,model_upgrade_install_wizard,base.group_system,1,1,1,1

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_openupgrade_record access_upgrade_record openupgrade.record all upgrade.record all model_openupgrade_record model_upgrade_record 1 0 0 0
3 access_openupgrade_attribute access_upgrade_attribute openupgrade.attribute all upgrade.attribute all model_openupgrade_attribute model_upgrade_attribute 1 0 0 0
4 access_openupgrade_comparison_config access_upgrade_comparison_config openupgrade.comparison.config upgrade.comparison.config model_openupgrade_comparison_config model_upgrade_comparison_config base.group_system 1 1 1 1
5 access_upgrade_analysis_wizard access_upgrade_analysis_wizard model_upgrade_analysis_wizard base.group_system 1 1 1 1
6 access_upgrade_generate_record_wizard access_upgrade_generate_record_wizard model_upgrade_generate_record_wizard base.group_system 1 1 1 1
7 access_upgrade_install_wizard access_upgrade_install_wizard model_upgrade_install_wizard base.group_system 1 1 1 1

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!-- Top level menu under 'Database structure' -->
<menuitem
id="menu_upgrade"
name="upgrade Development"
parent="base.menu_administration"
sequence="99"
/>
</odoo>

View File

@ -1,13 +1,11 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<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>
<record id="view_upgrade_comparison_config_tree" model="ir.ui.view">
<field name="model">upgrade.comparison.config</field>
<field name="arch" type="xml">
<tree string="OpenUpgrade Comparison Config">
<tree>
<field name="name" />
<field name="protocol" />
<field name="server" />
<field name="port" />
<field name="database" />
@ -15,14 +13,12 @@
</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>
<record id="view_upgrade_comparison_config_form" model="ir.ui.view">
<field name="model">upgrade.comparison.config</field>
<field name="arch" type="xml">
<form string="OpenUpgrade Comparison Config" version="6.1">
<form>
<group>
<field name="name" />
<field name="protocol" />
<field name="server" />
<field name="port" />
<field name="database" />
@ -57,21 +53,18 @@
</form>
</field>
</record>
)
<record
id="action_openupgrade_comparison_config_tree"
model="ir.actions.act_window"
>
<field name="name">OpenUpgrade Comparison Configs</field>
<record id="action_upgrade_comparison_config_tree" model="ir.actions.act_window">
<field name="name">upgrade Comparison Configs</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">openupgrade.comparison.config</field>
<field name="res_model">upgrade.comparison.config</field>
</record>
<menuitem
action="action_openupgrade_comparison_config_tree"
id="menu_openupgrade_comparison_config"
action="action_upgrade_comparison_config_tree"
id="menu_upgrade_comparison_config"
name="Comparison Configurations"
parent="menu_openupgrade"
parent="menu_upgrade"
/>
</odoo>

View File

@ -1,17 +1,8 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!-- Top level menu under 'Database structure' -->
<menuitem
id="menu_openupgrade"
name="OpenUpgrade Development"
parent="base.menu_administration"
sequence="99"
/>
<record id="view_openupgrade_record_search" model="ir.ui.view">
<field name="name">Search view for openupgrade records</field>
<field name="model">openupgrade.record</field>
<record id="view_upgrade_record_search" model="ir.ui.view">
<field name="model">upgrade.record</field>
<field name="arch" type="xml">
<search>
<field name="module" />
@ -39,11 +30,10 @@
</field>
</record>
<record id="view_openupgrade_record_tree" model="ir.ui.view">
<field name="name">view.openupgrade.record.tree</field>
<field name="model">openupgrade.record</field>
<record id="view_upgrade_record_tree" model="ir.ui.view">
<field name="model">upgrade.record</field>
<field name="arch" type="xml">
<tree string="OpenUpgrade Records">
<tree>
<field name="module" />
<field name="model" />
<field name="field" />
@ -54,11 +44,10 @@
</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>
<record id="view_upgrade_record_form" model="ir.ui.view">
<field name="model">upgrade.record</field>
<field name="arch" type="xml">
<form string="OpenUpgrade Record" version="6.1">
<form>
<field name="module" />
<field name="model" />
<field name="field" />
@ -80,17 +69,17 @@
</field>
</record>
<record id="action_openupgrade_record_tree" model="ir.actions.act_window">
<field name="name">OpenUpgrade Records</field>
<record id="action_upgrade_record_tree" model="ir.actions.act_window">
<field name="name">upgrade Records</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">openupgrade.record</field>
<field name="res_model">upgrade.record</field>
</record>
<menuitem
action="action_openupgrade_record_tree"
id="menu_openupgrade_records"
action="action_upgrade_record_tree"
id="menu_upgrade_records"
name="Records"
parent="menu_openupgrade"
parent="menu_upgrade"
/>
</odoo>

View File

@ -0,0 +1,3 @@
from . import upgrade_analysis_wizard
from . import upgrade_generate_record_wizard
from . import upgrade_install_wizard

View File

@ -8,15 +8,15 @@ import os
from odoo import fields, models
from odoo.modules import get_module_path
from ..lib import compare
from .. import compare
class AnalysisWizard(models.TransientModel):
_name = "openupgrade.analysis.wizard"
_description = "OpenUpgrade Analysis Wizard"
class UpgradeAnalysisWizard(models.TransientModel):
_name = "upgrade.analysis.wizard"
_description = "upgrade Analysis Wizard"
server_config = fields.Many2one(
"openupgrade.comparison.config", "Configuration", required=True
server_config_id = fields.Many2one(
"upgrade.comparison.config", "Configuration", required=True
)
state = fields.Selection(
[("init", "Init"), ("ready", "Ready")], readonly=True, default="init"
@ -33,7 +33,7 @@ class AnalysisWizard(models.TransientModel):
change set
"""
def write_file(module, version, content, filename="openupgrade_analysis.txt"):
def write_file(module, version, content, filename="upgrade_analysis.txt"):
module_path = get_module_path(module)
if not module_path:
return "ERROR: could not find module path:\n"
@ -53,9 +53,9 @@ class AnalysisWizard(models.TransientModel):
return None
self.ensure_one()
connection = self.server_config.get_connection()
remote_record_obj = connection.env["openupgrade.record"]
local_record_obj = self.env["openupgrade.record"]
connection = self.server_config_id.get_connection()
remote_record_obj = connection.env["upgrade.record"]
local_record_obj = self.env["upgrade.record"]
# Retrieve field representations and compare
remote_records = remote_record_obj.field_dump()
@ -168,9 +168,9 @@ class AnalysisWizard(models.TransientModel):
"base",
modules["base"].installed_version,
general,
"openupgrade_general_log.txt",
"upgrade_general_log.txt",
)
self.server_config.write({"last_log": general})
self.server_config_id.write({"last_log": general})
self.write({"state": "ready", "log": general})
return {

View File

@ -10,8 +10,8 @@ from odoo.modules.registry import Registry
class GenerateWizard(models.TransientModel):
_name = "openupgrade.generate.records.wizard"
_description = "OpenUpgrade Generate Records Wizard"
_name = "upgrade.generate.record.wizard"
_description = "Upgrade Generate Record Wizard"
_rec_name = "state"
state = fields.Selection([("init", "init"), ("ready", "ready")], default="init")
@ -35,9 +35,9 @@ class GenerateWizard(models.TransientModel):
TODO: update module list and versions, then update all modules?"""
# Truncate the records table
if openupgrade_tools.table_exists(
self.env.cr, "openupgrade_attribute"
) and openupgrade_tools.table_exists(self.env.cr, "openupgrade_record"):
self.env.cr.execute("TRUNCATE openupgrade_attribute, openupgrade_record;")
self.env.cr, "upgrade_attribute"
) and openupgrade_tools.table_exists(self.env.cr, "upgrade_record"):
self.env.cr.execute("TRUNCATE upgrade_attribute, upgrade_record;")
# Run any quirks
self.quirk_standard_calendar_attendances()
@ -67,7 +67,7 @@ class GenerateWizard(models.TransientModel):
# Set domain property
self.env.cr.execute(
""" UPDATE openupgrade_record our
""" UPDATE upgrade_record our
SET domain = iaw.domain
FROM ir_model_data imd
JOIN ir_act_window iaw ON imd.res_id = iaw.id
@ -79,13 +79,13 @@ class GenerateWizard(models.TransientModel):
)
self.env.cache.invalidate(
[
(self.env["openupgrade.record"]._fields["domain"], None),
(self.env["upgrade.record"]._fields["domain"], None),
]
)
# Set noupdate property from ir_model_data
self.env.cr.execute(
""" UPDATE openupgrade_record our
""" UPDATE upgrade_record our
SET noupdate = imd.noupdate
FROM ir_model_data imd
WHERE our.type = 'xmlid'
@ -101,7 +101,7 @@ class GenerateWizard(models.TransientModel):
# Log model records
self.env.cr.execute(
"""INSERT INTO openupgrade_record
"""INSERT INTO upgrade_record
(module, name, model, type)
SELECT imd2.module, imd2.module || '.' || imd.name AS name,
im.model, 'model' AS type

View File

@ -9,9 +9,9 @@ from odoo.osv.expression import AND
from ..blacklist import BLACKLIST_MODULES
class InstallAll(models.TransientModel):
_name = "openupgrade.install.all.wizard"
_description = "OpenUpgrade Install All Wizard"
class UpgradeInstallWizard(models.TransientModel):
_name = "upgrade.install.wizard"
_description = "Upgrade Install Wizard"
state = fields.Selection(
[("init", "init"), ("ready", "ready")], readonly=True, default="init"
@ -22,7 +22,7 @@ class InstallAll(models.TransientModel):
def default_get(self, fields):
"""Update module list and retrieve the number
of installable modules"""
res = super(InstallAll, self).default_get(fields)
res = super().default_get(fields)
update, add = self.env["ir.module.module"].update_list()
modules = self.env["ir.module.module"].search(
[("state", "not in", ["uninstallable", "unknown"])]

View File

@ -1,13 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<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>
<record id="view_upgrade_analysis_wizard_form" model="ir.ui.view">
<field name="model">upgrade.analysis.wizard</field>
<field name="arch" type="xml">
<form string="OpenUpgrade Analysis Wizard">
<form>
<group>
<field name="server_config" readonly="1" />
<field name="server_config_id" readonly="1" />
<field name="state" />
<field
name="log"

View File

@ -1,18 +1,17 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<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>
<record id="view_upgrade_generate_record_wizard_form" model="ir.ui.view">
<field name="model">upgrade.generate.record.wizard</field>
<field name="arch" type="xml">
<form string="OpenUpgrade Generate Records Wizard">
<form string="upgrade Generate Record Wizard">
<field name="state" invisible="1" />
<group states="init" colspan="4">
<p
>This will reinitialize all the modules installed on this database. Do not continue if you use this database in production.</p>
</group>
<group states="ready" colspan="4">
<p>Modules initialized and records created</p>
<p>Modules initialized and record created</p>
</group>
<footer>
<button
@ -28,19 +27,19 @@
</field>
</record>
<record id="action_generate_records" model="ir.actions.act_window">
<record id="action_upgrade_generate_record_wizard" 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="res_model">upgrade.generate.record.wizard</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"
id="menu_upgrade_generate_record"
parent="menu_upgrade"
action="action_upgrade_generate_record_wizard"
sequence="15"
/>

View File

@ -1,11 +1,10 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<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>
<record id="view_upgrade_install_wizard_form" model="ir.ui.view">
<field name="model">upgrade.install.wizard</field>
<field name="arch" type="xml">
<form string="OpenUpgrade Install All Modules Wizard">
<form>
<field name="state" invisible="1" />
<group states="init" colspan="4">
<p
@ -29,19 +28,19 @@
</field>
</record>
<record id="action_install_all" model="ir.actions.act_window">
<field name="name">Install All Modules</field>
<record id="action_upgrade_install_wizard" model="ir.actions.act_window">
<field name="name">Install Modules</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">openupgrade.install.all.wizard</field>
<field name="res_model">upgrade.install.wizard</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"
name="Install Modules"
id="menu_upgrade_install"
parent="menu_upgrade"
action="action_upgrade_install_wizard"
sequence="14"
/>