diff --git a/module_analysis/README.rst b/module_analysis/README.rst new file mode 100644 index 000000000..45f6a77b6 --- /dev/null +++ b/module_analysis/README.rst @@ -0,0 +1,187 @@ +=============== +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-OCA%2Fserver--tools-lightgray.png?logo=github + :target: https://github.com/OCA/server-tools/tree/15.0/module_analysis + :alt: OCA/server-tools +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-tools-15-0/server-tools-15-0-module_analysis + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/149/15.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +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/OCA/server-tools/15.0/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/OCA/server-tools/15.0/module_analysis/static/description/module_authors.png + +* ``ir.module.type``, populated by default with Odoo and OCA values. + +.. image:: https://raw.githubusercontent.com/OCA/server-tools/15.0/module_analysis/static/description/module_types.png + +Each installed modules have extra data in the 'Technical Data' tab : + +.. image:: https://raw.githubusercontent.com/OCA/server-tools/15.0/module_analysis/static/description/module_form.png + +**Table of contents** + +.. contents:: + :local: + +Installation +============ + +To use this module, you have to install the ``pygount`` python librairy. + +``pip install pygount`` + +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/OCA/server-tools/15.0/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/OCA/server-tools/15.0/module_analysis/static/description/add_module_type_rules.png + + +to update the data manually, you have to : + +* Go to 'Apps' / 'Update Apps List' + +* Check the box 'Analyse Installed modules' + + .. image:: https://raw.githubusercontent.com/OCA/server-tools/15.0/module_analysis/static/description/base_module_update.png + +This will update analysis of your installed modules. + +to update the data automatically, you have to : + +* Go to 'Settings' / 'Technical' / 'Scheduled Actions' + +* Configure the action 'Update Module Analysis'. (By default, the analysis will be done nightly) + + +Adding Extra data +~~~~~~~~~~~~~~~~~ + +If you want to analyse other data, (for exemple, having the number of HTML +files), create a custom modules and overload the module model : + +.. code-block:: python + + from odoo import api, fields, models + + class IrModuleModule(models.Model): + _inherit = 'ir.module.module' + + xml_documentation_qty = fields.Integer( + string='Quantity of Comments in XML Files') + + @api.model + def _get_analyse_settings(self): + res = super()._get_analyse_settings() + if not '.html' in res: + res['.html'] = {} + res['.html']['documentation'] 'xml_documentation_qty' + return res + +Exclude files and directories +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Two parameters are availaible in 'Settings' / 'Technical' / 'Parameters' +'System Parameters' : + + .. image:: https://raw.githubusercontent.com/OCA/server-tools/15.0/module_analysis/static/description/config_parameters.png + +The list of folders and filename will be exclude from the analysis. +You can change the default settings. + +Usage +===== + +* Go to 'Apps' / 'Module Analysis' / 'Installed module by Types' + +Open the stats to analyse the detail of the code installed + + .. image:: https://raw.githubusercontent.com/OCA/server-tools/15.0/module_analysis/static/description/analysis_pivot.png + + .. image:: https://raw.githubusercontent.com/OCA/server-tools/15.0/module_analysis/static/description/analysis_pie.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 maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +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. + +This module is part of the `OCA/server-tools `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/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..47d38af47 --- /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)", + "maintainers": ["legalsylvain"], + "website": "https://github.com/OCA/server-tools", + "version": "16.0.1.0.0", + "license": "AGPL-3", + "depends": ["base"], + "data": [ + "security/ir.model.access.csv", + "views/menu.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_cron.xml", + "data/ir_config_parameter.xml", + "data/ir_module_type.xml", + "data/ir_module_type_rule.xml", + "data/ir_cron.xml", + ], + "external_dependencies": { + "python": ["pygount"], + }, + "installable": True, +} diff --git a/module_analysis/data/ir_config_parameter.xml b/module_analysis/data/ir_config_parameter.xml new file mode 100644 index 000000000..a5ab19622 --- /dev/null +++ b/module_analysis/data/ir_config_parameter.xml @@ -0,0 +1,16 @@ + + + + + module_analysis.exclude_directories + lib,demo,test,tests,doc,description + + + module_analysis.exclude_files + __openerp__.py,__manifest__.py + + diff --git a/module_analysis/data/ir_cron.xml b/module_analysis/data/ir_cron.xml new file mode 100644 index 000000000..da0d7b552 --- /dev/null +++ b/module_analysis/data/ir_cron.xml @@ -0,0 +1,20 @@ + + + + + Update Module Analysis + + + code + model.cron_analyse_code() + 1 + days + + -1 + + + diff --git a/module_analysis/data/ir_module_type.xml b/module_analysis/data/ir_module_type.xml new file mode 100644 index 000000000..3bc2193c4 --- /dev/null +++ b/module_analysis/data/ir_module_type.xml @@ -0,0 +1,14 @@ + + + + + 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..a789bf183 --- /dev/null +++ b/module_analysis/data/ir_module_type_rule.xml @@ -0,0 +1,37 @@ + + + + + + 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/i18n/it.po b/module_analysis/i18n/it.po new file mode 100644 index 000000000..b29d0c2a9 --- /dev/null +++ b/module_analysis/i18n/it.po @@ -0,0 +1,222 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * module_analysis +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-03-07 19:33+0000\n" +"Last-Translator: Francesco Foresti \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.14.1\n" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_base_module_update__analyse_installed_modules +msgid "Analyse Installed Modules" +msgstr "Analizza Moduli Installati" + +#. module: module_analysis +#: model:ir.ui.menu,name:module_analysis.menu_module_analysis +msgid "Analysis" +msgstr "Analisi" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_module__author_ids +msgid "Authors" +msgstr "Autori" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_module__css_code_qty +msgid "CSS Code Quantity" +msgstr "Quantità Codice CSS" + +#. module: module_analysis +#: model_terms:ir.ui.view,arch_db:module_analysis.view_ir_module_module_form +msgid "Code Size" +msgstr "Dimensione Codice" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__create_uid +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__create_uid +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__create_uid +msgid "Created by" +msgstr "Creato da" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__create_date +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__create_date +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__create_date +msgid "Created on" +msgstr "Creato il" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__display_name +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__display_name +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__display_name +msgid "Display Name" +msgstr "Nome Visualizzato" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__id +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__id +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__id +msgid "ID" +msgstr "ID" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__installed_module_ids +msgid "Installed Modules" +msgstr "Moduli Installati" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__installed_module_qty +msgid "Installed Modules Quantity" +msgstr "Quantità Moduli Installati" + +#. module: module_analysis +#: model:ir.actions.act_window,name:module_analysis.action_ir_module_module_by_type +#: model:ir.ui.menu,name:module_analysis.menu_module_by_type +msgid "Installed Modules by Types" +msgstr "Moduli Installati per Tipo" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_module__js_code_qty +msgid "JS Code Quantity" +msgstr "Quantità Codice JS" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author____last_update +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type____last_update +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule____last_update +msgid "Last Modified on" +msgstr "Ultima Modifica il" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__write_uid +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__write_uid +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__write_uid +msgid "Last Updated by" +msgstr "Ultimo aggiornamento di" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__write_date +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__write_date +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__write_date +msgid "Last Updated on" +msgstr "Ultimo aggiornamento il" + +#. module: module_analysis +#: model:ir.model,name:module_analysis.model_ir_module_module +msgid "Module" +msgstr "Modulo" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__module_domain +msgid "Module Domain" +msgstr "Dominio Modulo" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_module__module_type_id +msgid "Module Type" +msgstr "Tipo Modulo" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__module_type_id +msgid "Module type" +msgstr "Tipo Modulo" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__installed_module_ids +msgid "Modules" +msgstr "Moduli" + +#. module: module_analysis +#: model:ir.actions.act_window,name:module_analysis.action_ir_module_author +#: model:ir.model,name:module_analysis.model_ir_module_author +#: model:ir.ui.menu,name:module_analysis.menu_module_authors +msgid "Modules Authors" +msgstr "Autori Moduli" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__installed_module_qty +msgid "Modules Quantity" +msgstr "Quantità Moduli" + +#. module: module_analysis +#: model:ir.actions.act_window,name:module_analysis.action_ir_module_type +#: model:ir.model,name:module_analysis.model_ir_module_type +#: model:ir.ui.menu,name:module_analysis.menu_module_types +msgid "Modules Types" +msgstr "Tipologia Moduli" + +#. module: module_analysis +#: model:ir.actions.act_window,name:module_analysis.action_ir_module_type_rule +#: model:ir.model,name:module_analysis.model_ir_module_type_rule +#: model:ir.ui.menu,name:module_analysis.menu_module_type_rules +msgid "Modules Types Rules" +msgstr "Regole Tipologie Moduli" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__name +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__name +msgid "Name" +msgstr "Nome" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_module__python_code_qty +msgid "Python Code Quantity" +msgstr "Quantità Codice Python" + +#. module: module_analysis +#: model_terms:ir.ui.view,arch_db:module_analysis.view_ir_module_module_form +msgid "Refresh Code Analysis" +msgstr "Aggiorna Analisi Codice" + +#. module: module_analysis +#: model:ir.ui.menu,name:module_analysis.menu_module_analysis_reporting +msgid "Reporting" +msgstr "Resoconti" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__sequence +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__sequence +msgid "Sequence" +msgstr "Sequenza" + +#. module: module_analysis +#: model:ir.ui.menu,name:module_analysis.menu_module_analysis_settings +msgid "Settings" +msgstr "Impostazioni" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__smart_search +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__smart_search +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__smart_search +msgid "Smart Search" +msgstr "" + +#. module: module_analysis +#: model:ir.model.constraint,message:module_analysis.constraint_ir_module_author_name_uniq +msgid "The name of the modules author should be unique per database!" +msgstr "Il nome dell'autore del modulo deve essere unico nel database!" + +#. module: module_analysis +#: model_terms:ir.ui.view,arch_db:module_analysis.view_ir_module_module_form +msgid "Type and Authors" +msgstr "Tipologia e Autori" + +#. module: module_analysis +#: model:ir.model,name:module_analysis.model_base_module_update +msgid "Update Module" +msgstr "Aggiorna Modulo" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_module__xml_code_qty +msgid "XML Code Quantity" +msgstr "Quantità Codice XML" diff --git a/module_analysis/i18n/module_analysis.pot b/module_analysis/i18n/module_analysis.pot new file mode 100644 index 000000000..4797adb96 --- /dev/null +++ b/module_analysis/i18n/module_analysis.pot @@ -0,0 +1,219 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * module_analysis +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_base_module_update__analyse_installed_modules +msgid "Analyse Installed Modules" +msgstr "" + +#. module: module_analysis +#: model:ir.ui.menu,name:module_analysis.menu_module_analysis +msgid "Analysis" +msgstr "" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_module__author_ids +msgid "Authors" +msgstr "" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_module__css_code_qty +msgid "CSS Code Quantity" +msgstr "" + +#. module: module_analysis +#: model_terms:ir.ui.view,arch_db:module_analysis.view_ir_module_module_form +msgid "Code Size" +msgstr "" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__create_uid +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__create_uid +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__create_uid +msgid "Created by" +msgstr "" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__create_date +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__create_date +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__create_date +msgid "Created on" +msgstr "" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__display_name +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__display_name +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__display_name +msgid "Display Name" +msgstr "" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__id +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__id +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__id +msgid "ID" +msgstr "" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__installed_module_ids +msgid "Installed Modules" +msgstr "" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__installed_module_qty +msgid "Installed Modules Quantity" +msgstr "" + +#. module: module_analysis +#: model:ir.actions.act_window,name:module_analysis.action_ir_module_module_by_type +#: model:ir.ui.menu,name:module_analysis.menu_module_by_type +msgid "Installed Modules by Types" +msgstr "" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_module__js_code_qty +msgid "JS Code Quantity" +msgstr "" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author____last_update +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type____last_update +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule____last_update +msgid "Last Modified on" +msgstr "" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__write_uid +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__write_uid +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__write_date +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__write_date +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__write_date +msgid "Last Updated on" +msgstr "" + +#. module: module_analysis +#: model:ir.model,name:module_analysis.model_ir_module_module +msgid "Module" +msgstr "" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__module_domain +msgid "Module Domain" +msgstr "" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_module__module_type_id +msgid "Module Type" +msgstr "" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__module_type_id +msgid "Module type" +msgstr "" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__installed_module_ids +msgid "Modules" +msgstr "" + +#. module: module_analysis +#: model:ir.actions.act_window,name:module_analysis.action_ir_module_author +#: model:ir.model,name:module_analysis.model_ir_module_author +#: model:ir.ui.menu,name:module_analysis.menu_module_authors +msgid "Modules Authors" +msgstr "" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__installed_module_qty +msgid "Modules Quantity" +msgstr "" + +#. module: module_analysis +#: model:ir.actions.act_window,name:module_analysis.action_ir_module_type +#: model:ir.model,name:module_analysis.model_ir_module_type +#: model:ir.ui.menu,name:module_analysis.menu_module_types +msgid "Modules Types" +msgstr "" + +#. module: module_analysis +#: model:ir.actions.act_window,name:module_analysis.action_ir_module_type_rule +#: model:ir.model,name:module_analysis.model_ir_module_type_rule +#: model:ir.ui.menu,name:module_analysis.menu_module_type_rules +msgid "Modules Types Rules" +msgstr "" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__name +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__name +msgid "Name" +msgstr "" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_module__python_code_qty +msgid "Python Code Quantity" +msgstr "" + +#. module: module_analysis +#: model_terms:ir.ui.view,arch_db:module_analysis.view_ir_module_module_form +msgid "Refresh Code Analysis" +msgstr "" + +#. module: module_analysis +#: model:ir.ui.menu,name:module_analysis.menu_module_analysis_reporting +msgid "Reporting" +msgstr "" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__sequence +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__sequence +msgid "Sequence" +msgstr "" + +#. module: module_analysis +#: model:ir.ui.menu,name:module_analysis.menu_module_analysis_settings +msgid "Settings" +msgstr "" + +#. module: module_analysis +#: model:ir.model.constraint,message:module_analysis.constraint_ir_module_author_name_uniq +msgid "The name of the modules author should be unique per database!" +msgstr "" + +#. module: module_analysis +#: model_terms:ir.ui.view,arch_db:module_analysis.view_ir_module_module_form +msgid "Type and Authors" +msgstr "" + +#. module: module_analysis +#: model:ir.model,name:module_analysis.model_base_module_update +msgid "Update Module" +msgstr "" + +#. module: module_analysis +#: model:ir.actions.server,name:module_analysis.cron_module_analysis_ir_actions_server +#: model:ir.cron,cron_name:module_analysis.cron_module_analysis +#: model:ir.cron,name:module_analysis.cron_module_analysis +msgid "Update Module Analysis" +msgstr "" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_module__xml_code_qty +msgid "XML Code Quantity" +msgstr "" diff --git a/module_analysis/i18n/zh_CN.po b/module_analysis/i18n/zh_CN.po new file mode 100644 index 000000000..103a42d22 --- /dev/null +++ b/module_analysis/i18n/zh_CN.po @@ -0,0 +1,222 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * module_analysis +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2019-08-31 06:51+0000\n" +"Last-Translator: 黎伟杰 <674416404@qq.com>\n" +"Language-Team: none\n" +"Language: zh_CN\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 3.8\n" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_base_module_update__analyse_installed_modules +msgid "Analyse Installed Modules" +msgstr "分析已安装的模块" + +#. module: module_analysis +#: model:ir.ui.menu,name:module_analysis.menu_module_analysis +msgid "Analysis" +msgstr "分析" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_module__author_ids +msgid "Authors" +msgstr "作者" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_module__css_code_qty +msgid "CSS Code Quantity" +msgstr "CSS代码数量" + +#. module: module_analysis +#: model_terms:ir.ui.view,arch_db:module_analysis.view_ir_module_module_form +msgid "Code Size" +msgstr "代码大小" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__create_uid +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__create_uid +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__create_uid +msgid "Created by" +msgstr "创建者" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__create_date +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__create_date +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__create_date +msgid "Created on" +msgstr "创建时间" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__display_name +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__display_name +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__display_name +msgid "Display Name" +msgstr "显示名称" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__id +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__id +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__id +msgid "ID" +msgstr "ID" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__installed_module_ids +msgid "Installed Modules" +msgstr "已安装的模块" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__installed_module_qty +msgid "Installed Modules Quantity" +msgstr "已安装的模块数量" + +#. module: module_analysis +#: model:ir.actions.act_window,name:module_analysis.action_ir_module_module_by_type +#: model:ir.ui.menu,name:module_analysis.menu_module_by_type +msgid "Installed Modules by Types" +msgstr "按类型安装的模块" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_module__js_code_qty +msgid "JS Code Quantity" +msgstr "JS代码数量" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author____last_update +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type____last_update +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule____last_update +msgid "Last Modified on" +msgstr "最后修改时间" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__write_uid +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__write_uid +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__write_uid +msgid "Last Updated by" +msgstr "最后更新者" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__write_date +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__write_date +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__write_date +msgid "Last Updated on" +msgstr "最后更新时间" + +#. module: module_analysis +#: model:ir.model,name:module_analysis.model_ir_module_module +msgid "Module" +msgstr "模块" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__module_domain +msgid "Module Domain" +msgstr "模块域" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_module__module_type_id +msgid "Module Type" +msgstr "模块类型" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__module_type_id +msgid "Module type" +msgstr "模块类型" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__installed_module_ids +msgid "Modules" +msgstr "模块" + +#. module: module_analysis +#: model:ir.actions.act_window,name:module_analysis.action_ir_module_author +#: model:ir.model,name:module_analysis.model_ir_module_author +#: model:ir.ui.menu,name:module_analysis.menu_module_authors +msgid "Modules Authors" +msgstr "模块作者" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__installed_module_qty +msgid "Modules Quantity" +msgstr "模块数量" + +#. module: module_analysis +#: model:ir.actions.act_window,name:module_analysis.action_ir_module_type +#: model:ir.model,name:module_analysis.model_ir_module_type +#: model:ir.ui.menu,name:module_analysis.menu_module_types +msgid "Modules Types" +msgstr "模块类型" + +#. module: module_analysis +#: model:ir.actions.act_window,name:module_analysis.action_ir_module_type_rule +#: model:ir.model,name:module_analysis.model_ir_module_type_rule +#: model:ir.ui.menu,name:module_analysis.menu_module_type_rules +msgid "Modules Types Rules" +msgstr "模块类型规则" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__name +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__name +msgid "Name" +msgstr "名称" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_module__python_code_qty +msgid "Python Code Quantity" +msgstr "Python代码数量" + +#. module: module_analysis +#: model_terms:ir.ui.view,arch_db:module_analysis.view_ir_module_module_form +msgid "Refresh Code Analysis" +msgstr "刷新代码分析" + +#. module: module_analysis +#: model:ir.ui.menu,name:module_analysis.menu_module_analysis_reporting +msgid "Reporting" +msgstr "报告" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__sequence +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__sequence +msgid "Sequence" +msgstr "序列" + +#. module: module_analysis +#: model:ir.ui.menu,name:module_analysis.menu_module_analysis_settings +msgid "Settings" +msgstr "设置" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_author__smart_search +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type__smart_search +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_type_rule__smart_search +msgid "Smart Search" +msgstr "" + +#. module: module_analysis +#: model:ir.model.constraint,message:module_analysis.constraint_ir_module_author_name_uniq +msgid "The name of the modules author should be unique per database!" +msgstr "模块作者的名称应该是每个数据库唯一的!" + +#. module: module_analysis +#: model_terms:ir.ui.view,arch_db:module_analysis.view_ir_module_module_form +msgid "Type and Authors" +msgstr "类型和作者" + +#. module: module_analysis +#: model:ir.model,name:module_analysis.model_base_module_update +msgid "Update Module" +msgstr "更新模块" + +#. module: module_analysis +#: model:ir.model.fields,field_description:module_analysis.field_ir_module_module__xml_code_qty +msgid "XML Code Quantity" +msgstr "XML代码数量" diff --git a/module_analysis/models/__init__.py b/module_analysis/models/__init__.py new file mode 100644 index 000000000..53b968f5a --- /dev/null +++ b/module_analysis/models/__init__.py @@ -0,0 +1,4 @@ +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/ir_module_author.py b/module_analysis/models/ir_module_author.py new file mode 100644 index 000000000..0b1e51fd6 --- /dev/null +++ b/module_analysis/models/ir_module_author.py @@ -0,0 +1,45 @@ +# 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(required=True) + + installed_module_ids = fields.Many2many( + string="Modules", + comodel_name="ir.module.module", + relation="ir_module_module_author_rel", + ) + + installed_module_qty = fields.Integer( + string="Installed Modules Quantity", + compute="_compute_installed_module_qty", + store=True, + ) + + _sql_constraints = [ + ( + "name_uniq", + "unique(name)", + "The name of the modules author should be unique per database!", + ), + ] + + @api.depends("installed_module_ids") + def _compute_installed_module_qty(self): + for author in self: + author.installed_module_qty = len(author.installed_module_ids) + + @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..8e18d28aa --- /dev/null +++ b/module_analysis/models/ir_module_module.py @@ -0,0 +1,180 @@ +# 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 logging +import os +from pathlib import Path + +from pygount import SourceAnalysis + +from odoo import api, fields, models +from odoo.modules.module import get_module_path +from odoo.tools.safe_eval import safe_eval + +_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, + relation="ir_module_module_author_rel", + ) + + module_type_id = fields.Many2one( + string="Module Type", comodel_name="ir.module.type", readonly=True + ) + + python_code_qty = fields.Integer(string="Python Code Quantity", readonly=True) + + xml_code_qty = fields.Integer(string="XML Code Quantity", readonly=True) + + js_code_qty = fields.Integer(string="JS Code Quantity", readonly=True) + + css_code_qty = fields.Integer(string="CSS Code Quantity", readonly=True) + scss_code_qty = fields.Integer(string="SCSS Code Quantity", readonly=True) + + # Overloadable Section + @api.model + def _get_analyse_settings(self): + """Return a dictionnary of data analysed + Overload this function if you want to analyse other data + { + 'extension': { + 'data_to_analyse': 'field_name', + }, + }, [...] + extension: extension of the file, with the '.' + data_to_analyse : possible value : code, documentation, empty, string + field_name: Odoo field name to store the analysis + """ + return { + ".py": {"code": "python_code_qty"}, + ".xml": {"code": "xml_code_qty"}, + ".js": {"code": "js_code_qty"}, + ".css": {"code": "css_code_qty"}, + ".scss": {"code": "scss_code_qty"}, + } + + @api.model + def _get_clean_analyse_values(self): + """List of fields to unset when a module is uninstalled""" + return { + "author_ids": [(6, 0, [])], + "module_type_id": False, + "python_code_qty": False, + "xml_code_qty": 0, + "js_code_qty": 0, + "css_code_qty": 0, + "scss_code_qty": 0, + } + + @api.model + def _get_module_encoding(self, file_ext): + return "utf-8" + + def write(self, vals): + res = super().write(vals) + if vals.get("state", False) == "uninstalled" and "module_analysis" not in [ + x.name for x in self + ]: + self.write(self._get_clean_analyse_values()) + return res + + # Public Section + def button_analyse_code(self): + self._analyse_code() + + @api.model + def cron_analyse_code(self, domain=None): + if domain is None: + domain = [("state", "=", "installed")] + self.search(domain)._analyse_code() + + # Custom Section + def _analyse_code(self): + IrModuleAuthor = self.env["ir.module.author"] + IrModuleTypeRule = self.env["ir.module.type.rule"] + rules = IrModuleTypeRule.search([]) + + cfg = self.env["ir.config_parameter"] + val = cfg.get_param("module_analysis.exclude_directories", "") + exclude_directories = [x.strip() for x in val.split(",") if x.strip()] + val = cfg.get_param("module_analysis.exclude_files", "") + exclude_files = [x.strip() for x in val.split(",") if x.strip()] + + for module in self: + _logger.info("Analysing 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 and module.author.split(",")) or [] + + 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 + module_path = get_module_path(module.name) + + # Get Files + analysed_datas = self._get_analyse_data_dict() + file_extensions = analysed_datas.keys() + file_list = self._get_files_to_analyse( + module_path, file_extensions, exclude_directories, exclude_files + ) + + for file_path, file_ext in file_list: + file_res = SourceAnalysis.from_file( + file_path, "", encoding=self._get_module_encoding(file_ext) + ) + for k, v in analysed_datas.get(file_ext).items(): + v["value"] += getattr(file_res, k) + + # Update the module with the datas + values = {} + for analyses in analysed_datas.values(): + for v in analyses.values(): + values[v["field"]] = v["value"] + module.write(values) + + @api.model + def _get_files_to_analyse( + self, path, file_extensions, exclude_directories, exclude_files + ): + res = [] + if not path: + return res + for root, _, files in os.walk(path, followlinks=True): + if set(Path(root).parts) & set(exclude_directories): + continue + for name in files: + if name in exclude_files: + continue + filename, file_extension = os.path.splitext(name) + if file_extension in file_extensions: + res.append((os.path.join(root, name), file_extension)) + return res + + @api.model + def _get_analyse_data_dict(self): + res_dict = self._get_analyse_settings().copy() + for analyse_dict in res_dict.values(): + for analyse_type, v in analyse_dict.items(): + analyse_dict[analyse_type] = {"field": v, "value": 0} + return res_dict diff --git a/module_analysis/models/ir_module_type.py b/module_analysis/models/ir_module_type.py new file mode 100644 index 000000000..de804d0b3 --- /dev/null +++ b/module_analysis/models/ir_module_type.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 + + +class IrModuleType(models.Model): + _name = "ir.module.type" + _description = "Modules Types" + _order = "sequence" + + name = fields.Char(required=True) + + sequence = fields.Integer() + + installed_module_ids = fields.One2many( + string="Installed Modules", + comodel_name="ir.module.module", + inverse_name="module_type_id", + ) + + installed_module_qty = fields.Integer( + string="Modules Quantity", compute="_compute_installed_module_qty", store=True + ) + + @api.depends("installed_module_ids.module_type_id") + def _compute_installed_module_qty(self): + for module_type in self: + module_type.installed_module_qty = len(module_type.installed_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..3ad721217 --- /dev/null +++ b/module_analysis/models/ir_module_type_rule.py @@ -0,0 +1,29 @@ +# 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 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() + + module_domain = fields.Char(required=True, default="[]") + + module_type_id = fields.Many2one( + string="Module type", comodel_name="ir.module.type", required=True + ) + + 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..a9e291d2b --- /dev/null +++ b/module_analysis/readme/CONFIGURE.rst @@ -0,0 +1,65 @@ +* 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 manually, you have to : + +* Go to 'Settings' / 'Technical' / 'Scheduled Actions' + +* Run manually the action : 'Update Module Analysis' + +This will update analysis of your installed modules. + +to update the data automatically, you have to : + +* Go to 'Settings' / 'Technical' / 'Scheduled Actions' + +* Configure the action 'Update Module Analysis' and activate it. (By default, the cron is unactive and no analysis is done) + + +Adding Extra data +~~~~~~~~~~~~~~~~~ + +If you want to analyse other data, (for exemple, having the number of HTML +files), create a custom modules and overload the module model : + +.. code-block:: python + + from odoo import api, fields, models + + class IrModuleModule(models.Model): + _inherit = 'ir.module.module' + + xml_documentation_qty = fields.Integer( + string='Quantity of Comments in XML Files') + + @api.model + def _get_analyse_settings(self): + res = super()._get_analyse_settings() + if not '.html' in res: + res['.html'] = {} + res['.html']['documentation'] 'xml_documentation_qty' + return res + +Exclude files and directories +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Two parameters are availaible in 'Settings' / 'Technical' / 'Parameters' +'System Parameters' : + + .. image:: ../static/description/config_parameters.png + +The list of folders and filename will be exclude from the analysis. +You can change the default settings. 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..15ecd6f37 --- /dev/null +++ b/module_analysis/readme/INSTALL.rst @@ -0,0 +1,3 @@ +To use this module, you have to install the ``pygount`` python librairy. + +``pip install pygount`` diff --git a/module_analysis/readme/USAGE.rst b/module_analysis/readme/USAGE.rst new file mode 100644 index 000000000..eea896d0f --- /dev/null +++ b/module_analysis/readme/USAGE.rst @@ -0,0 +1,7 @@ +* Go to 'Apps' / 'Module Analysis' / 'Installed module by Types' + +Open the stats to analyse the detail of the code installed + + .. image:: ../static/description/analysis_pivot.png + + .. image:: ../static/description/analysis_pie.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_pie.png b/module_analysis/static/description/analysis_pie.png new file mode 100644 index 000000000..1ed7f867d Binary files /dev/null and b/module_analysis/static/description/analysis_pie.png differ diff --git a/module_analysis/static/description/analysis_pivot.png b/module_analysis/static/description/analysis_pivot.png new file mode 100644 index 000000000..376f008d2 Binary files /dev/null and b/module_analysis/static/description/analysis_pivot.png differ diff --git a/module_analysis/static/description/config_parameters.png b/module_analysis/static/description/config_parameters.png new file mode 100644 index 000000000..b11538a8a Binary files /dev/null and b/module_analysis/static/description/config_parameters.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/index.html b/module_analysis/static/description/index.html new file mode 100644 index 000000000..a54431bdd --- /dev/null +++ b/module_analysis/static/description/index.html @@ -0,0 +1,528 @@ + + + + + + +Module Analysis + + + +
+

