Browse Source

Merge PR #1618 into 12.0

Signed-off-by legalsylvain
12.0
OCA-git-bot 5 years ago
parent
commit
c47a864793
  1. 182
      module_analysis/README.rst
  2. 2
      module_analysis/__init__.py
  3. 34
      module_analysis/__manifest__.py
  4. 20
      module_analysis/data/ir_config_parameter.xml
  5. 18
      module_analysis/data/ir_module_type.xml
  6. 42
      module_analysis/data/ir_module_type_rule.xml
  7. 213
      module_analysis/i18n/module_analysis.pot
  8. 5
      module_analysis/models/__init__.py
  9. 18
      module_analysis/models/base_module_update.py
  10. 39
      module_analysis/models/ir_module_author.py
  11. 186
      module_analysis/models/ir_module_module.py
  12. 30
      module_analysis/models/ir_module_type.py
  13. 30
      module_analysis/models/ir_module_type_rule.py
  14. 13
      module_analysis/post_init_hook.py
  15. 61
      module_analysis/readme/CONFIGURE.rst
  16. 1
      module_analysis/readme/CONTRIBUTORS.rst
  17. 26
      module_analysis/readme/DESCRIPTION.rst
  18. 3
      module_analysis/readme/INSTALL.rst
  19. 8
      module_analysis/readme/USAGE.rst
  20. 4
      module_analysis/security/ir.model.access.csv
  21. BIN
      module_analysis/static/description/add_module_type_rules.png
  22. BIN
      module_analysis/static/description/analysis_pie.png
  23. BIN
      module_analysis/static/description/analysis_pivot.png
  24. BIN
      module_analysis/static/description/base_module_update.png
  25. BIN
      module_analysis/static/description/config_parameters.png
  26. BIN
      module_analysis/static/description/default_module_type_rules.png
  27. BIN
      module_analysis/static/description/icon.png
  28. 523
      module_analysis/static/description/index.html
  29. BIN
      module_analysis/static/description/installed_modules_by_types.png
  30. BIN
      module_analysis/static/description/module_authors.png
  31. BIN
      module_analysis/static/description/module_form.png
  32. BIN
      module_analysis/static/description/module_types.png
  33. 1
      module_analysis/tests/__init__.py
  34. 33
      module_analysis/tests/test_module.py
  35. 24
      module_analysis/views/menu.xml
  36. 20
      module_analysis/views/view_base_module_update.xml
  37. 52
      module_analysis/views/view_ir_module_author.xml
  38. 67
      module_analysis/views/view_ir_module_module.xml
  39. 50
      module_analysis/views/view_ir_module_type.xml
  40. 34
      module_analysis/views/view_ir_module_type_rule.xml
  41. 1
      requirements.txt
  42. 2
      setup/_metapackage/VERSION.txt
  43. 1
      setup/_metapackage/setup.py
  44. 1
      setup/module_analysis/odoo/addons/module_analysis
  45. 6
      setup/module_analysis/setup.py

182
module_analysis/README.rst

@ -0,0 +1,182 @@
===============
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/12.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-12-0/server-tools-12-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/12.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/12.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/12.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/12.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/12.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/12.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/12.0/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/OCA/server-tools/12.0/module_analysis/static/description/base_module_update.png
This will update analysis of your installed modules.
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/12.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/12.0/module_analysis/static/description/analysis_pivot.png
.. image:: https://raw.githubusercontent.com/OCA/server-tools/12.0/module_analysis/static/description/analysis_pie.png
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-tools/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 <https://github.com/OCA/server-tools/issues/new?body=module:%20module_analysis%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
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 <https://github.com/OCA/server-tools/tree/12.0/module_analysis>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

2
module_analysis/__init__.py

@ -0,0 +1,2 @@
from . import models
from . post_init_hook import analyse_installed_modules

34
module_analysis/__manifest__.py

@ -0,0 +1,34 @@
# Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
# @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_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,
}

20
module_analysis/data/ir_config_parameter.xml

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<odoo noupdate="1">
<record id="parameter_exclude_directories" model="ir.config_parameter">
<field name="key">module_analysis.exclude_directories</field>
<field name="value">lib,demo,test,tests,doc,description</field>
</record>
<record id="parameter_exclude_files" model="ir.config_parameter">
<field name="key">module_analysis.exclude_files</field>
<field name="value">__openerp__.py,__manifest__.py</field>
</record>
</odoo>

