186 lines
5.9 KiB
Python
186 lines
5.9 KiB
Python
# Copyright 2011-2015 Therp BV <https://therp.nl>
|
|
# Copyright 2016-2020 Opener B.V. <https://opener.am>
|
|
# Copyright 2019 ForgeFlow <https://forgeflow.com>
|
|
# Copyright 2020 GRAP <https://grap.coop>
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
|
|
import ast
|
|
import logging
|
|
import os
|
|
|
|
from odoo import api, fields, models
|
|
from odoo.exceptions import ValidationError
|
|
from odoo.modules.module import MANIFEST_NAMES, get_module_path
|
|
from odoo.tools.translate import _
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class UpgradeRecord(models.Model):
|
|
_name = "upgrade.record"
|
|
_description = "Upgrade 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 "
|
|
"in this module. If this module modifies an attribute of an "
|
|
"existing field, set to Modify.",
|
|
readonly=True,
|
|
)
|
|
|
|
type = fields.Selection(
|
|
[("field", "Field"), ("xmlid", "XML ID"), ("model", "Model")],
|
|
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)
|
|
|
|
definition = 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")
|
|
def _compute_prefix_and_suffix(self):
|
|
for rec in self:
|
|
rec.prefix, rec.suffix = rec.name.split(".", 1)
|
|
|
|
@api.depends("model", "type")
|
|
def _compute_model_original_module(self):
|
|
for rec in self:
|
|
if rec.type == "model":
|
|
rec.model_original_module = self.env[rec.model]._original_module
|
|
else:
|
|
rec.model_original_module = ""
|
|
|
|
@api.depends("model", "type")
|
|
def _compute_model_type(self):
|
|
for rec in self:
|
|
if rec.type == "model":
|
|
model = self.env[rec.model]
|
|
if model._auto and model._transient:
|
|
rec.model_type = "transient"
|
|
elif model._auto:
|
|
rec.model_type = ""
|
|
elif not model._auto and model._abstract:
|
|
rec.model_type = "abstract"
|
|
else:
|
|
rec.model_type = "sql_view"
|
|
else:
|
|
rec.model_type = ""
|
|
|
|
@api.model
|
|
def field_dump(self):
|
|
keys = [
|
|
"attachment",
|
|
"module",
|
|
"mode",
|
|
"model",
|
|
"field",
|
|
"type",
|
|
"isfunction",
|
|
"isproperty",
|
|
"isrelated",
|
|
"relation",
|
|
"required",
|
|
"stored",
|
|
"selection_keys",
|
|
"hasdefault",
|
|
"table",
|
|
"_inherits",
|
|
"_order",
|
|
]
|
|
|
|
template = {x: False for x in keys}
|
|
data = []
|
|
for record in self.search([("type", "=", "field")]):
|
|
repre = template.copy()
|
|
repre.update(
|
|
{
|
|
"module": record.module,
|
|
"model": record.model,
|
|
"field": record.field,
|
|
"mode": record.mode,
|
|
}
|
|
)
|
|
repre.update({x.name: x.value for x in record.attribute_ids})
|
|
if repre["table"]:
|
|
repre.update(
|
|
{
|
|
"column1": self.env[repre["model"]]
|
|
._fields[repre["field"]]
|
|
.column1,
|
|
"column2": self.env[repre["model"]]
|
|
._fields[repre["field"]]
|
|
.column2,
|
|
}
|
|
)
|
|
data.append(repre)
|
|
return data
|
|
|
|
@api.model
|
|
def list_modules(self):
|
|
"""Return the set of covered modules"""
|
|
self.env.cr.execute(
|
|
"""SELECT DISTINCT(module) FROM upgrade_record
|
|
ORDER BY module"""
|
|
)
|
|
return [module for module, in self.env.cr.fetchall()]
|
|
|
|
@staticmethod
|
|
def _read_manifest(addon_dir):
|
|
for manifest_name in MANIFEST_NAMES:
|
|
if os.access(os.path.join(addon_dir, manifest_name), os.R_OK):
|
|
with open(os.path.join(addon_dir, manifest_name), "r") as f:
|
|
manifest_string = f.read()
|
|
return ast.literal_eval(manifest_string)
|
|
raise ValidationError(
|
|
_("No manifest found in %(addon_dir)s") % {"addon_dir": addon_dir}
|
|
)
|
|
|
|
@api.model
|
|
def get_xml_records(self, module):
|
|
"""Return all XML records from the given module"""
|
|
addon_dir = get_module_path(module)
|
|
manifest = self._read_manifest(addon_dir)
|
|
# The order of the keys are important.
|
|
# Load files in the same order as in
|
|
# module/loading.py:load_module_graph
|
|
files = []
|
|
for key in ["init_xml", "update_xml", "data"]:
|
|
if not manifest.get(key):
|
|
continue
|
|
for xml_file in manifest[key]:
|
|
if not xml_file.lower().endswith(".xml"):
|
|
continue
|
|
parts = xml_file.split("/")
|
|
try:
|
|
with open(os.path.join(addon_dir, *parts), "r") as xml_handle:
|
|
files.append(xml_handle.read())
|
|
except UnicodeDecodeError:
|
|
_logger.warning(
|
|
"Encoding error: Unable to read %s",
|
|
os.path.join(addon_dir, *parts),
|
|
)
|
|
continue
|
|
return files
|