# -*- coding: utf-8 -*- ############################################################################## # # OpenERP, Open Source Management Solution # Copyright (C) 2004-2010 Tiny SPRL (). # # 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 . # ############################################################################## 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 = ''' Fields Result%s'''%(self.get_style()) res += str_html header = ('%s%s%s', [_('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 = '' + self.format_html_table(header, data_list=final_dict) + '

' res += res_str.replace('''' return "" def get_result_general(self, dict_obj, name=''): str_html = ''' %s Result%s'''% (name, self.get_style()) header = ('', [_('Object Name'), _('Suggestion')]) if not self.error: res = str_html + self.format_html_table(header, data_list=dict_obj) + '
%s%s
' res = res.replace('''