Module Analysis

+ + +

Beta License: AGPL-3 OCA/server-tools Translate me on Weblate Try me on Runbot

+

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
  • +
+https://raw.githubusercontent.com/OCA/server-tools/15.0/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.
  • +
+https://raw.githubusercontent.com/OCA/server-tools/15.0/module_analysis/static/description/module_authors.png +
    +
  • ir.module.type, populated by default with Odoo and OCA values.
  • +
+https://raw.githubusercontent.com/OCA/server-tools/15.0/module_analysis/static/description/module_types.png +

Each installed modules have extra data in the ‘Technical Data’ tab :

+https://raw.githubusercontent.com/OCA/server-tools/15.0/module_analysis/static/description/module_form.png +

Table of contents

+ +
+

Installation

+

To use this module, you have to install the pygount python librairy.

+

pip install pygount

+
+
+

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.

+
+https://raw.githubusercontent.com/OCA/server-tools/15.0/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,

+
+https://raw.githubusercontent.com/OCA/server-tools/15.0/module_analysis/static/description/add_module_type_rules.png +
+

to update the data manually, you have to :

+
    +
  • Go to ‘Apps’ / ‘Update Apps List’

    +
  • +
  • Check the box ‘Analyse Installed modules’

    +
    +https://raw.githubusercontent.com/OCA/server-tools/15.0/module_analysis/static/description/base_module_update.png +
    +
  • +