18
module_analysis/data/ir_module_type.xml

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<odoo>
<record id="module_type_odoo" model="ir.module.type">
<field name="name">Odoo Core</field>
</record>
<record id="module_type_oca" model="ir.module.type">
<field name="name">OCA</field>
</record>
</odoo>

42
module_analysis/data/ir_module_type_rule.xml

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<odoo>
<!-- Odoo Rules -->
<record id="module_type_rule_odoo_a" model="ir.module.type.rule">
<field name="sequence">1</field>
<field name="module_domain">[('author_ids', 'ilike', 'Odoo S.A')]</field>
<field name="module_type_id" ref="module_type_odoo"/>
</record>
<record id="module_type_rule_odoo_b" model="ir.module.type.rule">
<field name="sequence">2</field>
<field name="module_domain">[('author_ids', 'ilike', 'OpenERP SA')]</field>
<field name="module_type_id" ref="module_type_odoo"/>
</record>
<record id="module_type_rule_odoo_c" model="ir.module.type.rule">
<field name="sequence">3</field>
<field name="module_domain">[('author_ids', '=', 'Odoo SA')]</field>
<field name="module_type_id" ref="module_type_odoo"/>
</record>
<record id="module_type_rule_odoo_d" model="ir.module.type.rule">
<field name="sequence">4</field>
<field name="module_domain">[('author_ids', '=', 'Odoo')]</field>
<field name="module_type_id" ref="module_type_odoo"/>
</record>
<!-- OCA Rules -->
<record id="module_type_rule_oca_a" model="ir.module.type.rule">
<field name="sequence">100</field>
<field name="module_domain">[('author_ids', '=', 'Odoo Community Association (OCA)')]</field>
<field name="module_type_id" ref="module_type_oca"/>
</record>
</odoo>

213
module_analysis/i18n/module_analysis.pot

@ -0,0 +1,213 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * module_analysis
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 12.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
#: sql_constraint:ir.module.author:0
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 ""

5
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

18
module_analysis/models/base_module_update.py

@ -0,0 +1,18 @@
# Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
# @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'
analyse_installed_modules = fields.Boolean(
string='Analyse Installed Modules', default=True)
@api.multi
def update_module(self):
return super(BaseModuleUpdate, self.with_context(
analyse_installed_modules=self.analyse_installed_modules)
).update_module()

39
module_analysis/models/ir_module_author.py

@ -0,0 +1,39 @@
# Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
# @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)
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.multi
@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})

186
module_analysis/models/ir_module_module.py

@ -0,0 +1,186 @@
# Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
# @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import os
import pygount
from pathlib import Path
import logging
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,
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)
# 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',
},
}
@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,
}
@api.model
def _get_module_encoding(self, file_ext):
return 'utf-8'
# Overload Section
@api.model
def update_list(self):
res = super().update_list()
if self.env.context.get('analyse_installed_modules', False):
self.search([('state', '=', 'installed')]).button_analyse_code()
return res
@api.multi
def write(self, vals):
res = super().write(vals)
if vals.get('state', False) == 'installed':
self.button_analyse_code()
elif 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
@api.multi
def button_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.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
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 = pygount.source_analysis(
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 file_ext, analyses in analysed_datas.items():
for k, v in analyses.items():
values[v['field']] = v['value']
module.write(values)
# Custom Section
@api.model
def _get_files_to_analyse(
self, path, file_extensions, exclude_directories, exclude_files):
res = []
for root, dirs, 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 file_ext, analyse_dict in res_dict.items():
for analyse_type, v in analyse_dict.items():
analyse_dict[analyse_type] = {
'field': v,
'value': 0
}
return res_dict

30
module_analysis/models/ir_module_type.py

@ -0,0 +1,30 @@
# Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
# @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(string='Name', required=True)
sequence = fields.Integer(string='Sequence')
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.multi
@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)

30
module_analysis/models/ir_module_type_rule.py

@ -0,0 +1,30 @@
# Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
# @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

13
module_analysis/post_init_hook.py

@ -0,0 +1,13 @@
# Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
# @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, SUPERUSER_ID
def analyse_installed_modules(cr, registry):
with api.Environment.manage():
env = api.Environment(cr, SUPERUSER_ID, {})
installed_modules = env['ir.module.module'].search(
[('state', '=', 'installed')])
installed_modules.button_analyse_code()

