You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

126 lines
4.6 KiB

  1. # Copyright (C) 2019-Today: GRAP (<http://www.grap.coop/>)
  2. # @author: Sylvain LE GAL (https://twitter.com/legalsylvain)
  3. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  4. import json
  5. import logging
  6. import os
  7. import subprocess
  8. from odoo import api, fields, models
  9. from odoo.tools.safe_eval import safe_eval
  10. from odoo.modules.module import get_module_path
  11. _logger = logging.getLogger(__name__)
  12. class IrModuleModule(models.Model):
  13. _inherit = 'ir.module.module'
  14. author_ids = fields.Many2many(
  15. string='Authors', comodel_name='ir.module.author', readonly=True,
  16. relation='ir_module_module_author_rel')
  17. module_type_id = fields.Many2one(
  18. string='Module Type', comodel_name='ir.module.type', readonly=True)
  19. python_lines_qty = fields.Integer(string='Python Lines', readonly=True)
  20. xml_lines_qty = fields.Integer(string='XML Lines', readonly=True)
  21. js_lines_qty = fields.Integer(string='JS Lines', readonly=True)
  22. css_lines_qty = fields.Integer(string='CSS Lines', readonly=True)
  23. @api.model
  24. def update_list(self):
  25. res = super(IrModuleModule, self).update_list()
  26. if self.env.context.get('analyze_installed_modules', False):
  27. self.search([('state', '=', 'installed')]).button_analyze_code()
  28. return res
  29. @api.multi
  30. def write(self, vals):
  31. res = super(IrModuleModule, self).write(vals)
  32. if vals.get('state', False) == 'installed':
  33. self.button_analyze_code()
  34. elif vals.get('state', False) == 'uninstalled'\
  35. and 'module_analysis' not in [x.name for x in self]:
  36. self.write({
  37. 'author_ids': [6, 0, []],
  38. 'module_type_id': False,
  39. 'python_lines_qty': False,
  40. 'xml_lines_qty': 0,
  41. 'js_lines_qty': 0,
  42. 'css_lines_qty': 0,
  43. })
  44. return res
  45. @api.multi
  46. def button_analyze_code(self):
  47. IrModuleAuthor = self.env['ir.module.author']
  48. IrModuleTypeRule = self.env['ir.module.type.rule']
  49. rules = IrModuleTypeRule.search([])
  50. exclude_dir = "lib,description,tests,demo"
  51. include_lang = self._get_analyzed_languages()
  52. for module in self:
  53. _logger.info("Analyzing Code for module %s" % (module.name))
  54. # Update Authors, based on manifest key
  55. authors = []
  56. if module.author and module.author[0] == '[':
  57. author_txt_list = safe_eval(module.author)
  58. else:
  59. author_txt_list = module.author.split(',')
  60. author_txt_list = [x.strip() for x in author_txt_list]
  61. author_txt_list = [x for x in author_txt_list if x]
  62. for author_txt in author_txt_list:
  63. authors.append(IrModuleAuthor._get_or_create(author_txt))
  64. author_ids = [x.id for x in authors]
  65. module.author_ids = author_ids
  66. # Update Module Type, based on rules
  67. module_type_id = rules._get_module_type_id_from_module(module)
  68. module.module_type_id = module_type_id
  69. # Get Path of module folder and parse the code
  70. path = get_module_path(module.name)
  71. try:
  72. command = [
  73. 'cloc',
  74. '--exclude-dir=%s' % (exclude_dir),
  75. '--skip-uniqueness',
  76. '--include-lang=%s' % (include_lang),
  77. '--not-match-f="__openerp__.py|__manifest__.py"',
  78. '--json',
  79. os.path.join(path)]
  80. temp = subprocess.Popen(command, stdout=subprocess.PIPE)
  81. bytes_output = temp.communicate()
  82. output = bytes_output[0].decode("utf-8").replace('\n', '')
  83. json_res = json.loads(output)
  84. values = self._prepare_values_from_json(json_res)
  85. module.write(values)
  86. except Exception:
  87. _logger.warning(
  88. 'Failed to execute the cloc command on module %s' % (
  89. module.name))
  90. @api.model
  91. def _get_analyzed_languages(self):
  92. "Overload the function to add extra languages to analyze"
  93. return "Python,XML,JavaScript,CSS"
  94. @api.model
  95. def _prepare_values_from_json(self, json_value):
  96. return {
  97. 'python_lines_qty': json_value.get('Python', {}).get('code', 0),
  98. 'xml_lines_qty': json_value.get('XML', {}).get('code', 0),
  99. 'js_lines_qty': json_value.get('JavaScript', {}).get('code', 0),
  100. 'css_lines_qty': json_value.get('CSS', {}).get('code', 0),
  101. }