Launchpad automatic translations update.
[odoo/odoo.git] / addons / base_module_quality / base_module_quality.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21 import os
22
23 import pooler
24 from tools.translate import _
25 from osv import osv, fields
26 import logging
27 import addons
28 class abstract_quality_check(object):
29     '''
30         This Class is abstract class for all test
31     '''
32
33     def __init__(self):
34         '''
35         this method should initialize the variables
36         '''
37         #This float have to store the rating of the module.
38         #Used to compute the final score (average of all scores).
39         #0 <= self.score <= 1
40         self.score = 0.0
41
42         #This char have to store the name of the test.
43         self.name = ""
44
45         #This char have to store the aim of the test and eventually a note.
46         self.note = ""
47
48         #This char have to store the result.
49         #Used to display the result of the test.
50         self.result = ""
51
52         #This char have to store the result with more details.
53         #Used to provide more details if necessary.
54         self.result_details = ""
55
56         # This boolean variable defines that if you do not want to calculate score and just only need detail
57         # or summary report for some test then you will make it False.
58         self.bool_count_score = True
59
60         #This bool defines if the test can be run only if the module
61         #is installed.
62         #True => the module have to be installed.
63         #False => the module can be uninstalled.
64         self.bool_installed_only = True
65
66         #This variable is used to give result of test more weight,
67         #because some tests are more critical than others.
68         self.ponderation = 1.0
69
70         #Specify test got an error on module
71         self.error = False
72
73         #Specify the minimal score for the test (in percentage(%))
74         self.min_score = 50
75
76         #Specify whether test should be consider for Quality checking of the module
77         self.active = True
78
79         #This variable used to give message if test result is good or not
80         self.message = ''
81         self.log = logging.getLogger('module.quality')
82
83         #The tests have to subscribe itselfs in this list, that contains
84         #all the test that have to be performed.
85         self.tests = []
86         module_path = addons.get_module_path('base_module_quality')
87         for item in os.listdir(module_path):
88             path = module_path + '/' + item
89             if os.path.isdir(path) and os.path.exists(path + '/' + item + '.py') and item not in ['report', 'wizard', 'security']:
90                 item2 = 'base_module_quality.' + item +'.' + item
91                 x_module = __import__(item2)
92                 x_file = getattr(x_module, item)
93                 x_obj = getattr(x_file, item)
94                 self.tests.append(x_obj)
95
96     def run_test(self, cr, uid, module_path=""):
97         '''
98         this method should do the test and fill the score, result and result_details var
99         '''
100         raise osv.except_osv(_('Programming Error'), _('Test Is Not Implemented'))
101
102     def get_objects(self, cr, uid, module):
103         # This function returns all object of the given module..
104         pool = pooler.get_pool(cr.dbname)
105         ids2 = pool.get('ir.model.data').search(cr, uid,
106             [('module', '=', module), ('model', '=', 'ir.model')])
107         model_list = []
108         model_data = pool.get('ir.model.data').browse(cr, uid, ids2)
109         for model in model_data:
110             model_list.append(model.res_id)
111         self.log.debug('get_objects() model_list: %s', ','.join(map(str, model_list)))
112         obj_list = []
113         for mod in pool.get('ir.model').browse(cr, uid, model_list):
114             obj_list.append(str(mod.model))
115         self.log.debug('get_objects() obj_list: %s', ','.join(obj_list))
116         return obj_list
117
118     def get_model_ids(self, cr, uid, models=[]):
119         # This function returns all ids of the given objects..
120         if not models:
121             return []
122         pool = pooler.get_pool(cr.dbname)
123         self.log.debug('get_model_ids([%s])', ', '.join(models))
124         return pool.get('ir.model').search(cr, uid, [('model', 'in', models)])
125
126     def get_ids(self, cr, uid, object_list):
127         #This method return dictionary with ids of records of object for module
128         pool = pooler.get_pool(cr.dbname)
129         result_ids = {}
130         for obj in object_list:
131             ids = pool.get(obj).search(cr, uid, [])
132             ids = filter(lambda id: id != None, ids or [])
133             result_ids[obj] = ids
134         return result_ids
135
136     def format_table(self, header=[], data_list={}): #This function can work forwidget="text_wiki"
137         detail = ""
138         detail += (header[0]) % tuple(header[1])
139         frow = '\n|-'
140         for i in header[1]:
141             frow += '\n| %s'
142         for key, value in data_list.items():
143             detail += (frow) % tuple(value)
144         detail = detail + '\n|}'
145         return detail
146
147     def format_html_table(self, header=[], data_list=[]): #This function can work for widget="html_tag"
148         # function create html table....
149         detail = ""
150         detail += (header[0]) % tuple(header[1])
151         frow = '<tr>'
152         for i in header[1]:
153             frow += '<td>%s</td>'
154         frow += '</tr>'
155         for key, value in data_list.items():
156             detail += (frow) % tuple(value)
157         return detail
158
159     def add_quatation(self, x_no, y_no):
160         return x_no/y_no
161
162     def get_style(self):
163         # This function return style tag with specified styles for html pages
164         style = '''
165             <style>
166                 .divstyle {
167                 border:1px solid #aaaaaa;
168                 background-color:#f9f9f9;
169                 padding:5px;
170                 }
171                 .tablestyle
172                 {
173                 border:1px dashed gray;
174                 }
175                 .tdatastyle
176                 {
177                 border:0.5px solid gray;
178                 }
179                 .head
180                 {
181                 color: black;
182                 background: none;
183                 font-weight: normal;
184                 margin: 0;
185                 padding-top: .5em;
186                 padding-bottom: .17em;
187                 border-bottom: 1px solid #aaa;
188                 }
189                 }
190           </style> '''
191         return style
192
193
194 class module_quality_check(osv.osv):
195     _name = 'module.quality.check'
196     _columns = {
197         'name': fields.char('Rated Module', size=64, ),
198         'final_score': fields.char('Final Score (%)', size=10,),
199         'check_detail_ids': fields.one2many('module.quality.detail', 'quality_check_id', 'Tests',)
200     }
201
202     def check_quality(self, cr, uid, module_name, module_state=None):
203         '''
204         This function will calculate score of openerp module
205         It will return data in below format:
206             Format: {'final_score':'80.50', 'name': 'sale',
207                     'check_detail_ids':
208                         [(0,0,{'name':'workflow_test', 'score':'100', 'ponderation':'0', 'summary': text_wiki format data, 'detail': html format data, 'state':'done', 'note':'XXXX'}),
209                         ((0,0,{'name':'terp_test', 'score':'60', 'ponderation':'1', 'summary': text_wiki format data, 'detail': html format data, 'state':'done', 'note':'terp desctioption'}),
210                          ..........]}
211         So here the detail result is in html format and summary will be in text_wiki format.
212         '''
213         pool = pooler.get_pool(cr.dbname)
214         log = logging.getLogger('module.quality')
215         obj_module = pool.get('ir.module.module')
216         if not module_state:
217             module_id = obj_module.search(cr, uid, [('name', '=', module_name)])
218             if module_id:
219                 module_state = obj_module.browse(cr, uid, module_id[0]).state
220
221         abstract_obj = abstract_quality_check()
222         score_sum = 0.0
223         ponderation_sum = 0.0
224         create_ids = []
225         module_path = addons.get_module_path(module_name)
226         log.info('Performing quality tests for %s', module_name)
227         for test in abstract_obj.tests:
228             val = test.quality_test()
229             if not val.active:
230                 log.info('Skipping inactive step %s for %s', val.name, module_name)
231                 continue
232
233             log.info('Performing step %s for %s', val.name, module_name)
234             # Get a separate cursor per test, so that an SQL error in one
235             # will not block the others.
236             cr2 = pooler.get_db(cr.dbname).cursor()
237
238             try:
239                 if not val.bool_installed_only or module_state == "installed":
240                     val.run_test(cr2, uid, str(module_path))
241                     if not val.error:
242                         data = {
243                             'name': val.name,
244                             'score': val.score * 100,
245                             'ponderation': val.ponderation,
246                             'summary': val.result,
247                             'detail': val.result_details,
248                             'state': 'done',
249                             'note': val.note,
250                             'message': val.message
251                         }
252                         if val.bool_count_score:
253                             score_sum += val.score * val.ponderation
254                             ponderation_sum += val.ponderation
255                     else:
256                         data = {
257                             'name': val.name,
258                             'score': 0,
259                             'summary': val.result,
260                             'state': 'skipped',
261                             'note': val.note,
262                         }
263                 else:
264                     data = {
265                         'name': val.name,
266                         'note': val.note,
267                         'score': 0,
268                         'state': 'skipped',
269                         'summary': _("The module has to be installed before running this test.")
270                     }
271                 create_ids.append((0, 0, data))
272                 log.info('Finished quality test step')
273             except Exception, e:
274                 log.exception("Could not finish test step %s due to %s", val.name, e)
275             finally:
276                 cr2.rollback()
277                 cr2.close()
278         final_score = ponderation_sum and '%.2f' % (score_sum / ponderation_sum * 100) or 0
279         data = {
280             'name': module_name,
281             'final_score': final_score,
282             'check_detail_ids' : create_ids,
283         }
284         return data
285
286 module_quality_check()
287
288 class module_quality_detail(osv.osv):
289     _name = 'module.quality.detail'
290     _columns = {
291         'quality_check_id': fields.many2one('module.quality.check', 'Quality'),
292         'name': fields.char('Name',size=128),
293         'score': fields.float('Score (%)'),
294         'ponderation': fields.float('Ponderation', help='Some tests are more critical than others, so they have a bigger weight in the computation of final rating'),
295         'note': fields.text('Note'),
296         'summary': fields.text('Summary'),
297         'detail': fields.text('Details'),
298         'message': fields.char('Message', size=64),
299         'state': fields.selection([('done','Done'),('skipped','Skipped'),], 'State', size=24, help='The test will be completed only if the module is installed or if the test may be processed on uninstalled module.'),
300     }
301
302 module_quality_detail()
303
304 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: