diff --git a/kpi/__manifest__.py b/kpi/__manifest__.py index 81a69b68ef..15104625a8 100644 --- a/kpi/__manifest__.py +++ b/kpi/__manifest__.py @@ -14,8 +14,6 @@ "security/ir.model.access.csv", "views/kpi_category_views.xml", "views/kpi_history_views.xml", - "views/kpi_threshold_range_views.xml", - "views/kpi_threshold_views.xml", "views/kpi_views.xml", "views/menu.xml", "data/kpi_data.xml", @@ -23,8 +21,6 @@ "images": [ "images/kpi_definition.png", "images/kpi_computation.png", - "images/kpi_threshold.png", - "images/kpi_range.png", ], "installable": True, } diff --git a/kpi/models/__init__.py b/kpi/models/__init__.py index ae1cb46571..0244fa0d6d 100644 --- a/kpi/models/__init__.py +++ b/kpi/models/__init__.py @@ -1,7 +1,5 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from . import kpi_category -from . import kpi_threshold_range -from . import kpi_threshold from . import kpi_history from . import kpi diff --git a/kpi/models/kpi.py b/kpi/models/kpi.py index 169499befc..ada12b53d7 100644 --- a/kpi/models/kpi.py +++ b/kpi/models/kpi.py @@ -57,12 +57,11 @@ class KPI(models.Model): name = fields.Char(required=True) description = fields.Text() category_id = fields.Many2one("kpi.category", required=True) - threshold_id = fields.Many2one("kpi.threshold", required=True) periodicity = fields.Integer(default=1) periodicity_uom = fields.Selection( [ - ("minute", "Minute"), + # ("minute", "Minute"), Comentamos opciĆ³n minuto por tema performance ("hour", "Hour"), ("day", "Day"), ("week", "Week"), @@ -74,18 +73,15 @@ class KPI(models.Model): next_execution_date = fields.Datetime(readonly=True) value = fields.Float(compute="_compute_display_last_kpi_value") - color = fields.Text(compute="_compute_display_last_kpi_value") last_execution = fields.Datetime(compute="_compute_display_last_kpi_value") kpi_type = fields.Selection( [ ("python", "Python"), - ("local", "SQL - Local DB"), - ("external", "SQL - External DB"), + ("sql", "SQL - Local DB"), ], "KPI Computation Type", ) - dbsource_id = fields.Many2one("base.external.dbsource", "External DB Source") kpi_code = fields.Text( help=( "SQL code must return the result as 'value' " "(i.e. 'SELECT 5 AS value')." @@ -110,40 +106,27 @@ def _compute_display_last_kpi_value(self): if history_ids: his = obj.history_ids[0] obj.value = his.value - obj.color = his.color obj.last_execution = his.date else: obj.value = 0 - obj.color = "#FFFFFF" obj.last_execution = False def _get_kpi_value(self): self.ensure_one() kpi_value = 0 if self.kpi_code: - if self.kpi_type == "local" and is_sql_or_ddl_statement(self.kpi_code): + if self.kpi_type == "sql" and is_sql_or_ddl_statement(self.kpi_code): self.env.cr.execute(self.kpi_code) dic = self.env.cr.dictfetchall() if is_one_value(dic): kpi_value = dic[0]["value"] - elif ( - self.kpi_type == "external" - and self.dbsource_id.id - and is_sql_or_ddl_statement(self.kpi_code) - ): - dbsrc_obj = self.dbsource_id - res = dbsrc_obj.execute(self.kpi_code) - if is_one_value(res): - kpi_value = res[0]["value"] elif self.kpi_type == "python": kpi_value = safe_eval(self.kpi_code, {"self": self}) if isinstance(kpi_value, dict): res = kpi_value else: - threshold_obj = self.threshold_id res = { "value": kpi_value, - "color": threshold_obj.get_color(kpi_value), } res.update({"kpi_id": self.id}) return res @@ -159,8 +142,8 @@ def update_next_execution_date(self): for obj in self: if obj.periodicity_uom == "hour": delta = relativedelta(hours=obj.periodicity) - elif obj.periodicity_uom == "minute": - delta = relativedelta(minutes=obj.periodicity) + # elif obj.periodicity_uom == "minute": + # delta = relativedelta(minutes=obj.periodicity) elif obj.periodicity_uom == "day": delta = relativedelta(days=obj.periodicity) elif obj.periodicity_uom == "week": diff --git a/kpi/models/kpi_history.py b/kpi/models/kpi_history.py index dd3b87087b..d43ced5de9 100644 --- a/kpi/models/kpi_history.py +++ b/kpi/models/kpi_history.py @@ -23,7 +23,6 @@ class KPIHistory(models.Model): default=lambda r: fields.Datetime.now(), ) value = fields.Float(required=True, readonly=True) - color = fields.Text(required=True, readonly=True, default="#FFFFFF") company_id = fields.Many2one( "res.company", "Company", default=lambda self: self.env.company ) diff --git a/kpi/models/kpi_threshold.py b/kpi/models/kpi_threshold.py deleted file mode 100644 index 66f363bcc3..0000000000 --- a/kpi/models/kpi_threshold.py +++ /dev/null @@ -1,92 +0,0 @@ -# Copyright 2012 - Now Savoir-faire Linux -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import _, api, exceptions, fields, models - - -class KPIThreshold(models.Model): - """KPI Threshold.""" - - _name = "kpi.threshold" - _description = "KPI Threshold" - - def _compute_is_valid_threshold(self): - for obj in self: - # check if ranges overlap - # TODO: This code can be done better - obj.valid = True - for range1 in obj.range_ids: - if not range1.valid: - obj.valid = False - break - for range2 in obj.range_ids - range1: - if ( - range1.max_value >= range2.min_value - and range1.min_value <= range2.max_value - ): - obj.valid = False - break - if obj.valid: - obj.invalid_message = None - else: - obj.invalid_message = ( - "Some ranges are invalid or overlapping. " - "Please make sure your ranges do not overlap." - ) - - name = fields.Char(required=True) - range_ids = fields.Many2many( - "kpi.threshold.range", - "kpi_threshold_range_rel", - "threshold_id", - "range_id", - "Ranges", - ) - valid = fields.Boolean( - required=True, - compute="_compute_is_valid_threshold", - default=True, - ) - invalid_message = fields.Char( - string="Message", size=100, compute="_compute_is_valid_threshold" - ) - kpi_ids = fields.One2many("kpi", "threshold_id", "KPIs") - company_id = fields.Many2one( - "res.company", "Company", default=lambda self: self.env.company - ) - - @api.model - def create(self, data): - # check if ranges overlap - # TODO: This code can be done better - range_obj1 = self.env["kpi.threshold.range"] - range_obj2 = self.env["kpi.threshold.range"] - if data.get("range_ids"): - for range1 in data["range_ids"][0][2]: - range_obj1 = range_obj1.browse(range1) - for range2 in data["range_ids"][0][2]: - range_obj2 = range_obj2.browse(range2) - if ( - range_obj1.valid - and range_obj2.valid - and range_obj1.min_value < range_obj2.min_value - ): - if range_obj1.max_value > range_obj2.min_value: - raise exceptions.Warning( - _("Two of your ranges are overlapping."), - _("Make sure your ranges do not overlap!"), - ) - range_obj2 = self.env["kpi.threshold.range"] - range_obj1 = self.env["kpi.threshold.range"] - return super(KPIThreshold, self).create(data) - - def get_color(self, kpi_value): - color = "#FFFFFF" - for obj in self: - for range_obj in obj.range_ids: - if ( - range_obj.min_value <= kpi_value <= range_obj.max_value - and range_obj.valid - ): - color = range_obj.color - return color diff --git a/kpi/models/kpi_threshold_range.py b/kpi/models/kpi_threshold_range.py deleted file mode 100644 index a5d4b217b8..0000000000 --- a/kpi/models/kpi_threshold_range.py +++ /dev/null @@ -1,174 +0,0 @@ -# Copyright 2012 - Now Savoir-faire Linux -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -import re - -from odoo import api, fields, models -from odoo.tools.safe_eval import safe_eval - - -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" - - @api.model - def _selection_value_type(self): - return [ - ("static", "Fixed value"), - ("python", "Python Code"), - ("local", "SQL - Local DB"), - ("external", "SQL - External DB"), - ] - - name = fields.Char(required=True) - valid = fields.Boolean( - 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(selection="_selection_value_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(selection="_selection_value_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(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.company - ) - - 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 - - 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 - - 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 = "" diff --git a/kpi/security/ir.model.access.csv b/kpi/security/ir.model.access.csv index 0d79d817d2..3c4eefef43 100644 --- a/kpi/security/ir.model.access.csv +++ b/kpi/security/ir.model.access.csv @@ -2,11 +2,6 @@ "access_kpi_user","kpi.user","model_kpi","base.group_user",1,0,0,0 "access_kpi_history_user","kpi.history.user","model_kpi_history","base.group_user",1,0,0,0 "access_kpi_category_user","kpi.category.user","model_kpi_category","base.group_user",1,0,0,0 -"access_kpi_threshold_user","kpi.threshold.user","model_kpi_threshold","base.group_user",1,0,0,0 -"access_kpi_threshold_range_user","kpi.threshold.range.user","model_kpi_threshold_range","base.group_user",1,0,0,0 "access_kpi_manager","kpi.manager","model_kpi","kpi.group_kpi_manager",1,1,1,1 "access_kpi_history_manager","kpi.history.user","model_kpi_history","kpi.group_kpi_manager",1,1,1,1 "access_kpi_category_manager","kpi.category.manager","model_kpi_category","kpi.group_kpi_manager",1,1,1,1 -"access_kpi_threshold_manager","kpi.threshold.manager","model_kpi_threshold","kpi.group_kpi_manager",1,1,1,1 -"access_kpi_threshold_range_manager","kpi.threshold.range.manager","model_kpi_threshold_range","kpi.group_kpi_manager",1,1,1,1 -"access_base_external_dbsource_manager","base.external.dbsource.manager","base_external_dbsource.model_base_external_dbsource","base.group_user",1,1,1,1 diff --git a/kpi/security/kpi_security.xml b/kpi/security/kpi_security.xml index 82f32238b7..ded1ee0e34 100644 --- a/kpi/security/kpi_security.xml +++ b/kpi/security/kpi_security.xml @@ -19,22 +19,6 @@ name="domain_force" >['|',('company_id','=',False),('company_id', 'in', company_ids)] - - kpi_threshold_range multi-company - - - ['|',('company_id','=',False),('company_id', 'in', company_ids)] - - - kpi_threshold multi-company - - - ['|',('company_id','=',False),('company_id', 'in', company_ids)] - kpi_history multi-company diff --git a/kpi/views/kpi_history_views.xml b/kpi/views/kpi_history_views.xml index 304f1c6b8a..8b85fb792d 100644 --- a/kpi/views/kpi_history_views.xml +++ b/kpi/views/kpi_history_views.xml @@ -8,10 +8,10 @@ kpi.history - + + - @@ -24,10 +24,9 @@ - + - diff --git a/kpi/views/kpi_threshold_range_views.xml b/kpi/views/kpi_threshold_range_views.xml deleted file mode 100644 index 2a323762a5..0000000000 --- a/kpi/views/kpi_threshold_range_views.xml +++ /dev/null @@ -1,112 +0,0 @@ - - - - - - kpi.threshold.range.tree - kpi.threshold.range - - - - - - - - - - - - - kpi.threshold.range.form - kpi.threshold.range - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - Ranges - kpi.threshold.range - tree,form - - -
diff --git a/kpi/views/kpi_threshold_views.xml b/kpi/views/kpi_threshold_views.xml deleted file mode 100644 index 46eabc11d5..0000000000 --- a/kpi/views/kpi_threshold_views.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - kpi.threshold.tree - kpi.threshold - - - - - - - - - - kpi.threshold.form - kpi.threshold - -
- - - - - - - - - - - - - - - - - - -
-
-
- - Thresholds - kpi.threshold - tree,form - - -
diff --git a/kpi/views/kpi_views.xml b/kpi/views/kpi_views.xml index 6be42b6f9c..06c2570416 100644 --- a/kpi/views/kpi_views.xml +++ b/kpi/views/kpi_views.xml @@ -10,7 +10,7 @@ - + @@ -24,7 +24,6 @@ - @@ -38,7 +37,6 @@
@@ -91,7 +89,6 @@ - @@ -122,11 +119,6 @@ - @@ -142,7 +134,7 @@ KPI Dashboard kpi - kanban,form + graph,pivot,kanban,form @@ -154,7 +146,7 @@ KPI Maintenance kpi - tree,form + kanban,tree,form diff --git a/kpi/views/menu.xml b/kpi/views/menu.xml index b951090e83..654ebdb9ac 100644 --- a/kpi/views/menu.xml +++ b/kpi/views/menu.xml @@ -32,25 +32,4 @@ parent="menu_configuration_kpi" sequence="20" /> - - -