61
module_analysis/readme/CONFIGURE.rst

@ -0,0 +1,61 @@
* 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 modules.
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.

1
module_analysis/readme/CONTRIBUTORS.rst

@ -0,0 +1 @@
* Sylvain LE GAL (https://twitter.com/legalsylvain)

26
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

3
module_analysis/readme/INSTALL.rst

@ -0,0 +1,3 @@
To use this module, you have to install the ``pygount`` python librairy.
``pip install pygount``

8
module_analysis/readme/USAGE.rst

@ -0,0 +1,8 @@
* 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

4
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

BIN
module_analysis/static/description/add_module_type_rules.png

After

Width: 914  |  Height: 298  |  Size: 34 KiB

BIN
module_analysis/static/description/analysis_pie.png

After

Width: 374  |  Height: 404  |  Size: 16 KiB

BIN
module_analysis/static/description/analysis_pivot.png

After

Width: 578  |  Height: 404  |  Size: 34 KiB

BIN
module_analysis/static/description/base_module_update.png

After

Width: 530  |  Height: 214  |  Size: 9.1 KiB

BIN
module_analysis/static/description/config_parameters.png

After

Width: 1291  |  Height: 63  |  Size: 6.9 KiB

BIN
module_analysis/static/description/default_module_type_rules.png

After

Width: 841  |  Height: 268  |  Size: 29 KiB

BIN
module_analysis/static/description/icon.png

After

Width: 128  |  Height: 128  |  Size: 9.2 KiB

523
module_analysis/static/description/index.html

@ -0,0 +1,523 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.14: http://docutils.sourceforge.net/" />
<title>Module Analysis</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="module-analysis">
<h1 class="title">Module Analysis</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/server-tools/tree/12.0/module_analysis"><img alt="OCA/server-tools" src="https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/server-tools-12-0/server-tools-12-0-module_analysis"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/149/12.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>This module allows you to know ‘how much code’ is running on your Odoo
instance, group by ‘Type’ (Odoo Core, OCA, other…)</p>
<p>This module can be usefull in the following cases :</p>
<ul class="simple">
<li>To analyse the size of your technical debt, regarding your Custom modules</li>
<li>To know the ratio between Odoo / OCA and Custom modules</li>
<li>To evaluate the amount to pay to odoo to upgrade your custom code, or the
induced workload</li>
</ul>
<img alt="https://raw.githubusercontent.com/OCA/server-tools/12.0/module_analysis/static/description/installed_modules_by_types.png" src="https://raw.githubusercontent.com/OCA/server-tools/12.0/module_analysis/static/description/installed_modules_by_types.png" />
<p>For that purpose, it adds new concepts</p>
<ul class="simple">
<li><tt class="docutils literal">ir.module.author</tt>, based on the value <tt class="docutils literal">author</tt> present in the manifest
file.</li>
</ul>
<img alt="https://raw.githubusercontent.com/OCA/server-tools/12.0/module_analysis/static/description/module_authors.png" src="https://raw.githubusercontent.com/OCA/server-tools/12.0/module_analysis/static/description/module_authors.png" />
<ul class="simple">
<li><tt class="docutils literal">ir.module.type</tt>, populated by default with Odoo and OCA values.</li>
</ul>
<img alt="https://raw.githubusercontent.com/OCA/server-tools/12.0/module_analysis/static/description/module_types.png" src="https://raw.githubusercontent.com/OCA/server-tools/12.0/module_analysis/static/description/module_types.png" />
<p>Each installed modules have extra data in the ‘Technical Data’ tab :</p>
<img alt="https://raw.githubusercontent.com/OCA/server-tools/12.0/module_analysis/static/description/module_form.png" src="https://raw.githubusercontent.com/OCA/server-tools/12.0/module_analysis/static/description/module_form.png" />
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#installation" id="id1">Installation</a></li>
<li><a class="reference internal" href="#configuration" id="id2">Configuration</a><ul>
<li><a class="reference internal" href="#adding-extra-data" id="id3">Adding Extra data</a></li>
<li><a class="reference internal" href="#exclude-files-and-directories" id="id4">Exclude files and directories</a></li>
</ul>
</li>
<li><a class="reference internal" href="#usage" id="id5">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="id6">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id7">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id8">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id9">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id10">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="installation">
<h1><a class="toc-backref" href="#id1">Installation</a></h1>
<p>To use this module, you have to install the <tt class="docutils literal">pygount</tt> python librairy.</p>
<p><tt class="docutils literal">pip install pygount</tt></p>
</div>
<div class="section" id="configuration">
<h1><a class="toc-backref" href="#id2">Configuration</a></h1>
<ul class="simple">
<li>Go to Apps / Module Analysis / Modules Types Rules</li>
</ul>
<p>The Module types Rules are usefull to get the Type of a module, based on
it information.</p>
<p>This module comes with default rules.</p>
<blockquote>
<img alt="https://raw.githubusercontent.com/OCA/server-tools/12.0/module_analysis/static/description/default_module_type_rules.png" src="https://raw.githubusercontent.com/OCA/server-tools/12.0/module_analysis/static/description/default_module_type_rules.png" />
</blockquote>
<p>You can add your custom rules to identify the modules your team have
developped for exemple,</p>
<blockquote>
<img alt="https://raw.githubusercontent.com/OCA/server-tools/12.0/module_analysis/static/description/add_module_type_rules.png" src="https://raw.githubusercontent.com/OCA/server-tools/12.0/module_analysis/static/description/add_module_type_rules.png" />
</blockquote>
<p>to update the data, you have to :</p>
<ul>
<li><p class="first">Go to ‘Apps’ / ‘Update Apps List’</p>
</li>
<li><p class="first">Check the box ‘Analyse Installed modules’</p>
<blockquote>
<img alt="https://raw.githubusercontent.com/OCA/server-tools/12.0/module_analysis/static/description/base_module_update.png" src="https://raw.githubusercontent.com/OCA/server-tools/12.0/module_analysis/static/description/base_module_update.png" />
</blockquote>
</li>
</ul>
<p>This will update analysis of your installed modules.</p>
<div class="section" id="adding-extra-data">
<h2><a class="toc-backref" href="#id3">Adding Extra data</a></h2>
<p>If you want to analyse other data, (for exemple, having the number of HTML
files), create a custom modules and overload the module model :</p>
<pre class="code python literal-block">
<span class="kn">from</span> <span class="nn">odoo</span> <span class="kn">import</span> <span class="n">api</span><span class="p">,</span> <span class="n">fields</span><span class="p">,</span> <span class="n">models</span>
<span class="k">class</span> <span class="nc">IrModuleModule</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
<span class="n">_inherit</span> <span class="o">=</span> <span class="s1">'ir.module.module'</span>
<span class="n">xml_documentation_qty</span> <span class="o">=</span> <span class="n">fields</span><span class="o">.</span><span class="n">Integer</span><span class="p">(</span>
<span class="n">string</span><span class="o">=</span><span class="s1">'Quantity of Comments in XML Files'</span><span class="p">)</span>
<span class="nd">&#64;api.model</span>
<span class="k">def</span> <span class="nf">_get_analyse_settings</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">res</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="n">_get_analyse_settings</span><span class="p">()</span>
<span class="k">if</span> <span class="ow">not</span> <span class="s1">'.html'</span> <span class="ow">in</span> <span class="n">res</span><span class="p">:</span>
<span class="n">res</span><span class="p">[</span><span class="s1">'.html'</span><span class="p">]</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">res</span><span class="p">[</span><span class="s1">'.html'</span><span class="p">][</span><span class="s1">'documentation'</span><span class="p">]</span> <span class="s1">'xml_documentation_qty'</span>
<span class="k">return</span> <span class="n">res</span>
</pre>
</div>
<div class="section" id="exclude-files-and-directories">
<h2><a class="toc-backref" href="#id4">Exclude files and directories</a></h2>
<p>Two parameters are availaible in ‘Settings’ / ‘Technical’ / ‘Parameters’
‘System Parameters’ :</p>
<blockquote>
<img alt="https://raw.githubusercontent.com/OCA/server-tools/12.0/module_analysis/static/description/config_parameters.png" src="https://raw.githubusercontent.com/OCA/server-tools/12.0/module_analysis/static/description/config_parameters.png" />
</blockquote>
<p>The list of folders and filename will be exclude from the analysis.
You can change the default settings.</p>
</div>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#id5">Usage</a></h1>
<ul class="simple">
<li>Go to ‘Apps’ / ‘Module Analysis’ / ‘Installed module by Types’</li>
</ul>
<p>Open the stats to analyse the detail of the code installed</p>
<blockquote>
<img alt="https://raw.githubusercontent.com/OCA/server-tools/12.0/module_analysis/static/description/analysis_pivot.png" src="https://raw.githubusercontent.com/OCA/server-tools/12.0/module_analysis/static/description/analysis_pivot.png" />
<img alt="https://raw.githubusercontent.com/OCA/server-tools/12.0/module_analysis/static/description/analysis_pie.png" src="https://raw.githubusercontent.com/OCA/server-tools/12.0/module_analysis/static/description/analysis_pie.png" />
</blockquote>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id6">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/server-tools/issues">GitHub Issues</a>.
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
<a class="reference external" href="https://github.com/OCA/server-tools/issues/new?body=module:%20module_analysis%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#id7">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id8">Authors</a></h2>
<ul class="simple">
<li>GRAP</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id9">Contributors</a></h2>
<ul class="simple">
<li>Sylvain LE GAL (<a class="reference external" href="https://twitter.com/legalsylvain">https://twitter.com/legalsylvain</a>)</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id10">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>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.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/server-tools/tree/12.0/module_analysis">OCA/server-tools</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</body>
</html>

BIN
module_analysis/static/description/installed_modules_by_types.png

After

Width: 415  |  Height: 238  |  Size: 14 KiB

BIN
module_analysis/static/description/module_authors.png

After

Width: 907  |  Height: 339  |  Size: 25 KiB

BIN
module_analysis/static/description/module_form.png

After

Width: 775  |  Height: 521  |  Size: 39 KiB

BIN
module_analysis/static/description/module_types.png

After

Width: 904  |  Height: 196  |  Size: 15 KiB

1
module_analysis/tests/__init__.py

@ -0,0 +1 @@
from . import test_module

33
module_analysis/tests/test_module.py

@ -0,0 +1,33 @@
# Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
# @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, post_install
@post_install(True)
class TestModule(TransactionCase):
def setUp(self):
super().setUp()
self.IrModuleModule = self.env['ir.module.module']
def test_installed_modules(self):
installed_modules = self.IrModuleModule.search(
[('state', '=', 'installed')])
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))

24
module_analysis/views/menu.xml

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<odoo>
<menuitem id="menu_module_analysis"
name="Analysis"
parent="base.menu_management" sequence="100"
groups="base.group_no_one"/>
<menuitem id="menu_module_analysis_settings"
name="Settings"
parent="menu_module_analysis" sequence="10"/>
<menuitem id="menu_module_analysis_reporting"
name="Reporting"
parent="menu_module_analysis" sequence="20"/>
</odoo>

20
module_analysis/views/view_base_module_update.xml

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<odoo>
<record id="view_base_module_update_form" model="ir.ui.view">
<field name="model">base.module.update</field>
<field name="inherit_id" ref="base.view_base_module_update"/>
<field name="arch" type="xml">
<xpath expr='//group[@states="init"]' position="inside">
<field name="analyse_installed_modules"/>
</xpath>
</field>
</record>
</odoo>

52
module_analysis/views/view_ir_module_author.xml

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<odoo>
<record id="view_ir_module_author_form" model="ir.ui.view">
<field name="model">ir.module.author</field>
<field name="arch" type="xml">
<form>
<sheet>
<div class="oe_title">
<div class="oe_edit_only"><label for="name"/></div>
<h1><field name="name"/></h1>
</div>
<group>
<field name="installed_module_qty"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="view_ir_module_author_tree" model="ir.ui.view">
<field name="model">ir.module.author</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="installed_module_qty"/>
</tree>
</field>
</record>
<record id="action_ir_module_author" model="ir.actions.act_window">
<field name="name">Modules Authors</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">ir.module.author</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="menu_module_authors"
parent="menu_module_analysis_settings" sequence="1"
action="action_ir_module_author"
groups="base.group_no_one"/>
</odoo>

67
module_analysis/views/view_ir_module_module.xml

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<odoo>
<record id="view_ir_module_module_form" model="ir.ui.view">
<field name="model">ir.module.module</field>
<field name="inherit_id" ref="base.module_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='state']/.." position="after">
<button name="button_analyse_code" states="installed" string="Refresh Code Analysis" type="object" class="btn btn-primary"/>
<group string="Code Size" col="4">
<field name="python_code_qty"/>
<field name="xml_code_qty"/>
<field name="js_code_qty"/>
<field name="css_code_qty"/>
</group>
<group string="Type and Authors">
<field name="author_ids" widget="many2many_tags"/>
<field name="module_type_id"/>
</group>
</xpath>
</field>
</record>
<record id="view_ir_module_module_pivot" model="ir.ui.view">
<field name="model">ir.module.module</field>
<field name="arch" type="xml">
<pivot>
<field name="module_type_id" type="row"/>
<field name="python_code_qty" type="measure"/>
<field name="xml_code_qty" type="measure"/>
<field name="js_code_qty" type="measure"/>
<field name="css_code_qty" type="measure"/>
</pivot>
</field>
</record>
<record id="view_ir_module_module_graph" model="ir.ui.view">
<field name="model">ir.module.module</field>
<field name="arch" type="xml">
<graph type="pie">
<field name="module_type_id" type="row"/>
</graph>
</field>
</record>
<record id="action_ir_module_module_by_type" model="ir.actions.act_window">
<field name="name">Installed Modules by Types</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">ir.module.module</field>
<field name="domain">[('state', '=', 'installed')]</field>
<field name="view_type">form</field>
<field name="view_mode">pivot,graph</field>
</record>
<menuitem id="menu_module_by_type"
parent="menu_module_analysis_reporting" sequence="5"
action="action_ir_module_module_by_type"
groups="base.group_no_one"/>
</odoo>

