211 lines
10 KiB
Python
211 lines
10 KiB
Python
# -*- coding: utf-8 -*-
|
|
##############################################################################
|
|
#
|
|
# OpenERP, Open Source Management Solution
|
|
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Affero General Public License as
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
# License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
##############################################################################
|
|
|
|
import os
|
|
import re
|
|
|
|
from tools.translate import _
|
|
from base_module_quality import base_module_quality
|
|
import pooler
|
|
|
|
class quality_test(base_module_quality.abstract_quality_check):
|
|
|
|
def __init__(self):
|
|
super(quality_test, self).__init__()
|
|
self.name = _("Object Test")
|
|
self.note = _("""
|
|
Test checks for fields, views, security rules, dependancy level
|
|
""")
|
|
self.bool_installed_only = True
|
|
self.min_score = 40
|
|
return None
|
|
|
|
def run_test(self, cr, uid, module_path):
|
|
pool = pooler.get_pool(cr.dbname)
|
|
module_name = module_path.split('/')[-1]
|
|
obj_list = self.get_objects(cr, uid, module_name)
|
|
ids_model = self.get_model_ids(cr, uid, obj_list)
|
|
result_security = {}
|
|
|
|
if obj_list: # if module has no new created classes skipp fields, views, security tests
|
|
field_obj = pool.get('ir.model.fields')
|
|
view_obj = pool.get('ir.ui.view')
|
|
access_obj = pool.get('ir.model.access')
|
|
|
|
field_ids = field_obj.search(cr, uid, [('model', 'in', obj_list)])
|
|
view_ids = view_obj.search(cr, uid, [('model', 'in', obj_list), ('type', 'in', ['tree', 'form'])])
|
|
access_ids = access_obj.search(cr, uid, [('model_id', 'in', ids_model)])
|
|
|
|
field_data = field_obj.browse(cr, uid, field_ids)
|
|
view_data = view_obj.browse(cr, uid, view_ids)
|
|
access_data = access_obj.browse(cr, uid, access_ids)
|
|
|
|
result_dict = {}
|
|
result_view = {}
|
|
good_field = 0
|
|
total_field = 0
|
|
|
|
# field test .....
|
|
for field in field_data:
|
|
result_dict[field.model] = []
|
|
for field in field_data:
|
|
ttype = field.ttype
|
|
name = field.name
|
|
total_field += 1
|
|
check_str = re.compile('[a-z]+[\w_]*$') #re.compile('[a-z]+[_]?[a-z]+$')
|
|
if ttype == 'many2one':
|
|
if name.split('_')[-1] == 'id':
|
|
good_field += 1
|
|
else:
|
|
data = 'many2one field should end with _id'
|
|
result_dict[field.model].append([field.model, name, data])
|
|
elif ttype in ['many2many', 'one2many']:
|
|
if name.split('_')[-1] == 'ids':
|
|
good_field += 1
|
|
else:
|
|
data = '%s field should end with _ids'% (ttype)
|
|
result_dict[field.model].append([field.model, name, data])
|
|
elif check_str.match(name):
|
|
good_field += 1
|
|
else:
|
|
data = 'Field name should be in lower case or it should follow python standard'
|
|
result_dict[field.model].append([field.model, name, data])
|
|
|
|
#views tests
|
|
for res in result_dict.keys():
|
|
if not result_dict[res]:
|
|
del result_dict[res]
|
|
view_dict = {}
|
|
total_views = len(obj_list) * 2
|
|
model_views = 0
|
|
for view in view_data:
|
|
view_dict[view.model] = []
|
|
model_views += 1
|
|
for view in view_data:
|
|
ttype = view.type
|
|
view_dict[view.model].append(ttype)
|
|
for view in view_dict:
|
|
if len(view_dict[view]) < 2:
|
|
model_views -= 1
|
|
result_view[view] = [view, 'You should have atleast form/tree view of an object']
|
|
if model_views > total_views:
|
|
model_views = total_views
|
|
|
|
#security rules test...
|
|
list_files = os.listdir(module_path)
|
|
security_folder = False
|
|
for file_sec in list_files:
|
|
if file_sec == 'security':
|
|
path = os.path.join(module_path, file_sec)
|
|
if os.path.isdir(path):
|
|
security_folder = True
|
|
if not security_folder:
|
|
result_security[module_name] = [module_name, 'Security folder is not available (All security rules and groups should define in security folder)']
|
|
access_list = []
|
|
good_sec = len(obj_list)
|
|
bad_sec = 0
|
|
for access in access_data:
|
|
access_list.append(access.model_id.model)
|
|
if not access.group_id:
|
|
result_security[access.model_id.model] = [access.model_id.model, 'Specified object has no related group define on access rules']
|
|
bad_sec += 1 # to be check
|
|
not_avail_access = filter(lambda x: not x in access_list, obj_list)
|
|
for obj in not_avail_access:
|
|
bad_sec += 1
|
|
result_security[obj] = [obj, 'Object should have at least one security rule defined on it']
|
|
|
|
# Dependacy test of module
|
|
module_obj = pool.get('ir.module.module')
|
|
module_ids = module_obj.search(cr, uid, [('name', '=', module_name)])
|
|
module_data = module_obj.browse(cr, uid, module_ids)
|
|
depend_list = []
|
|
depend_check = []
|
|
remove_list = []
|
|
for depend in module_data[0].dependencies_id:
|
|
depend_list.append(depend.name)
|
|
module_ids = module_obj.search(cr, uid, [('name', 'in', depend_list)])
|
|
module_data = module_obj.browse(cr, uid, module_ids)
|
|
for data in module_data:
|
|
for check in data.dependencies_id:
|
|
depend_check.append(check.name)
|
|
for dep in depend_list:
|
|
if dep in depend_check and not dep in remove_list:
|
|
remove_list.append(str(dep))
|
|
if remove_list:
|
|
result_security[module_name] = [remove_list, 'Unnecessary dependacy should be removed please Provide only highest requirement level']
|
|
bad_depend = len(remove_list)
|
|
|
|
if not obj_list:
|
|
score_depend = (100 - (bad_depend * 5)) / 100.0 # note : score is calculated based on if you have for e.g. two module extra in dependancy it will score -10 out of 100
|
|
self.score = score_depend
|
|
self.result = self.get_result({ module_name: ['No object found', 'No object found', 'No object found', int(score_depend * 100)]})
|
|
self.result_details += self.get_result_general(result_security, name="General")
|
|
return None
|
|
|
|
score_view = total_views and float(model_views) / float(total_views)
|
|
score_field = total_field and float(good_field) / float(total_field)
|
|
score_depend = (100 - (bad_depend * 5)) / 100.0 # note : score is calculated based on if you have for e.g. two module extra in dependancy it will score -10 out of 100
|
|
score_security = good_sec and float(good_sec - bad_sec) / float(good_sec)
|
|
self.score = (score_view + score_field + score_security + score_depend) / 4
|
|
if self.score*100 < self.min_score:
|
|
self.message = 'Score is below than minimal score(%s%%)' % self.min_score
|
|
self.result = self.get_result({ module_name: [int(score_field * 100), int(score_view * 100), int(score_security * 100), int(score_depend * 100)]})
|
|
self.result_details += self.get_result_details(result_dict)
|
|
self.result_details += self.get_result_general(result_view, name="View")
|
|
self.result_details += self.get_result_general(result_security, name="General")
|
|
return None
|
|
|
|
def get_result(self, dict_obj):
|
|
header = ('{| border="1" cellspacing="0" cellpadding="5" align="left" \n! %-40s \n! %-40s \n! %-40s \n! %-10s \n', [_('Result of fields in %'), _('Result of views in %'), _('Result of Security in %'), _('Result of dependancy in %')])
|
|
if not self.error:
|
|
return self.format_table(header, data_list=dict_obj)
|
|
return ""
|
|
|
|
def get_result_details(self, dict_obj):
|
|
res = ""
|
|
if dict_obj != {}:
|
|
str_html = '''<html><strong> Fields Result</strong><head>%s</head><body>'''%(self.get_style())
|
|
res += str_html
|
|
header = ('<tr><th class="tdatastyle">%s</th><th class="tdatastyle">%s</th><th class="tdatastyle">%s</th></tr>', [_('Object Name'), _('Field name'), _('Suggestion')])
|
|
if not self.error:
|
|
for key in dict_obj.keys():
|
|
data_list = []
|
|
final_dict = {}
|
|
data_list = dict_obj[key]
|
|
count = 0
|
|
for i in data_list:
|
|
count = count + 1
|
|
final_dict[key + str(count)] = i
|
|
res_str = '<table class="tablestyle">' + self.format_html_table(header, data_list=final_dict) + '</table><br>'
|
|
res += res_str.replace('''<td''', '''<td class="tdatastyle" ''')
|
|
return res + '</body></html>'
|
|
return ""
|
|
|
|
def get_result_general(self, dict_obj, name=''):
|
|
str_html = '''<html><strong> %s Result</strong><head>%s</head><body><table class="tablestyle">'''% (name, self.get_style())
|
|
header = ('<tr><th class="tdatastyle">%s</th><th class="tdatastyle">%s</th></tr>', [_('Object Name'), _('Suggestion')])
|
|
if not self.error:
|
|
res = str_html + self.format_html_table(header, data_list=dict_obj) + '</table></body></html>'
|
|
res = res.replace('''<td''', '''<td class="tdatastyle" ''')
|
|
return res
|
|
return ""
|
|
|
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: |