[IMP] module_analysis: black, isort, prettier
parent
2e88c5b31e
commit
8fc7d87806
|
@ -1,2 +1,2 @@
|
||||||
from . import models
|
from . import models
|
||||||
from . post_init_hook import analyse_installed_modules
|
from .post_init_hook import analyse_installed_modules
|
||||||
|
|
|
@ -3,32 +3,28 @@
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
{
|
{
|
||||||
'name': "Module Analysis",
|
"name": "Module Analysis",
|
||||||
'summary': "Add analysis tools regarding installed modules"
|
"summary": "Add analysis tools regarding installed modules"
|
||||||
" to know which installed modules comes from Odoo Core, OCA, or are"
|
" to know which installed modules comes from Odoo Core, OCA, or are"
|
||||||
" custom modules",
|
" custom modules",
|
||||||
'author': 'GRAP, Odoo Community Association (OCA)',
|
"author": "GRAP, Odoo Community Association (OCA)",
|
||||||
'website': "https://github.com/OCA/server-tools/",
|
"website": "https://github.com/OCA/server-tools/",
|
||||||
'version': '12.0.1.0.0',
|
"version": "12.0.1.0.0",
|
||||||
'license': 'AGPL-3',
|
"license": "AGPL-3",
|
||||||
'depends': [
|
"depends": ["base",],
|
||||||
'base',
|
"data": [
|
||||||
|
"security/ir.model.access.csv",
|
||||||
|
"views/menu.xml",
|
||||||
|
"views/view_base_module_update.xml",
|
||||||
|
"views/view_ir_module_author.xml",
|
||||||
|
"views/view_ir_module_type.xml",
|
||||||
|
"views/view_ir_module_type_rule.xml",
|
||||||
|
"views/view_ir_module_module.xml",
|
||||||
|
"data/ir_config_parameter.xml",
|
||||||
|
"data/ir_module_type.xml",
|
||||||
|
"data/ir_module_type_rule.xml",
|
||||||
],
|
],
|
||||||
'data': [
|
"external_dependencies": {"python": ["pygount"],},
|
||||||
'security/ir.model.access.csv',
|
"post_init_hook": "analyse_installed_modules",
|
||||||
'views/menu.xml',
|
"installable": True,
|
||||||
'views/view_base_module_update.xml',
|
|
||||||
'views/view_ir_module_author.xml',
|
|
||||||
'views/view_ir_module_type.xml',
|
|
||||||
'views/view_ir_module_type_rule.xml',
|
|
||||||
'views/view_ir_module_module.xml',
|
|
||||||
'data/ir_config_parameter.xml',
|
|
||||||
'data/ir_module_type.xml',
|
|
||||||
'data/ir_module_type_rule.xml',
|
|
||||||
],
|
|
||||||
'external_dependencies': {
|
|
||||||
'python': ['pygount'],
|
|
||||||
},
|
|
||||||
'post_init_hook': 'analyse_installed_modules',
|
|
||||||
'installable': True,
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,16 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<!--
|
<!--
|
||||||
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
|
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
|
||||||
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<odoo noupdate="1">
|
<odoo noupdate="1">
|
||||||
|
|
||||||
<record id="parameter_exclude_directories" model="ir.config_parameter">
|
<record id="parameter_exclude_directories" model="ir.config_parameter">
|
||||||
<field name="key">module_analysis.exclude_directories</field>
|
<field name="key">module_analysis.exclude_directories</field>
|
||||||
<field name="value">lib,demo,test,tests,doc,description</field>
|
<field name="value">lib,demo,test,tests,doc,description</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="parameter_exclude_files" model="ir.config_parameter">
|
<record id="parameter_exclude_files" model="ir.config_parameter">
|
||||||
<field name="key">module_analysis.exclude_files</field>
|
<field name="key">module_analysis.exclude_files</field>
|
||||||
<field name="value">__openerp__.py,__manifest__.py</field>
|
<field name="value">__openerp__.py,__manifest__.py</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
@ -1,18 +1,14 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<!--
|
<!--
|
||||||
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
|
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
|
||||||
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<odoo>
|
<odoo>
|
||||||
|
|
||||||
<record id="module_type_odoo" model="ir.module.type">
|
<record id="module_type_odoo" model="ir.module.type">
|
||||||
<field name="name">Odoo Core</field>
|
<field name="name">Odoo Core</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="module_type_oca" model="ir.module.type">
|
<record id="module_type_oca" model="ir.module.type">
|
||||||
<field name="name">OCA</field>
|
<field name="name">OCA</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
@ -1,42 +1,37 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<!--
|
<!--
|
||||||
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
|
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
|
||||||
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<odoo>
|
<odoo>
|
||||||
|
|
||||||
<!-- Odoo Rules -->
|
<!-- Odoo Rules -->
|
||||||
<record id="module_type_rule_odoo_a" model="ir.module.type.rule">
|
<record id="module_type_rule_odoo_a" model="ir.module.type.rule">
|
||||||
<field name="sequence">1</field>
|
<field name="sequence">1</field>
|
||||||
<field name="module_domain">[('author_ids', 'ilike', 'Odoo S.A')]</field>
|
<field name="module_domain">[('author_ids', 'ilike', 'Odoo S.A')]</field>
|
||||||
<field name="module_type_id" ref="module_type_odoo"/>
|
<field name="module_type_id" ref="module_type_odoo" />
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="module_type_rule_odoo_b" model="ir.module.type.rule">
|
<record id="module_type_rule_odoo_b" model="ir.module.type.rule">
|
||||||
<field name="sequence">2</field>
|
<field name="sequence">2</field>
|
||||||
<field name="module_domain">[('author_ids', 'ilike', 'OpenERP SA')]</field>
|
<field name="module_domain">[('author_ids', 'ilike', 'OpenERP SA')]</field>
|
||||||
<field name="module_type_id" ref="module_type_odoo"/>
|
<field name="module_type_id" ref="module_type_odoo" />
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="module_type_rule_odoo_c" model="ir.module.type.rule">
|
<record id="module_type_rule_odoo_c" model="ir.module.type.rule">
|
||||||
<field name="sequence">3</field>
|
<field name="sequence">3</field>
|
||||||
<field name="module_domain">[('author_ids', '=', 'Odoo SA')]</field>
|
<field name="module_domain">[('author_ids', '=', 'Odoo SA')]</field>
|
||||||
<field name="module_type_id" ref="module_type_odoo"/>
|
<field name="module_type_id" ref="module_type_odoo" />
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="module_type_rule_odoo_d" model="ir.module.type.rule">
|
<record id="module_type_rule_odoo_d" model="ir.module.type.rule">
|
||||||
<field name="sequence">4</field>
|
<field name="sequence">4</field>
|
||||||
<field name="module_domain">[('author_ids', '=', 'Odoo')]</field>
|
<field name="module_domain">[('author_ids', '=', 'Odoo')]</field>
|
||||||
<field name="module_type_id" ref="module_type_odoo"/>
|
<field name="module_type_id" ref="module_type_odoo" />
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<!-- OCA Rules -->
|
<!-- OCA Rules -->
|
||||||
<record id="module_type_rule_oca_a" model="ir.module.type.rule">
|
<record id="module_type_rule_oca_a" model="ir.module.type.rule">
|
||||||
<field name="sequence">100</field>
|
<field name="sequence">100</field>
|
||||||
<field name="module_domain">[('author_ids', '=', 'Odoo Community Association (OCA)')]</field>
|
<field
|
||||||
<field name="module_type_id" ref="module_type_oca"/>
|
name="module_domain"
|
||||||
|
>[('author_ids', '=', 'Odoo Community Association (OCA)')]</field>
|
||||||
|
<field name="module_type_id" ref="module_type_oca" />
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
@ -6,13 +6,15 @@ from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
class BaseModuleUpdate(models.TransientModel):
|
class BaseModuleUpdate(models.TransientModel):
|
||||||
_inherit = 'base.module.update'
|
_inherit = "base.module.update"
|
||||||
|
|
||||||
analyse_installed_modules = fields.Boolean(
|
analyse_installed_modules = fields.Boolean(
|
||||||
string='Analyse Installed Modules', default=True)
|
string="Analyse Installed Modules", default=True
|
||||||
|
)
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def update_module(self):
|
def update_module(self):
|
||||||
return super(BaseModuleUpdate, self.with_context(
|
return super(
|
||||||
analyse_installed_modules=self.analyse_installed_modules)
|
BaseModuleUpdate,
|
||||||
|
self.with_context(analyse_installed_modules=self.analyse_installed_modules),
|
||||||
).update_module()
|
).update_module()
|
||||||
|
|
|
@ -6,34 +6,41 @@ from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
class IrModuleAuthor(models.Model):
|
class IrModuleAuthor(models.Model):
|
||||||
_name = 'ir.module.author'
|
_name = "ir.module.author"
|
||||||
_description = 'Modules Authors'
|
_description = "Modules Authors"
|
||||||
|
|
||||||
name = fields.Char(string='Name', required=True)
|
name = fields.Char(string="Name", required=True)
|
||||||
|
|
||||||
installed_module_ids = fields.Many2many(
|
installed_module_ids = fields.Many2many(
|
||||||
string='Modules', comodel_name='ir.module.module',
|
string="Modules",
|
||||||
relation='ir_module_module_author_rel')
|
comodel_name="ir.module.module",
|
||||||
|
relation="ir_module_module_author_rel",
|
||||||
|
)
|
||||||
|
|
||||||
installed_module_qty = fields.Integer(
|
installed_module_qty = fields.Integer(
|
||||||
string="Installed Modules Quantity",
|
string="Installed Modules Quantity",
|
||||||
compute='_compute_installed_module_qty', store=True)
|
compute="_compute_installed_module_qty",
|
||||||
|
store=True,
|
||||||
|
)
|
||||||
|
|
||||||
_sql_constraints = [
|
_sql_constraints = [
|
||||||
('name_uniq', 'unique(name)',
|
(
|
||||||
'The name of the modules author should be unique per database!'),
|
"name_uniq",
|
||||||
|
"unique(name)",
|
||||||
|
"The name of the modules author should be unique per database!",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
@api.depends('installed_module_ids')
|
@api.depends("installed_module_ids")
|
||||||
def _compute_installed_module_qty(self):
|
def _compute_installed_module_qty(self):
|
||||||
for author in self:
|
for author in self:
|
||||||
author.installed_module_qty = len(author.installed_module_ids)
|
author.installed_module_qty = len(author.installed_module_ids)
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _get_or_create(self, name):
|
def _get_or_create(self, name):
|
||||||
authors = self.search([('name', '=', name)])
|
authors = self.search([("name", "=", name)])
|
||||||
if authors:
|
if authors:
|
||||||
return authors[0]
|
return authors[0]
|
||||||
else:
|
else:
|
||||||
return self.create({'name': name})
|
return self.create({"name": name})
|
||||||
|
|
|
@ -2,39 +2,40 @@
|
||||||
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
import os
|
|
||||||
import pygount
|
|
||||||
from pathlib import Path
|
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pygount
|
||||||
|
|
||||||
from odoo import api, fields, models
|
from odoo import api, fields, models
|
||||||
from odoo.tools.safe_eval import safe_eval
|
|
||||||
from odoo.modules.module import get_module_path
|
from odoo.modules.module import get_module_path
|
||||||
|
from odoo.tools.safe_eval import safe_eval
|
||||||
|
|
||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class IrModuleModule(models.Model):
|
class IrModuleModule(models.Model):
|
||||||
_inherit = 'ir.module.module'
|
_inherit = "ir.module.module"
|
||||||
|
|
||||||
author_ids = fields.Many2many(
|
author_ids = fields.Many2many(
|
||||||
string='Authors', comodel_name='ir.module.author', readonly=True,
|
string="Authors",
|
||||||
relation='ir_module_module_author_rel')
|
comodel_name="ir.module.author",
|
||||||
|
readonly=True,
|
||||||
|
relation="ir_module_module_author_rel",
|
||||||
|
)
|
||||||
|
|
||||||
module_type_id = fields.Many2one(
|
module_type_id = fields.Many2one(
|
||||||
string='Module Type', comodel_name='ir.module.type', readonly=True)
|
string="Module Type", comodel_name="ir.module.type", readonly=True
|
||||||
|
)
|
||||||
|
|
||||||
python_code_qty = fields.Integer(
|
python_code_qty = fields.Integer(string="Python Code Quantity", readonly=True)
|
||||||
string='Python Code Quantity', readonly=True)
|
|
||||||
|
|
||||||
xml_code_qty = fields.Integer(
|
xml_code_qty = fields.Integer(string="XML Code Quantity", readonly=True)
|
||||||
string='XML Code Quantity', readonly=True)
|
|
||||||
|
|
||||||
js_code_qty = fields.Integer(
|
js_code_qty = fields.Integer(string="JS Code Quantity", readonly=True)
|
||||||
string='JS Code Quantity', readonly=True)
|
|
||||||
|
|
||||||
css_code_qty = fields.Integer(
|
css_code_qty = fields.Integer(string="CSS Code Quantity", readonly=True)
|
||||||
string='CSS Code Quantity', readonly=True)
|
|
||||||
|
|
||||||
# Overloadable Section
|
# Overloadable Section
|
||||||
@api.model
|
@api.model
|
||||||
|
@ -51,76 +52,69 @@ class IrModuleModule(models.Model):
|
||||||
field_name: Odoo field name to store the analysis
|
field_name: Odoo field name to store the analysis
|
||||||
"""
|
"""
|
||||||
return {
|
return {
|
||||||
'.py': {
|
".py": {"code": "python_code_qty",},
|
||||||
'code': 'python_code_qty',
|
".xml": {"code": "xml_code_qty",},
|
||||||
},
|
".js": {"code": "js_code_qty",},
|
||||||
'.xml': {
|
".css": {"code": "css_code_qty",},
|
||||||
'code': 'xml_code_qty',
|
|
||||||
},
|
|
||||||
'.js': {
|
|
||||||
'code': 'js_code_qty',
|
|
||||||
},
|
|
||||||
'.css': {
|
|
||||||
'code': 'css_code_qty',
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _get_clean_analyse_values(self):
|
def _get_clean_analyse_values(self):
|
||||||
"""List of fields to unset when a module is uninstalled"""
|
"""List of fields to unset when a module is uninstalled"""
|
||||||
return {
|
return {
|
||||||
'author_ids': [6, 0, []],
|
"author_ids": [6, 0, []],
|
||||||
'module_type_id': False,
|
"module_type_id": False,
|
||||||
'python_code_qty': False,
|
"python_code_qty": False,
|
||||||
'xml_code_qty': 0,
|
"xml_code_qty": 0,
|
||||||
'js_code_qty': 0,
|
"js_code_qty": 0,
|
||||||
'css_code_qty': 0,
|
"css_code_qty": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _get_module_encoding(self, file_ext):
|
def _get_module_encoding(self, file_ext):
|
||||||
return 'utf-8'
|
return "utf-8"
|
||||||
|
|
||||||
# Overload Section
|
# Overload Section
|
||||||
@api.model
|
@api.model
|
||||||
def update_list(self):
|
def update_list(self):
|
||||||
res = super().update_list()
|
res = super().update_list()
|
||||||
if self.env.context.get('analyse_installed_modules', False):
|
if self.env.context.get("analyse_installed_modules", False):
|
||||||
self.search([('state', '=', 'installed')]).button_analyse_code()
|
self.search([("state", "=", "installed")]).button_analyse_code()
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def write(self, vals):
|
def write(self, vals):
|
||||||
res = super().write(vals)
|
res = super().write(vals)
|
||||||
if vals.get('state', False) == 'installed':
|
if vals.get("state", False) == "installed":
|
||||||
self.button_analyse_code()
|
self.button_analyse_code()
|
||||||
elif vals.get('state', False) == 'uninstalled'\
|
elif vals.get("state", False) == "uninstalled" and "module_analysis" not in [
|
||||||
and 'module_analysis' not in [x.name for x in self]:
|
x.name for x in self
|
||||||
|
]:
|
||||||
self.write(self._get_clean_analyse_values())
|
self.write(self._get_clean_analyse_values())
|
||||||
return res
|
return res
|
||||||
|
|
||||||
# Public Section
|
# Public Section
|
||||||
@api.multi
|
@api.multi
|
||||||
def button_analyse_code(self):
|
def button_analyse_code(self):
|
||||||
IrModuleAuthor = self.env['ir.module.author']
|
IrModuleAuthor = self.env["ir.module.author"]
|
||||||
IrModuleTypeRule = self.env['ir.module.type.rule']
|
IrModuleTypeRule = self.env["ir.module.type.rule"]
|
||||||
rules = IrModuleTypeRule.search([])
|
rules = IrModuleTypeRule.search([])
|
||||||
|
|
||||||
cfg = self.env['ir.config_parameter']
|
cfg = self.env["ir.config_parameter"]
|
||||||
val = cfg.get_param('module_analysis.exclude_directories', '')
|
val = cfg.get_param("module_analysis.exclude_directories", "")
|
||||||
exclude_directories = [x.strip() for x in val.split(',') if x.strip()]
|
exclude_directories = [x.strip() for x in val.split(",") if x.strip()]
|
||||||
val = cfg.get_param('module_analysis.exclude_files', '')
|
val = cfg.get_param("module_analysis.exclude_files", "")
|
||||||
exclude_files = [x.strip() for x in val.split(',') if x.strip()]
|
exclude_files = [x.strip() for x in val.split(",") if x.strip()]
|
||||||
|
|
||||||
for module in self:
|
for module in self:
|
||||||
_logger.info("Analysing Code for module %s ..." % (module.name))
|
_logger.info("Analysing Code for module %s ..." % (module.name))
|
||||||
|
|
||||||
# Update Authors, based on manifest key
|
# Update Authors, based on manifest key
|
||||||
authors = []
|
authors = []
|
||||||
if module.author and module.author[0] == '[':
|
if module.author and module.author[0] == "[":
|
||||||
author_txt_list = safe_eval(module.author)
|
author_txt_list = safe_eval(module.author)
|
||||||
else:
|
else:
|
||||||
author_txt_list = module.author.split(',')
|
author_txt_list = module.author.split(",")
|
||||||
|
|
||||||
author_txt_list = [x.strip() for x in author_txt_list]
|
author_txt_list = [x.strip() for x in author_txt_list]
|
||||||
author_txt_list = [x for x in author_txt_list if x]
|
author_txt_list = [x for x in author_txt_list if x]
|
||||||
|
@ -141,27 +135,28 @@ class IrModuleModule(models.Model):
|
||||||
analysed_datas = self._get_analyse_data_dict()
|
analysed_datas = self._get_analyse_data_dict()
|
||||||
file_extensions = analysed_datas.keys()
|
file_extensions = analysed_datas.keys()
|
||||||
file_list = self._get_files_to_analyse(
|
file_list = self._get_files_to_analyse(
|
||||||
module_path, file_extensions, exclude_directories,
|
module_path, file_extensions, exclude_directories, exclude_files
|
||||||
exclude_files)
|
)
|
||||||
|
|
||||||
for file_path, file_ext in file_list:
|
for file_path, file_ext in file_list:
|
||||||
file_res = pygount.source_analysis(
|
file_res = pygount.source_analysis(
|
||||||
file_path, '',
|
file_path, "", encoding=self._get_module_encoding(file_ext)
|
||||||
encoding=self._get_module_encoding(file_ext))
|
)
|
||||||
for k, v in analysed_datas.get(file_ext).items():
|
for k, v in analysed_datas.get(file_ext).items():
|
||||||
v['value'] += getattr(file_res, k)
|
v["value"] += getattr(file_res, k)
|
||||||
|
|
||||||
# Update the module with the datas
|
# Update the module with the datas
|
||||||
values = {}
|
values = {}
|
||||||
for file_ext, analyses in analysed_datas.items():
|
for file_ext, analyses in analysed_datas.items():
|
||||||
for k, v in analyses.items():
|
for k, v in analyses.items():
|
||||||
values[v['field']] = v['value']
|
values[v["field"]] = v["value"]
|
||||||
module.write(values)
|
module.write(values)
|
||||||
|
|
||||||
# Custom Section
|
# Custom Section
|
||||||
@api.model
|
@api.model
|
||||||
def _get_files_to_analyse(
|
def _get_files_to_analyse(
|
||||||
self, path, file_extensions, exclude_directories, exclude_files):
|
self, path, file_extensions, exclude_directories, exclude_files
|
||||||
|
):
|
||||||
res = []
|
res = []
|
||||||
for root, dirs, files in os.walk(path, followlinks=True):
|
for root, dirs, files in os.walk(path, followlinks=True):
|
||||||
if set(Path(root).parts) & set(exclude_directories):
|
if set(Path(root).parts) & set(exclude_directories):
|
||||||
|
@ -179,8 +174,5 @@ class IrModuleModule(models.Model):
|
||||||
res_dict = self._get_analyse_settings().copy()
|
res_dict = self._get_analyse_settings().copy()
|
||||||
for file_ext, analyse_dict in res_dict.items():
|
for file_ext, analyse_dict in res_dict.items():
|
||||||
for analyse_type, v in analyse_dict.items():
|
for analyse_type, v in analyse_dict.items():
|
||||||
analyse_dict[analyse_type] = {
|
analyse_dict[analyse_type] = {"field": v, "value": 0}
|
||||||
'field': v,
|
|
||||||
'value': 0
|
|
||||||
}
|
|
||||||
return res_dict
|
return res_dict
|
||||||
|
|
|
@ -6,25 +6,26 @@ from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
class IrModuleType(models.Model):
|
class IrModuleType(models.Model):
|
||||||
_name = 'ir.module.type'
|
_name = "ir.module.type"
|
||||||
_description = 'Modules Types'
|
_description = "Modules Types"
|
||||||
_order = 'sequence'
|
_order = "sequence"
|
||||||
|
|
||||||
name = fields.Char(string='Name', required=True)
|
name = fields.Char(string="Name", required=True)
|
||||||
|
|
||||||
sequence = fields.Integer(string='Sequence')
|
sequence = fields.Integer(string="Sequence")
|
||||||
|
|
||||||
installed_module_ids = fields.One2many(
|
installed_module_ids = fields.One2many(
|
||||||
string='Installed Modules', comodel_name='ir.module.module',
|
string="Installed Modules",
|
||||||
inverse_name='module_type_id')
|
comodel_name="ir.module.module",
|
||||||
|
inverse_name="module_type_id",
|
||||||
|
)
|
||||||
|
|
||||||
installed_module_qty = fields.Integer(
|
installed_module_qty = fields.Integer(
|
||||||
string='Modules Quantity', compute='_compute_installed_module_qty',
|
string="Modules Quantity", compute="_compute_installed_module_qty", store=True
|
||||||
store=True)
|
)
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
@api.depends('installed_module_ids.module_type_id')
|
@api.depends("installed_module_ids.module_type_id")
|
||||||
def _compute_installed_module_qty(self):
|
def _compute_installed_module_qty(self):
|
||||||
for module_type in self:
|
for module_type in self:
|
||||||
module_type.installed_module_qty = len(
|
module_type.installed_module_qty = len(module_type.installed_module_ids)
|
||||||
module_type.installed_module_ids)
|
|
||||||
|
|
|
@ -7,24 +7,24 @@ from odoo.tools.safe_eval import safe_eval
|
||||||
|
|
||||||
|
|
||||||
class IrModuleType(models.Model):
|
class IrModuleType(models.Model):
|
||||||
_name = 'ir.module.type.rule'
|
_name = "ir.module.type.rule"
|
||||||
_description = 'Modules Types Rules'
|
_description = "Modules Types Rules"
|
||||||
_order = 'sequence'
|
_order = "sequence"
|
||||||
|
|
||||||
sequence = fields.Integer(string='Sequence')
|
sequence = fields.Integer(string="Sequence")
|
||||||
|
|
||||||
module_domain = fields.Char(
|
module_domain = fields.Char(string="Module Domain", required=True, default="[]")
|
||||||
string='Module Domain', required=True, default='[]')
|
|
||||||
|
|
||||||
module_type_id = fields.Many2one(
|
module_type_id = fields.Many2one(
|
||||||
string='Module type', comodel_name='ir.module.type', required=True)
|
string="Module type", comodel_name="ir.module.type", required=True
|
||||||
|
)
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def _get_module_type_id_from_module(self, module):
|
def _get_module_type_id_from_module(self, module):
|
||||||
IrModuleModule = self.env['ir.module.module']
|
IrModuleModule = self.env["ir.module.module"]
|
||||||
for rule in self:
|
for rule in self:
|
||||||
domain = safe_eval(rule.module_domain)
|
domain = safe_eval(rule.module_domain)
|
||||||
domain.append(('id', '=', module.id))
|
domain.append(("id", "=", module.id))
|
||||||
if IrModuleModule.search(domain):
|
if IrModuleModule.search(domain):
|
||||||
return rule.module_type_id.id
|
return rule.module_type_id.id
|
||||||
return False
|
return False
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
from odoo import api, SUPERUSER_ID
|
from odoo import SUPERUSER_ID, api
|
||||||
|
|
||||||
|
|
||||||
def analyse_installed_modules(cr, registry):
|
def analyse_installed_modules(cr, registry):
|
||||||
with api.Environment.manage():
|
with api.Environment.manage():
|
||||||
env = api.Environment(cr, SUPERUSER_ID, {})
|
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||||
installed_modules = env['ir.module.module'].search(
|
installed_modules = env["ir.module.module"].search(
|
||||||
[('state', '=', 'installed')])
|
[("state", "=", "installed")]
|
||||||
|
)
|
||||||
installed_modules.button_analyse_code()
|
installed_modules.button_analyse_code()
|
||||||
|
|
|
@ -5,4 +5,3 @@ Open the stats to analyse the detail of the code installed
|
||||||
.. image:: ../static/description/analysis_pivot.png
|
.. image:: ../static/description/analysis_pivot.png
|
||||||
|
|
||||||
.. image:: ../static/description/analysis_pie.png
|
.. image:: ../static/description/analysis_pie.png
|
||||||
|
|
||||||
|
|
|
@ -7,27 +7,26 @@ from odoo.tests.common import TransactionCase, post_install
|
||||||
|
|
||||||
@post_install(True)
|
@post_install(True)
|
||||||
class TestModule(TransactionCase):
|
class TestModule(TransactionCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
self.IrModuleModule = self.env['ir.module.module']
|
self.IrModuleModule = self.env["ir.module.module"]
|
||||||
|
|
||||||
def test_installed_modules(self):
|
def test_installed_modules(self):
|
||||||
installed_modules = self.IrModuleModule.search(
|
installed_modules = self.IrModuleModule.search([("state", "=", "installed")])
|
||||||
[('state', '=', 'installed')])
|
|
||||||
for module in installed_modules:
|
for module in installed_modules:
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
module.python_code_qty > 0 or
|
module.python_code_qty > 0
|
||||||
module.xml_code_qty > 0 or
|
or module.xml_code_qty > 0
|
||||||
module.js_code_qty > 0,
|
or module.js_code_qty > 0,
|
||||||
"module '%s' doesn't have code analysed defined, whereas it is"
|
"module '%s' doesn't have code analysed defined, whereas it is"
|
||||||
" installed." % (module.name))
|
" installed." % (module.name),
|
||||||
|
)
|
||||||
|
|
||||||
def test_uninstalled_modules(self):
|
def test_uninstalled_modules(self):
|
||||||
uninstalled_modules = self.IrModuleModule.search(
|
uninstalled_modules = self.IrModuleModule.search([("state", "!=", "installed")])
|
||||||
[('state', '!=', 'installed')])
|
|
||||||
for module in uninstalled_modules:
|
for module in uninstalled_modules:
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
module.python_code_qty == 0,
|
module.python_code_qty == 0,
|
||||||
"module '%s' has python lines defined, whereas it is"
|
"module '%s' has python lines defined, whereas it is"
|
||||||
" not installed." % (module.name))
|
" not installed." % (module.name),
|
||||||
|
)
|
||||||
|
|
|
@ -1,24 +1,27 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<!--
|
<!--
|
||||||
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
|
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
|
||||||
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<odoo>
|
<odoo>
|
||||||
|
<menuitem
|
||||||
<menuitem id="menu_module_analysis"
|
id="menu_module_analysis"
|
||||||
name="Analysis"
|
name="Analysis"
|
||||||
parent="base.menu_management" sequence="100"
|
parent="base.menu_management"
|
||||||
groups="base.group_no_one"/>
|
sequence="100"
|
||||||
|
groups="base.group_no_one"
|
||||||
<menuitem id="menu_module_analysis_settings"
|
/>
|
||||||
|
<menuitem
|
||||||
|
id="menu_module_analysis_settings"
|
||||||
name="Settings"
|
name="Settings"
|
||||||
parent="menu_module_analysis" sequence="10"/>
|
parent="menu_module_analysis"
|
||||||
|
sequence="10"
|
||||||
<menuitem id="menu_module_analysis_reporting"
|
/>
|
||||||
|
<menuitem
|
||||||
|
id="menu_module_analysis_reporting"
|
||||||
name="Reporting"
|
name="Reporting"
|
||||||
parent="menu_module_analysis" sequence="20"/>
|
parent="menu_module_analysis"
|
||||||
|
sequence="20"
|
||||||
|
/>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
@ -1,20 +1,17 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<!--
|
<!--
|
||||||
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
|
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
|
||||||
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<odoo>
|
<odoo>
|
||||||
|
|
||||||
<record id="view_base_module_update_form" model="ir.ui.view">
|
<record id="view_base_module_update_form" model="ir.ui.view">
|
||||||
<field name="model">base.module.update</field>
|
<field name="model">base.module.update</field>
|
||||||
<field name="inherit_id" ref="base.view_base_module_update"/>
|
<field name="inherit_id" ref="base.view_base_module_update" />
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<xpath expr='//group[@states="init"]' position="inside">
|
<xpath expr='//group[@states="init"]' position="inside">
|
||||||
<field name="analyse_installed_modules"/>
|
<field name="analyse_installed_modules" />
|
||||||
</xpath>
|
</xpath>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
@ -1,40 +1,39 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<!--
|
<!--
|
||||||
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
|
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
|
||||||
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<odoo>
|
<odoo>
|
||||||
|
|
||||||
<record id="view_ir_module_author_form" model="ir.ui.view">
|
<record id="view_ir_module_author_form" model="ir.ui.view">
|
||||||
<field name="model">ir.module.author</field>
|
<field name="model">ir.module.author</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form>
|
<form>
|
||||||
<sheet>
|
<sheet>
|
||||||
<div class="oe_title">
|
<div class="oe_title">
|
||||||
<div class="oe_edit_only"><label for="name"/></div>
|
<div class="oe_edit_only">
|
||||||
<h1><field name="name"/></h1>
|
<label for="name" />
|
||||||
|
</div>
|
||||||
|
<h1>
|
||||||
|
<field name="name" />
|
||||||
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<group>
|
<group>
|
||||||
<field name="installed_module_qty"/>
|
<field name="installed_module_qty" />
|
||||||
</group>
|
</group>
|
||||||
</sheet>
|
</sheet>
|
||||||
</form>
|
</form>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|
||||||
<record id="view_ir_module_author_tree" model="ir.ui.view">
|
<record id="view_ir_module_author_tree" model="ir.ui.view">
|
||||||
<field name="model">ir.module.author</field>
|
<field name="model">ir.module.author</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree>
|
<tree>
|
||||||
<field name="name"/>
|
<field name="name" />
|
||||||
<field name="installed_module_qty"/>
|
<field name="installed_module_qty" />
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="action_ir_module_author" model="ir.actions.act_window">
|
<record id="action_ir_module_author" model="ir.actions.act_window">
|
||||||
<field name="name">Modules Authors</field>
|
<field name="name">Modules Authors</field>
|
||||||
<field name="type">ir.actions.act_window</field>
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
@ -42,11 +41,11 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
<field name="view_type">form</field>
|
<field name="view_type">form</field>
|
||||||
<field name="view_mode">tree,form</field>
|
<field name="view_mode">tree,form</field>
|
||||||
</record>
|
</record>
|
||||||
|
<menuitem
|
||||||
|
id="menu_module_authors"
|
||||||
<menuitem id="menu_module_authors"
|
parent="menu_module_analysis_settings"
|
||||||
parent="menu_module_analysis_settings" sequence="1"
|
sequence="1"
|
||||||
action="action_ir_module_author"
|
action="action_ir_module_author"
|
||||||
groups="base.group_no_one"/>
|
groups="base.group_no_one"
|
||||||
|
/>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
@ -1,55 +1,55 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<!--
|
<!--
|
||||||
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
|
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
|
||||||
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<odoo>
|
<odoo>
|
||||||
|
|
||||||
<record id="view_ir_module_module_form" model="ir.ui.view">
|
<record id="view_ir_module_module_form" model="ir.ui.view">
|
||||||
<field name="model">ir.module.module</field>
|
<field name="model">ir.module.module</field>
|
||||||
<field name="inherit_id" ref="base.module_form"/>
|
<field name="inherit_id" ref="base.module_form" />
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<xpath expr="//field[@name='state']/.." position="after">
|
<xpath expr="//field[@name='state']/.." position="after">
|
||||||
<button name="button_analyse_code" states="installed" string="Refresh Code Analysis" type="object" class="btn btn-primary"/>
|
<button
|
||||||
|
name="button_analyse_code"
|
||||||
|
states="installed"
|
||||||
|
string="Refresh Code Analysis"
|
||||||
|
type="object"
|
||||||
|
class="btn btn-primary"
|
||||||
|
/>
|
||||||
<group string="Code Size" col="4">
|
<group string="Code Size" col="4">
|
||||||
<field name="python_code_qty"/>
|
<field name="python_code_qty" />
|
||||||
<field name="xml_code_qty"/>
|
<field name="xml_code_qty" />
|
||||||
<field name="js_code_qty"/>
|
<field name="js_code_qty" />
|
||||||
<field name="css_code_qty"/>
|
<field name="css_code_qty" />
|
||||||
</group>
|
</group>
|
||||||
<group string="Type and Authors">
|
<group string="Type and Authors">
|
||||||
<field name="author_ids" widget="many2many_tags"/>
|
<field name="author_ids" widget="many2many_tags" />
|
||||||
<field name="module_type_id"/>
|
<field name="module_type_id" />
|
||||||
</group>
|
</group>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="view_ir_module_module_pivot" model="ir.ui.view">
|
<record id="view_ir_module_module_pivot" model="ir.ui.view">
|
||||||
<field name="model">ir.module.module</field>
|
<field name="model">ir.module.module</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<pivot>
|
<pivot>
|
||||||
<field name="module_type_id" type="row"/>
|
<field name="module_type_id" type="row" />
|
||||||
<field name="python_code_qty" type="measure"/>
|
<field name="python_code_qty" type="measure" />
|
||||||
<field name="xml_code_qty" type="measure"/>
|
<field name="xml_code_qty" type="measure" />
|
||||||
<field name="js_code_qty" type="measure"/>
|
<field name="js_code_qty" type="measure" />
|
||||||
<field name="css_code_qty" type="measure"/>
|
<field name="css_code_qty" type="measure" />
|
||||||
</pivot>
|
</pivot>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="view_ir_module_module_graph" model="ir.ui.view">
|
<record id="view_ir_module_module_graph" model="ir.ui.view">
|
||||||
<field name="model">ir.module.module</field>
|
<field name="model">ir.module.module</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<graph type="pie">
|
<graph type="pie">
|
||||||
<field name="module_type_id" type="row"/>
|
<field name="module_type_id" type="row" />
|
||||||
</graph>
|
</graph>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="action_ir_module_module_by_type" model="ir.actions.act_window">
|
<record id="action_ir_module_module_by_type" model="ir.actions.act_window">
|
||||||
<field name="name">Installed Modules by Types</field>
|
<field name="name">Installed Modules by Types</field>
|
||||||
<field name="type">ir.actions.act_window</field>
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
@ -58,10 +58,11 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
<field name="view_type">form</field>
|
<field name="view_type">form</field>
|
||||||
<field name="view_mode">pivot,graph</field>
|
<field name="view_mode">pivot,graph</field>
|
||||||
</record>
|
</record>
|
||||||
|
<menuitem
|
||||||
<menuitem id="menu_module_by_type"
|
id="menu_module_by_type"
|
||||||
parent="menu_module_analysis_reporting" sequence="5"
|
parent="menu_module_analysis_reporting"
|
||||||
|
sequence="5"
|
||||||
action="action_ir_module_module_by_type"
|
action="action_ir_module_module_by_type"
|
||||||
groups="base.group_no_one"/>
|
groups="base.group_no_one"
|
||||||
|
/>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
@ -1,20 +1,22 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<!--
|
<!--
|
||||||
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
|
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
|
||||||
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<odoo>
|
<odoo>
|
||||||
|
|
||||||
<record id="view_ir_module_type_form" model="ir.ui.view">
|
<record id="view_ir_module_type_form" model="ir.ui.view">
|
||||||
<field name="model">ir.module.type</field>
|
<field name="model">ir.module.type</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<form>
|
<form>
|
||||||
<sheet>
|
<sheet>
|
||||||
<div class="oe_title">
|
<div class="oe_title">
|
||||||
<div class="oe_edit_only"><label for="name"/></div>
|
<div class="oe_edit_only">
|
||||||
<h1><field name="name"/></h1>
|
<label for="name" />
|
||||||
|
</div>
|
||||||
|
<h1>
|
||||||
|
<field name="name" />
|
||||||
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
<group>
|
<group>
|
||||||
</group>
|
</group>
|
||||||
|
@ -22,17 +24,15 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
</form>
|
</form>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="view_ir_module_type_tree" model="ir.ui.view">
|
<record id="view_ir_module_type_tree" model="ir.ui.view">
|
||||||
<field name="model">ir.module.type</field>
|
<field name="model">ir.module.type</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree>
|
<tree>
|
||||||
<field name="name"/>
|
<field name="name" />
|
||||||
<field name="installed_module_qty"/>
|
<field name="installed_module_qty" />
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="action_ir_module_type" model="ir.actions.act_window">
|
<record id="action_ir_module_type" model="ir.actions.act_window">
|
||||||
<field name="name">Modules Types</field>
|
<field name="name">Modules Types</field>
|
||||||
<field name="type">ir.actions.act_window</field>
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
@ -40,11 +40,11 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
<field name="view_type">form</field>
|
<field name="view_type">form</field>
|
||||||
<field name="view_mode">tree,form</field>
|
<field name="view_mode">tree,form</field>
|
||||||
</record>
|
</record>
|
||||||
|
<menuitem
|
||||||
|
id="menu_module_types"
|
||||||
<menuitem id="menu_module_types"
|
parent="menu_module_analysis_settings"
|
||||||
parent="menu_module_analysis_settings" sequence="2"
|
sequence="2"
|
||||||
action="action_ir_module_type"
|
action="action_ir_module_type"
|
||||||
groups="base.group_no_one"/>
|
groups="base.group_no_one"
|
||||||
|
/>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
@ -1,23 +1,20 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
<!--
|
<!--
|
||||||
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
|
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
|
||||||
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<odoo>
|
<odoo>
|
||||||
|
|
||||||
<record id="view_ir_module_type_rule_tree" model="ir.ui.view">
|
<record id="view_ir_module_type_rule_tree" model="ir.ui.view">
|
||||||
<field name="model">ir.module.type.rule</field>
|
<field name="model">ir.module.type.rule</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree editable="bottom">
|
<tree editable="bottom">
|
||||||
<field name="sequence" widget="handle"/>
|
<field name="sequence" widget="handle" />
|
||||||
<field name="module_type_id"/>
|
<field name="module_type_id" />
|
||||||
<field name="module_domain"/>
|
<field name="module_domain" />
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="action_ir_module_type_rule" model="ir.actions.act_window">
|
<record id="action_ir_module_type_rule" model="ir.actions.act_window">
|
||||||
<field name="name">Modules Types Rules</field>
|
<field name="name">Modules Types Rules</field>
|
||||||
<field name="type">ir.actions.act_window</field>
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
@ -25,10 +22,11 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
<field name="view_type">form</field>
|
<field name="view_type">form</field>
|
||||||
<field name="view_mode">tree</field>
|
<field name="view_mode">tree</field>
|
||||||
</record>
|
</record>
|
||||||
|
<menuitem
|
||||||
<menuitem id="menu_module_type_rules"
|
id="menu_module_type_rules"
|
||||||
parent="menu_module_analysis_settings" sequence="3"
|
parent="menu_module_analysis_settings"
|
||||||
|
sequence="3"
|
||||||
action="action_ir_module_type_rule"
|
action="action_ir_module_type_rule"
|
||||||
groups="base.group_no_one"/>
|
groups="base.group_no_one"
|
||||||
|
/>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
Loading…
Reference in New Issue