50
module_analysis/views/view_ir_module_type.xml

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<odoo>
<record id="view_ir_module_type_form" model="ir.ui.view">
<field name="model">ir.module.type</field>
<field name="arch" type="xml">
<form>
<sheet>
<div class="oe_title">
<div class="oe_edit_only"><label for="name"/></div>
<h1><field name="name"/></h1>
</div>
<group>
</group>
</sheet>
</form>
</field>
</record>
<record id="view_ir_module_type_tree" model="ir.ui.view">
<field name="model">ir.module.type</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="installed_module_qty"/>
</tree>
</field>
</record>
<record id="action_ir_module_type" model="ir.actions.act_window">
<field name="name">Modules Types</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">ir.module.type</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="menu_module_types"
parent="menu_module_analysis_settings" sequence="2"
action="action_ir_module_type"
groups="base.group_no_one"/>
</odoo>

34
module_analysis/views/view_ir_module_type_rule.xml

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
@author: Sylvain LE GAL (https://twitter.com/legalsylvain)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<odoo>
<record id="view_ir_module_type_rule_tree" model="ir.ui.view">
<field name="model">ir.module.type.rule</field>
<field name="arch" type="xml">
<tree editable="bottom">
<field name="sequence" widget="handle"/>
<field name="module_type_id"/>
<field name="module_domain"/>
</tree>
</field>
</record>
<record id="action_ir_module_type_rule" model="ir.actions.act_window">
<field name="name">Modules Types Rules</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">ir.module.type.rule</field>
<field name="view_type">form</field>
<field name="view_mode">tree</field>
</record>
<menuitem id="menu_module_type_rules"
parent="menu_module_analysis_settings" sequence="3"
action="action_ir_module_type_rule"
groups="base.group_no_one"/>
</odoo>

1
requirements.txt

@ -3,3 +3,4 @@ openpyxl
xlrd
xlwt
pysftp
pygount

2
setup/_metapackage/VERSION.txt

@ -1 +1 @@
12.0.20190709.0
12.0.20190723.0

1
setup/_metapackage/setup.py

@ -23,6 +23,7 @@ setuptools.setup(
'odoo12-addon-fetchmail_notify_error_to_sender',
'odoo12-addon-html_image_url_extractor',
'odoo12-addon-html_text',
'odoo12-addon-module_analysis',
'odoo12-addon-module_auto_update',
'odoo12-addon-onchange_helper',
'odoo12-addon-scheduler_error_mailer',

1
setup/module_analysis/odoo/addons/module_analysis

@ -0,0 +1 @@
../../../../module_analysis

6
setup/module_analysis/setup.py

@ -0,0 +1,6 @@
import setuptools
setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
Loading…
Cancel
Save