diff --git a/kpi_dashboard/README.rst b/kpi_dashboard/README.rst index ddc8b4889..db74bc809 100644 --- a/kpi_dashboard/README.rst +++ b/kpi_dashboard/README.rst @@ -45,6 +45,18 @@ Configure KPIs #. Meter: result must contain `value`, `min` and `max` #. Graph: result must contain a list on `graphs` containing `values`, `title` and `key` +#. In order to compute the KPI you can use a predefined function from a model or + use the code to directly compute it. + +Using KPI with code +~~~~~~~~~~~~~~~~~~~ + +Define the code directly on the code field. You can use: + +* `self` and `model` as the kpi element +* The script should create a variable called `result` as a dictionary that + will be stored as the value + Configure dashboards ~~~~~~~~~~~~~~~~~~~~ diff --git a/kpi_dashboard/__manifest__.py b/kpi_dashboard/__manifest__.py index c655897cc..97c155f78 100644 --- a/kpi_dashboard/__manifest__.py +++ b/kpi_dashboard/__manifest__.py @@ -20,4 +20,6 @@ "views/kpi_kpi.xml", "views/kpi_dashboard.xml", ], + "demo": ["demo/demo_dashboard.xml"], + "maintainers": ["etobella"], } diff --git a/kpi_dashboard_test/demo/demo_dashboard.xml b/kpi_dashboard/demo/demo_dashboard.xml similarity index 78% rename from kpi_dashboard_test/demo/demo_dashboard.xml rename to kpi_dashboard/demo/demo_dashboard.xml index 30fc5e861..958e15806 100644 --- a/kpi_dashboard_test/demo/demo_dashboard.xml +++ b/kpi_dashboard/demo/demo_dashboard.xml @@ -11,17 +11,21 @@ Number 01 $ - function + code number - test_demo_number + +result = {"value": 10000,"previous": 12000} + Number 02 - function + code number - test_demo_number + +result = {"value": 12000,"previous": 10000} + Meter 01 - function + code meter - test_demo_meter + +result = {"min": 0, "max": 100, "value": 90} + Meter 02 $ - function + code meter - test_demo_meter + +result = {"min": 0, "max": 100, "value": 40} + Graph - function + code graph - test_demo_graph + +result = {"graphs": [ + { + "values": [ + {"x": i, "y": i * 1000} + for i in range(1, 12) + ], + "title": "Current Year", + "key": "current", + "area": True, + "color": "ffffff", + }, + { + "values": [ + {"x": i, "y": 1000 * (12-i)} + for i in range(1, 12) + ], + "title": "Previous Year", + "key": "previous", + "area": True, + "color": "000000", + }, +]} + - Dashboard title diff --git a/kpi_dashboard/models/kpi_kpi.py b/kpi_dashboard/models/kpi_kpi.py index 41e6c5b60..275818d98 100644 --- a/kpi_dashboard/models/kpi_kpi.py +++ b/kpi_dashboard/models/kpi_kpi.py @@ -1,8 +1,11 @@ # Copyright 2020 Creu Blanca # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models +from odoo import api, fields, models, _ +from odoo.exceptions import ValidationError import ast +from odoo.tools.safe_eval import safe_eval +import re class KpiKpi(models.Model): @@ -13,7 +16,7 @@ class KpiKpi(models.Model): active = fields.Boolean(default=True) cron_id = fields.Many2one("ir.cron", readonly=True, copy=False) computation_method = fields.Selection( - [("function", "Function")], required=True + [("function", "Function"), ("code", "Code")], required=True ) value = fields.Serialized() dashboard_item_ids = fields.One2many("kpi.dashboard.item", inverse_name="kpi_id") @@ -34,6 +37,7 @@ class KpiKpi(models.Model): inverse_name='kpi_id', help="Actions that can be opened from the KPI" ) + code = fields.Text("Code") def _cron_vals(self): return { @@ -84,6 +88,33 @@ class KpiKpi(models.Model): vals["value_last_update"] = fields.Datetime.now() return super().write(vals) + def _get_code_input_dict(self): + return { + "self": self, + "model": self.browse(), + } + + def _forbidden_code(self): + return ["commit", "rollback", "getattr", "execute"] + + def _compute_value_code(self): + forbidden = self._forbidden_code() + search_terms = "(" + ("|".join(forbidden)) + ")" + if re.search(search_terms, (self.code or "").lower()): + message = ", ".join(forbidden[:-1]) or "" + if len(message) > 0: + message += _(" or ") + message += forbidden[-1] + raise ValidationError(_( + "The code cannot contain the following terms: %s." + ) % message) + results = self._get_code_input_dict() + savepoint = "kpi_formula_%s" % self.id + self.env.cr.execute("savepoint %s" % savepoint) + safe_eval(self.code or "", results, mode="exec", nocopy=True) + self.env.cr.execute("rollback to %s" % savepoint) + return results.get("result", {}) + class KpiKpiAction(models.Model): _name = 'kpi.kpi.action' diff --git a/kpi_dashboard/readme/CONFIGURE.rst b/kpi_dashboard/readme/CONFIGURE.rst index c4d0bea03..a4e700e98 100644 --- a/kpi_dashboard/readme/CONFIGURE.rst +++ b/kpi_dashboard/readme/CONFIGURE.rst @@ -8,6 +8,20 @@ Configure KPIs #. Meter: result must contain `value`, `min` and `max` #. Graph: result must contain a list on `graphs` containing `values`, `title` and `key` +#. In order to compute the KPI you can use a predefined function from a model or + use the code to directly compute it. + +Using KPI with code +~~~~~~~~~~~~~~~~~~~ + +Define the code directly on the code field. You can use `self` and `model` as the kpi element +The script should create a variable called `result` as a dictionary that +will be stored as the value. +For example, we can use:: + + result = {} + result['value'] = len(model.search([('id', '=', %s)])) + result['previous'] = len(model.search([('id', '!=', %s)])) Configure dashboards ~~~~~~~~~~~~~~~~~~~~ diff --git a/kpi_dashboard/static/description/index.html b/kpi_dashboard/static/description/index.html index 6da67fbd9..bde4304d4 100644 --- a/kpi_dashboard/static/description/index.html +++ b/kpi_dashboard/static/description/index.html @@ -3,7 +3,7 @@ - + Kpi Dashboard - - -
-

Kpi Dashboard: Test

- - -

Beta License: AGPL-3 OCA/reporting-engine Translate me on Weblate Try me on Runbot

-

This module is used in order to test KPI Dashboard

-

Table of contents

- -
-

Bug Tracker

-

Bugs are tracked on GitHub Issues. -In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us smashing it by providing a detailed and welcomed -feedback.

-

Do not contact contributors directly about support or help with technical issues.

-
-
-

Credits

-
-

Authors

-
    -
  • Creu Blanca
  • -
-
-
-

Contributors

- -
-
-

Maintainers

-

This module is maintained by the OCA.

-Odoo Community Association -

OCA, or the Odoo Community Association, is a nonprofit organization whose -mission is to support the collaborative development of Odoo features and -promote its widespread use.

-

This module is part of the OCA/reporting-engine project on GitHub.

-

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

-
-
-
- -