diff --git a/module_analysis/README.rst b/module_analysis/README.rst new file mode 100644 index 000000000..33742b6c4 --- /dev/null +++ b/module_analysis/README.rst @@ -0,0 +1,129 @@ +=============== +Module Analysis +=============== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-legalsylvain%2Fserver--tools-lightgray.png?logo=github + :target: https://github.com/legalsylvain/server-tools/tree/12.0-ADD-module_analysis/module_analysis + :alt: legalsylvain/server-tools + +|badge1| |badge2| |badge3| + +This module allows you to know 'how much code' is running on your Odoo +instance, group by 'Type' (Odoo Core, OCA, other...) + +This module can be usefull in the following cases : + +* To analyse the size of your technical debt, regarding your Custom modules +* To know the ratio between Odoo / OCA and Custom modules +* To evaluate the amount to pay to odoo to upgrade your custom code, or the + induced workload + +.. image:: https://raw.githubusercontent.com/legalsylvain/server-tools/12.0-ADD-module_analysis/module_analysis/static/description/installed_modules_by_types.png + +For that purpose, it adds new concepts + +* ``ir.module.author``, based on the value ``author`` present in the manifest + file. + +.. image:: https://raw.githubusercontent.com/legalsylvain/server-tools/12.0-ADD-module_analysis/module_analysis/static/description/module_authors.png + +* ``ir.module.type``, populated by default with Odoo and OCA values. + +.. image:: https://raw.githubusercontent.com/legalsylvain/server-tools/12.0-ADD-module_analysis/module_analysis/static/description/module_types.png + +Each installed modules have extra data in the 'Technical Data' tab : + +.. image:: https://raw.githubusercontent.com/legalsylvain/server-tools/12.0-ADD-module_analysis/module_analysis/static/description/module_form.png + +**Table of contents** + +.. contents:: + :local: + +Installation +============ + +To use this module, you have to install the application ``cloc`` + +.. code-block:: shell + + sudo apt-get install cloc + +Configuration +============= + +* Go to Apps / Module Analysis / Modules Types Rules + +The Module types Rules are usefull to get the Type of a module, based on +it information. + +This module comes with default rules. + + .. image:: https://raw.githubusercontent.com/legalsylvain/server-tools/12.0-ADD-module_analysis/module_analysis/static/description/default_module_type_rules.png + + +You can add your custom rules to identify the modules your team have +developped for exemple, + + .. image:: https://raw.githubusercontent.com/legalsylvain/server-tools/12.0-ADD-module_analysis/module_analysis/static/description/add_module_type_rules.png + + +to update the data, you have to : + +* Go to 'Apps' / 'Update Apps List' + +* Check the box 'Analyse Installed modules' + + .. image:: https://raw.githubusercontent.com/legalsylvain/server-tools/12.0-ADD-module_analysis/module_analysis/static/description/base_module_update.png + +This will update analysis of your installed module. + +Usage +===== + +* Go to 'Apps' / 'Module Analysis' / 'Installed module by Types' + +Open the stats to analyze the detail of the code installed + + .. image:: https://raw.githubusercontent.com/legalsylvain/server-tools/12.0-ADD-module_analysis/module_analysis/static/description/default_module_type_rules.png + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* GRAP + +Contributors +~~~~~~~~~~~~ + +* Sylvain LE GAL (https://twitter.com/legalsylvain) + +Maintainers +~~~~~~~~~~~ + +This module is part of the `legalsylvain/server-tools `_ project on GitHub. + +You are welcome to contribute. diff --git a/module_analysis/__init__.py b/module_analysis/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/module_analysis/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/module_analysis/__manifest__.py b/module_analysis/__manifest__.py new file mode 100644 index 000000000..fe304b429 --- /dev/null +++ b/module_analysis/__manifest__.py @@ -0,0 +1,33 @@ +# Copyright (C) 2019-Today: GRAP () +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + 'name': "Module Analysis", + 'summary': "Add analysis tools regarding installed modules" + " to know which installed modules comes from Odoo Core, OCA, or are" + " custom modules", + 'author': 'GRAP, Odoo Community Association (OCA)', + 'website': "https://github.com/OCA/server-tools/", + 'version': '12.0.1.0.0', + 'license': 'AGPL-3', + 'depends': [ + '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_module_type.xml', + 'data/ir_module_type_rule.xml', + ], + 'external_dependencies': { + 'lib': ['cloc'], + }, + 'installable': True, +} diff --git a/module_analysis/data/ir_module_type.xml b/module_analysis/data/ir_module_type.xml new file mode 100644 index 000000000..3e5e7a40d --- /dev/null +++ b/module_analysis/data/ir_module_type.xml @@ -0,0 +1,18 @@ + + + + + + + Odoo Core + + + + OCA + + + diff --git a/module_analysis/data/ir_module_type_rule.xml b/module_analysis/data/ir_module_type_rule.xml new file mode 100644 index 000000000..7e95e8e79 --- /dev/null +++ b/module_analysis/data/ir_module_type_rule.xml @@ -0,0 +1,42 @@ + + + + + + + + 1 + [('author_ids', 'ilike', 'Odoo S.A')] + + + + + 2 + [('author_ids', 'ilike', 'OpenERP SA')] + + + + + 3 + [('author_ids', '=', 'Odoo SA')] + + + + + 4 + [('author_ids', '=', 'Odoo')] + + + + + + 100 + [('author_ids', '=', 'Odoo Community Association (OCA)')] + + + + diff --git a/module_analysis/models/__init__.py b/module_analysis/models/__init__.py new file mode 100644 index 000000000..d320c5b34 --- /dev/null +++ b/module_analysis/models/__init__.py @@ -0,0 +1,5 @@ +from . import base_module_update +from . import ir_module_author +from . import ir_module_module +from . import ir_module_type +from . import ir_module_type_rule diff --git a/module_analysis/models/base_module_update.py b/module_analysis/models/base_module_update.py new file mode 100644 index 000000000..b56d0e93f --- /dev/null +++ b/module_analysis/models/base_module_update.py @@ -0,0 +1,18 @@ +# Copyright (C) 2019-Today: GRAP () +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models + + +class BaseModuleUpdate(models.TransientModel): + _inherit = 'base.module.update' + + analyze_installed_modules = fields.Boolean( + string='Analyze Installed Modules', default=True) + + @api.multi + def update_module(self): + return super(BaseModuleUpdate, self.with_context( + analyze_installed_modules=self.analyze_installed_modules) + ).update_module() diff --git a/module_analysis/models/ir_module_author.py b/module_analysis/models/ir_module_author.py new file mode 100644 index 000000000..371ab77c2 --- /dev/null +++ b/module_analysis/models/ir_module_author.py @@ -0,0 +1,44 @@ +# Copyright (C) 2019-Today: GRAP () +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models + + +class IrModuleAuthor(models.Model): + _name = 'ir.module.author' + _description = 'Modules Authors' + + name = fields.Char(string='Name', required=True) + + module_ids = fields.Many2many( + string='Modules', comodel_name='ir.module.module') + + module_qty = fields.Integer( + string="Modules Quantity", + compute='_compute_modules', store=True) + + installed_module_qty = fields.Integer( + string="Installed Modules Quantity", + compute='_compute_modules', store=True) + + _sql_constraints = [ + ('name_uniq', 'unique(name)', + 'The name of the modules author should be unique per database!'), + ] + + @api.multi + @api.depends('module_ids') + def _compute_modules(self): + for author in self: + author.module_qty = len(author.module_ids) + author.installed_module_qty = len( + author.module_ids.filtered(lambda x: x.state == 'installed')) + + @api.model + def _get_or_create(self, name): + authors = self.search([('name', '=', name)]) + if authors: + return authors[0] + else: + return self.create({'name': name}) diff --git a/module_analysis/models/ir_module_module.py b/module_analysis/models/ir_module_module.py new file mode 100644 index 000000000..784aa3ffb --- /dev/null +++ b/module_analysis/models/ir_module_module.py @@ -0,0 +1,124 @@ +# Copyright (C) 2019-Today: GRAP () +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import json +import logging +import os +import subprocess + +from odoo import api, fields, models +from odoo.tools.safe_eval import safe_eval +from odoo.modules.module import get_module_path + +_logger = logging.getLogger(__name__) + + +class IrModuleModule(models.Model): + _inherit = 'ir.module.module' + + author_ids = fields.Many2many( + string='Authors', comodel_name='ir.module.author', readonly=True) + + module_type_id = fields.Many2one( + string='Module Type', comodel_name='ir.module.type', readonly=True) + + python_lines_qty = fields.Integer(string='Python Lines', readonly=True) + + xml_lines_qty = fields.Integer(string='XML Lines', readonly=True) + + js_lines_qty = fields.Integer(string='JS Lines', readonly=True) + + css_lines_qty = fields.Integer(string='CSS Lines', readonly=True) + + @api.model + def update_list(self): + res = super(IrModuleModule, self).update_list() + if self.env.context.get('analyze_installed_modules', False): + self.search([('state', '=', 'installed')]).button_analyze_code() + return res + + @api.multi + def write(self, vals): + res = super(IrModuleModule, self).write(vals) + if vals.get('state', False) == 'installed': + self.button_analyze_code() + elif vals.get('state', False) == 'uninstalled': + self.write({ + 'author_ids': [6, 0, []], + 'module_type_id': False, + 'python_lines_qty': False, + 'xml_lines_qty': 0, + 'js_lines_qty': 0, + 'css_lines_qty': 0, + }) + return res + + @api.multi + def button_analyze_code(self): + IrModuleAuthor = self.env['ir.module.author'] + IrModuleTypeRule = self.env['ir.module.type.rule'] + rules = IrModuleTypeRule.search([]) + + exclude_dir = "lib,description,tests,demo" + include_lang = self._get_analyzed_languages() + + for module in self: + _logger.info("Analyzing Code for module %s" % (module.name)) + + # Update Authors, based on manifest key + authors = [] + if module.author and module.author[0] == '[': + author_txt_list = safe_eval(module.author) + else: + author_txt_list = module.author.split(',') + + author_txt_list = [x.strip() for x in author_txt_list] + author_txt_list = [x for x in author_txt_list if x] + for author_txt in author_txt_list: + authors.append(IrModuleAuthor._get_or_create(author_txt)) + + author_ids = [x.id for x in authors] + module.author_ids = author_ids + + # Update Module Type, based on rules + module_type_id = rules._get_module_type_id_from_module(module) + module.module_type_id = module_type_id + + # Get Path of module folder and parse the code + path = get_module_path(module.name) + + try: + command = [ + 'cloc', + '--exclude-dir=%s' % (exclude_dir), + '--skip-uniqueness', + '--include-lang=%s' % (include_lang), + '--not-match-f="__openerp__.py|__manifest__.py"', + '--json', + os.path.join(path)] + temp = subprocess.Popen(command, stdout=subprocess.PIPE) + bytes_output = temp.communicate() + output = bytes_output[0].decode("utf-8").replace('\n', '') + json_res = json.loads(output) + values = self._prepare_values_from_json(json_res) + module.write(values) + + except Exception as e: + _logger.warning( + 'Failed to execute the cloc command on module %s', + module.name, e.message()) + + @api.model + def _get_analyzed_languages(self): + "Overload the function to add extra languages to analyze" + return "Python,XML,JavaScript,CSS" + + @api.model + def _prepare_values_from_json(self, json_value): + return { + 'python_lines_qty': json_value.get('Python', {}).get('code', 0), + 'xml_lines_qty': json_value.get('XML', {}).get('code', 0), + 'js_lines_qty': json_value.get('JavaScript', {}).get('code', 0), + 'css_lines_qty': json_value.get('CSS', {}).get('code', 0), + } diff --git a/module_analysis/models/ir_module_type.py b/module_analysis/models/ir_module_type.py new file mode 100644 index 000000000..b3b901039 --- /dev/null +++ b/module_analysis/models/ir_module_type.py @@ -0,0 +1,25 @@ +# Copyright (C) 2019-Today: GRAP () +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models + + +class IrModuleType(models.Model): + _name = 'ir.module.type' + _description = 'Modules Types' + + name = fields.Char(string='Name', required=True) + + module_ids = fields.One2many( + string='Modules', comodel_name='ir.module.module', + inverse_name='module_type_id') + + module_qty = fields.Integer( + string='Modules Quantity', compute='_compute_module_qty', store=True) + + @api.multi + @api.depends('module_ids.module_type_id') + def _compute_module_qty(self): + for module_type in self: + module_type.module_qty = len(module_type.module_ids) diff --git a/module_analysis/models/ir_module_type_rule.py b/module_analysis/models/ir_module_type_rule.py new file mode 100644 index 000000000..d74a341cb --- /dev/null +++ b/module_analysis/models/ir_module_type_rule.py @@ -0,0 +1,30 @@ +# Copyright (C) 2019-Today: GRAP () +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models +from odoo.tools.safe_eval import safe_eval + + +class IrModuleType(models.Model): + _name = 'ir.module.type.rule' + _description = 'Modules Types Rules' + _order = 'sequence' + + sequence = fields.Integer(string='Sequence') + + module_domain = fields.Char( + string='Module Domain', required=True, default='[]') + + module_type_id = fields.Many2one( + string='Module type', comodel_name='ir.module.type', required=True) + + @api.multi + def _get_module_type_id_from_module(self, module): + IrModuleModule = self.env['ir.module.module'] + for rule in self: + domain = safe_eval(rule.module_domain) + domain.append(('id', '=', module.id)) + if IrModuleModule.search(domain): + return rule.module_type_id.id + return False diff --git a/module_analysis/readme/CONFIGURE.rst b/module_analysis/readme/CONFIGURE.rst new file mode 100644 index 000000000..0e177c02b --- /dev/null +++ b/module_analysis/readme/CONFIGURE.rst @@ -0,0 +1,25 @@ +* Go to Apps / Module Analysis / Modules Types Rules + +The Module types Rules are usefull to get the Type of a module, based on +it information. + +This module comes with default rules. + + .. image:: ../static/description/default_module_type_rules.png + + +You can add your custom rules to identify the modules your team have +developped for exemple, + + .. image:: ../static/description/add_module_type_rules.png + + +to update the data, you have to : + +* Go to 'Apps' / 'Update Apps List' + +* Check the box 'Analyse Installed modules' + + .. image:: ../static/description/base_module_update.png + +This will update analysis of your installed module. diff --git a/module_analysis/readme/CONTRIBUTORS.rst b/module_analysis/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..ae6f43a86 --- /dev/null +++ b/module_analysis/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Sylvain LE GAL (https://twitter.com/legalsylvain) diff --git a/module_analysis/readme/DESCRIPTION.rst b/module_analysis/readme/DESCRIPTION.rst new file mode 100644 index 000000000..d25f10807 --- /dev/null +++ b/module_analysis/readme/DESCRIPTION.rst @@ -0,0 +1,26 @@ +This module allows you to know 'how much code' is running on your Odoo +instance, group by 'Type' (Odoo Core, OCA, other...) + +This module can be usefull in the following cases : + +* To analyse the size of your technical debt, regarding your Custom modules +* To know the ratio between Odoo / OCA and Custom modules +* To evaluate the amount to pay to odoo to upgrade your custom code, or the + induced workload + +.. image:: ../static/description/installed_modules_by_types.png + +For that purpose, it adds new concepts + +* ``ir.module.author``, based on the value ``author`` present in the manifest + file. + +.. image:: ../static/description/module_authors.png + +* ``ir.module.type``, populated by default with Odoo and OCA values. + +.. image:: ../static/description/module_types.png + +Each installed modules have extra data in the 'Technical Data' tab : + +.. image:: ../static/description/module_form.png diff --git a/module_analysis/readme/INSTALL.rst b/module_analysis/readme/INSTALL.rst new file mode 100644 index 000000000..019f9c023 --- /dev/null +++ b/module_analysis/readme/INSTALL.rst @@ -0,0 +1,5 @@ +To use this module, you have to install the application ``cloc`` + +.. code-block:: shell + + sudo apt-get install cloc diff --git a/module_analysis/readme/USAGE.rst b/module_analysis/readme/USAGE.rst new file mode 100644 index 000000000..1997f96a3 --- /dev/null +++ b/module_analysis/readme/USAGE.rst @@ -0,0 +1,5 @@ +* Go to 'Apps' / 'Module Analysis' / 'Installed module by Types' + +Open the stats to analyze the detail of the code installed + + .. image:: ../static/description/default_module_type_rules.png diff --git a/module_analysis/security/ir.model.access.csv b/module_analysis/security/ir.model.access.csv new file mode 100644 index 000000000..af10dbde2 --- /dev/null +++ b/module_analysis/security/ir.model.access.csv @@ -0,0 +1,4 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_ir_module_author_manager,ir_module_author_manager,model_ir_module_author,base.group_system,1,1,1,1 +access_ir_module_type_manager,ir_module_type_manager,model_ir_module_type,base.group_system,1,1,1,1 +access_ir_module_type_rule_manager,ir_module_type_rule_manager,model_ir_module_type_rule,base.group_system,1,1,1,1 diff --git a/module_analysis/static/description/add_module_type_rules.png b/module_analysis/static/description/add_module_type_rules.png new file mode 100644 index 000000000..b9c97074e Binary files /dev/null and b/module_analysis/static/description/add_module_type_rules.png differ diff --git a/module_analysis/static/description/analysis.png b/module_analysis/static/description/analysis.png new file mode 100644 index 000000000..376f008d2 Binary files /dev/null and b/module_analysis/static/description/analysis.png differ diff --git a/module_analysis/static/description/base_module_update.png b/module_analysis/static/description/base_module_update.png new file mode 100644 index 000000000..e4d7884e6 Binary files /dev/null and b/module_analysis/static/description/base_module_update.png differ diff --git a/module_analysis/static/description/default_module_type_rules.png b/module_analysis/static/description/default_module_type_rules.png new file mode 100644 index 000000000..d3ee1b477 Binary files /dev/null and b/module_analysis/static/description/default_module_type_rules.png differ diff --git a/module_analysis/static/description/icon.png b/module_analysis/static/description/icon.png new file mode 100644 index 000000000..3a0328b51 Binary files /dev/null and b/module_analysis/static/description/icon.png differ diff --git a/module_analysis/static/description/installed_modules_by_types.png b/module_analysis/static/description/installed_modules_by_types.png new file mode 100644 index 000000000..7e08867c0 Binary files /dev/null and b/module_analysis/static/description/installed_modules_by_types.png differ diff --git a/module_analysis/static/description/module_authors.png b/module_analysis/static/description/module_authors.png new file mode 100644 index 000000000..0f04a8c3c Binary files /dev/null and b/module_analysis/static/description/module_authors.png differ diff --git a/module_analysis/static/description/module_form.png b/module_analysis/static/description/module_form.png new file mode 100644 index 000000000..12cef616d Binary files /dev/null and b/module_analysis/static/description/module_form.png differ diff --git a/module_analysis/static/description/module_types.png b/module_analysis/static/description/module_types.png new file mode 100644 index 000000000..7df84ee0d Binary files /dev/null and b/module_analysis/static/description/module_types.png differ diff --git a/module_analysis/views/menu.xml b/module_analysis/views/menu.xml new file mode 100644 index 000000000..39be49e99 --- /dev/null +++ b/module_analysis/views/menu.xml @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/module_analysis/views/view_base_module_update.xml b/module_analysis/views/view_base_module_update.xml new file mode 100644 index 000000000..7e284343f --- /dev/null +++ b/module_analysis/views/view_base_module_update.xml @@ -0,0 +1,20 @@ + + + + + + + base.module.update + + + + + + + + + diff --git a/module_analysis/views/view_ir_module_author.xml b/module_analysis/views/view_ir_module_author.xml new file mode 100644 index 000000000..136a45827 --- /dev/null +++ b/module_analysis/views/view_ir_module_author.xml @@ -0,0 +1,54 @@ + + + + + + + ir.module.author + +
+ +
+
+

+
+ + + + +
+
+
+
+ + + + ir.module.author + + + + + + + + + + + Modules Authors + ir.actions.act_window + ir.module.author + form + tree,form + + + + + +
diff --git a/module_analysis/views/view_ir_module_module.xml b/module_analysis/views/view_ir_module_module.xml new file mode 100644 index 000000000..da27f887e --- /dev/null +++ b/module_analysis/views/view_ir_module_module.xml @@ -0,0 +1,58 @@ + + + + + + + ir.module.module + + + +