# Copyright 2012 - Now Savoir-faire Linux # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import fields, models, api from odoo.tools.safe_eval import safe_eval import re def is_one_value(result): # check if sql query returns only one value if type(result) is dict and 'value' in result.dictfetchone(): return True elif type(result) is list and 'value' in result[0]: return True else: return False RE_SELECT_QUERY = re.compile('.*(' + '|'.join(( 'INSERT', 'UPDATE', 'DELETE', 'CREATE', 'ALTER', 'DROP', 'GRANT', 'REVOKE', 'INDEX', )) + ')') def is_sql_or_ddl_statement(query): """Check if sql query is a SELECT statement""" return not RE_SELECT_QUERY.match(query.upper()) class KPIThresholdRange(models.Model): """ KPI Threshold Range """ _name = "kpi.threshold.range" _description = "KPI Threshold Range" name = fields.Char('Name', size=50, required=True) valid = fields.Boolean(string='Valid', required=True, compute="_compute_is_valid_range", default=True) invalid_message = fields.Char(string='Message', size=100, compute="_compute_is_valid_range") min_type = fields.Selection(( ('static', 'Fixed value'), ('python', 'Python Code'), ('local', 'SQL - Local DB'), ('external', 'SQL - Externa DB'), ), 'Min Type', required=True) min_value = fields.Float(string='Minimum Value', compute="_compute_min_value") min_fixed_value = fields.Float('Minimum Fixed Value') min_code = fields.Text('Minimum Computation Code') min_error = fields.Char('Minimum Error', compute="_compute_min_value") min_dbsource_id = fields.Many2one( 'base.external.dbsource', 'External DB Source Minimum', ) max_type = fields.Selection(( ('static', 'Fixed value'), ('python', 'Python Code'), ('local', 'SQL - Local DB'), ('external', 'SQL - External DB'), ), 'Max Type', required=True) max_value = fields.Float(string='Maximum Value', compute="_compute_max_value") max_fixed_value = fields.Float('Maximum Fixed Value') max_code = fields.Text('Maximum Computation Code') max_error = fields.Char('Maximum Error', compute="_compute_max_value") max_dbsource_id = fields.Many2one( 'base.external.dbsource', 'External DB Source Maximum', ) color = fields.Char( string="Color", help="Choose your color" ) threshold_ids = fields.Many2many( 'kpi.threshold', 'kpi_threshold_range_rel', 'range_id', 'threshold_id', 'Thresholds', ) company_id = fields.Many2one( 'res.company', 'Company', default=lambda self: self.env.user.company_id.id) @api.multi def _compute_min_value(self): for obj in self: value = None error = None try: if obj.min_type == 'local' and is_sql_or_ddl_statement( obj.min_code): self.env.cr.execute(obj.min_code) dic = self.env.cr.dictfetchall() if is_one_value(dic): value = dic[0]['value'] elif (obj.min_type == 'external' and obj.min_dbsource_id.id and is_sql_or_ddl_statement(obj.min_code)): dbsrc_obj = obj.min_dbsource_id res = dbsrc_obj.execute(obj.min_code) if is_one_value(res): value = res[0]['value'] elif obj.min_type == 'python': value = safe_eval(obj.min_code) else: value = obj.min_fixed_value except Exception as e: value = None error = str(e) obj.min_value = value obj.min_error = error @api.multi def _compute_max_value(self): for obj in self: value = None error = None try: if obj.max_type == 'local' and is_sql_or_ddl_statement( obj.max_code): self.env.cr.execute(obj.max_code) dic = self.env.cr.dictfetchall() if is_one_value(dic): value = dic[0]['value'] elif (obj.max_type == 'external' and obj.max_dbsource_id.id and is_sql_or_ddl_statement(obj.max_code)): dbsrc_obj = obj.max_dbsource_id res = dbsrc_obj.execute(obj.max_code) if is_one_value(res): value = res[0]['value'] elif obj.max_type == 'python': value = safe_eval(obj.max_code) else: value = obj.max_fixed_value except Exception as e: value = None error = str(e) obj.max_value = value obj.max_error = error @api.multi def _compute_is_valid_range(self): for obj in self: if obj.min_error or obj.max_error: obj.valid = False obj.invalid_message = ( "Either minimum or maximum value has " "computation errors. Please fix them.") elif obj.max_value < obj.min_value: obj.valid = False obj.invalid_message = ( "Minimum value is greater than the maximum " "value! Please adjust them.") else: obj.valid = True obj.invalid_message = ""