+

This will update analysis of your installed modules.

+

to update the data automatically, you have to :

+
    +
  • Go to ‘Settings’ / ‘Technical’ / ‘Scheduled Actions’
  • +
  • Configure the action ‘Update Module Analysis’. (By default, the analysis will be done nightly)
  • +
+
+

Adding Extra data

+

If you want to analyse other data, (for exemple, having the number of HTML +files), create a custom modules and overload the module model :

+
+from odoo import api, fields, models
+
+class IrModuleModule(models.Model):
+   _inherit = 'ir.module.module'
+
+   xml_documentation_qty = fields.Integer(
+      string='Quantity of Comments in XML Files')
+
+  @api.model
+  def _get_analyse_settings(self):
+      res = super()._get_analyse_settings()
+      if not '.html' in res:
+          res['.html'] = {}
+      res['.html']['documentation'] 'xml_documentation_qty'
+      return res
+
+
+
+

Exclude files and directories

+

Two parameters are availaible in ‘Settings’ / ‘Technical’ / ‘Parameters’ +‘System Parameters’ :

+
+https://raw.githubusercontent.com/OCA/server-tools/15.0/module_analysis/static/description/config_parameters.png +
+

The list of folders and filename will be exclude from the analysis. +You can change the default settings.

+
+
+
+

Usage

+
    +
  • Go to ‘Apps’ / ‘Module Analysis’ / ‘Installed module by Types’
  • +
+

Open the stats to analyse the detail of the code installed

+
+https://raw.githubusercontent.com/OCA/server-tools/15.0/module_analysis/static/description/analysis_pivot.png +https://raw.githubusercontent.com/OCA/server-tools/15.0/module_analysis/static/description/analysis_pie.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
  • +
+
+ +
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

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.

+

This module is part of the OCA/server-tools project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + 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/tests/__init__.py b/module_analysis/tests/__init__.py new file mode 100644 index 000000000..d9b96c4fa --- /dev/null +++ b/module_analysis/tests/__init__.py @@ -0,0 +1 @@ +from . import test_module diff --git a/module_analysis/tests/test_module.py b/module_analysis/tests/test_module.py new file mode 100644 index 000000000..20f96fc3f --- /dev/null +++ b/module_analysis/tests/test_module.py @@ -0,0 +1,34 @@ +# 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.tests.common import TransactionCase + + +class TestModule(TransactionCase): + def setUp(self): + super().setUp() + self.IrModuleModule = self.env["ir.module.module"] + + def test_installed_modules(self): + self.IrModuleModule.cron_analyse_code() + installed_modules = self.IrModuleModule.search( + [("state", "=", "installed"), ("name", "not like", "_test")] + ) + for module in installed_modules: + self.assertTrue( + module.python_code_qty > 0 + or module.xml_code_qty > 0 + or module.js_code_qty > 0, + "module '%s' doesn't have code analysed defined, whereas it is" + " installed." % (module.name), + ) + + def test_uninstalled_modules(self): + uninstalled_modules = self.IrModuleModule.search([("state", "!=", "installed")]) + for module in uninstalled_modules: + self.assertTrue( + module.python_code_qty == 0, + "module '%s' has python lines defined, whereas it is" + " not installed." % (module.name), + ) diff --git a/module_analysis/views/menu.xml b/module_analysis/views/menu.xml new file mode 100644 index 000000000..97c906349 --- /dev/null +++ b/module_analysis/views/menu.xml @@ -0,0 +1,27 @@ + + + + + + + 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..1322c9f7c --- /dev/null +++ b/module_analysis/views/view_ir_module_author.xml @@ -0,0 +1,50 @@ + + + + + ir.module.author + +
+ +
+
+
+

+ +

+
+ + + +
+
+
+
+ + ir.module.author + + + + + + + + + Modules Authors + ir.actions.act_window + ir.module.author + 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..a93fb617e --- /dev/null +++ b/module_analysis/views/view_ir_module_module.xml @@ -0,0 +1,69 @@ + + + + + ir.module.module + + + +