From 6fad2c3f1fb5a555f06475894a00efb993ba2681 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Todorovich?= Date: Fri, 12 Aug 2016 01:45:30 -0300 Subject: [PATCH 1/7] Code cleanup, remove unused field 'active', avoid calculating fields in form view. Support relative time filters and context_today. Updated version and headers --- web_dashboard_tile/README.rst | 5 +- web_dashboard_tile/__init__.py | 29 +-- web_dashboard_tile/__openerp__.py | 35 +-- web_dashboard_tile/models/__init__.py | 24 +- web_dashboard_tile/models/tile_tile.py | 224 ++++++++---------- web_dashboard_tile/static/src/js/custom_js.js | 6 +- 6 files changed, 125 insertions(+), 198 deletions(-) diff --git a/web_dashboard_tile/README.rst b/web_dashboard_tile/README.rst index 21d96ab0..8aa70f02 100644 --- a/web_dashboard_tile/README.rst +++ b/web_dashboard_tile/README.rst @@ -17,20 +17,21 @@ Usage ===== * Dashboad sample, displaying Sale Orders to invoice: + .. image:: ./static/src/img/screenshot_dashboard.png + * Tree view displayed when user click on the tile: + .. image:: ./static/src/img/screenshot_action_click.png Kown issues/limits ================== * can not edit tile from dashboard (color, sequence, function, ...); * context are ignored; -* date filter can not be relative; * combine domain of menue and filter so can not restore origin filter; possible future improvments =========================== -* support context_today; * add icons; * support client side action (like inbox); diff --git a/web_dashboard_tile/__init__.py b/web_dashboard_tile/__init__.py index 35d8f47b..9bc26bb1 100644 --- a/web_dashboard_tile/__init__.py +++ b/web_dashboard_tile/__init__.py @@ -1,26 +1,7 @@ # -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2010-2013 OpenERP s.a. (). -# Copyright (C) 2014 initOS GmbH & Co. KG (). -# Copyright (C) 2015-Today GRAP -# Author Markus Schneider -# @author Sylvain LE GAL (https://twitter.com/legalsylvain) -# -# 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 . -# -############################################################################## +# © 2010-2013 OpenERP s.a. (). +# © 2014 initOS GmbH & Co. KG (). +# © 2015-Today GRAP +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html -from . import models +from . import models \ No newline at end of file diff --git a/web_dashboard_tile/__openerp__.py b/web_dashboard_tile/__openerp__.py index c401c30a..c438143a 100644 --- a/web_dashboard_tile/__openerp__.py +++ b/web_dashboard_tile/__openerp__.py @@ -1,38 +1,27 @@ # -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2010-2013 OpenERP s.a. (). -# Copyright (C) 2014 initOS GmbH & Co. KG (). -# Author Markus Schneider -# -# 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 . -# -############################################################################## +# © 2010-2013 OpenERP s.a. (). +# © 2014 initOS GmbH & Co. KG (). +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html { "name": "Dashboard Tile", "summary": "Add Tiles to Dashboard", - "version": "8.0.1.0.0", + "version": "8.0.1.1.0", "depends": [ 'web', 'board', 'mail', 'web_widget_color', ], - 'author': "initOS GmbH & Co. KG,GRAP,Odoo Community Association (OCA)", + 'author': 'initOS GmbH & Co. KG, ' + 'GRAP, ' + 'Odoo Community Association (OCA)', "category": "web", 'license': 'AGPL-3', + 'contributors': [ + 'initOS GmbH & Co. KG', + 'GRAP', + 'Iván Todorovich ' + ], 'data': [ 'views/tile.xml', 'views/templates.xml', diff --git a/web_dashboard_tile/models/__init__.py b/web_dashboard_tile/models/__init__.py index fc23e732..97fec216 100644 --- a/web_dashboard_tile/models/__init__.py +++ b/web_dashboard_tile/models/__init__.py @@ -1,23 +1,7 @@ # -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2015-Today GRAP -# @author Sylvain LE GAL (https://twitter.com/legalsylvain) -# -# 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 . -# -############################################################################## +# © 2010-2013 OpenERP s.a. (). +# © 2014 initOS GmbH & Co. KG (). +# © 2015-Today GRAP +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html from . import tile_tile diff --git a/web_dashboard_tile/models/tile_tile.py b/web_dashboard_tile/models/tile_tile.py index 4743afcb..e011105c 100644 --- a/web_dashboard_tile/models/tile_tile.py +++ b/web_dashboard_tile/models/tile_tile.py @@ -1,35 +1,20 @@ # -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2010-2013 OpenERP s.a. (). -# Copyright (C) 2014 initOS GmbH & Co. KG (). -# Copyright (C) 2015-Today GRAP -# Author Markus Schneider -# @author Sylvain LE GAL (https://twitter.com/legalsylvain) -# -# 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 . -# -############################################################################## - -from openerp import api, fields -from openerp.models import Model -from openerp.exceptions import except_orm +# © 2010-2013 OpenERP s.a. (). +# © 2014 initOS GmbH & Co. KG (). +# © 2015-Today GRAP +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html + +import datetime +import time +from dateutil.relativedelta import relativedelta + +from openerp import api, fields, models +from openerp.tools.safe_eval import safe_eval as eval from openerp.tools.translate import _ +from openerp.exceptions import ValidationError -class TileTile(Model): +class TileTile(models.Model): _name = 'tile.tile' _order = 'sequence, name' @@ -37,77 +22,38 @@ class TileTile(Model): # https://docs.python.org/3/library/statistics.html#statistics.median # TODO : refactor, using statistics.median when Odoo will be available # in Python 3.4 - even = (0 if len(aList) % 2 else 1) + 1 - half = (len(aList) - 1) / 2 - return sum(sorted(aList)[half:half + even]) / float(even) - - def _get_tile_info(self): - ima_obj = self.env['ir.model.access'] - res = {} - for r in self: - r.active = False - r.count = 0 - r.computed_value = 0 - r.helper = '' - if ima_obj.check(r.model_id.model, 'read', False): - # Compute count item - model = self.env[r.model_id.model] - r.count = model.search_count(eval(r.domain)) - r.active = True - - # Compute datas for field_id depending of field_function - if r.field_function and r.field_id and r.count != 0: - records = model.search(eval(r.domain)) - vals = [x[r.field_id.name] for x in records] - desc = r.field_id.field_description - if r.field_function == 'min': - r.computed_value = min(vals) - r.helper = _("Minimum value of '%s'") % desc - elif r.field_function == 'max': - r.computed_value = max(vals) - r.helper = _("Maximum value of '%s'") % desc - elif r.field_function == 'sum': - r.computed_value = sum(vals) - r.helper = _("Total value of '%s'") % desc - elif r.field_function == 'avg': - r.computed_value = sum(vals) / len(vals) - r.helper = _("Average value of '%s'") % desc - elif r.field_function == 'median': - r.computed_value = self.median(vals) - r.helper = _("Median value of '%s'") % desc - return res + even = (0 if len(aList) % 2 else 1) + 1 + half = (len(aList) - 1) / 2 + return sum(sorted(aList)[half:half + even]) / float(even) - def _search_active(self, operator, value): - cr = self.env.cr - if operator != '=': - raise except_orm( - 'Unimplemented Feature', - 'Search on Active field disabled.') - ima_obj = self.env['ir.model.access'] - ids = [] - cr.execute(""" - SELECT tt.id, im.model - FROM tile_tile tt - INNER JOIN ir_model im - ON tt.model_id = im.id""") - for result in cr.fetchall(): - if (ima_obj.check(result[1], 'read', False) == value): - ids.append(result[0]) - return [('id', 'in', ids)] + def _get_eval_context(self): + def _context_today(): + return fields.Date.from_string(fields.Date.context_today(self)) + context = self.env.context.copy() + context.update({ + 'time': time, + 'datetime': datetime, + 'relativedelta': relativedelta, + 'context_today': _context_today, + 'current_date': time.strftime('%Y-%m-%d'), + }) + return context # Column Section name = fields.Char(required=True) - model_id = fields.Many2one( - comodel_name='ir.model', string='Model', required=True) - user_id = fields.Many2one( - comodel_name='res.users', string='User') + sequence = fields.Integer(default=0, required=True) + user_id = fields.Many2one('res.users', 'User') + background_color = fields.Char(default='#0E6C7E', oldname='color') + font_color = fields.Char(default='#FFFFFF') + + model_id = fields.Many2one('ir.model', 'Model', required=True) domain = fields.Text(default='[]') - action_id = fields.Many2one( - comodel_name='ir.actions.act_window', string='Action') - count = fields.Integer(compute='_get_tile_info') - computed_value = fields.Float(compute='_get_tile_info') - helper = fields.Char(compute='_get_tile_info') - field_function = fields.Selection(selection=[ + action_id = fields.Many2one('ir.actions.act_window', 'Action') + + count = fields.Integer(compute='_compute_data') + computed_value = fields.Float(compute='_compute_data') + + field_function = fields.Selection([ ('min', 'Minimum'), ('max', 'Maximum'), ('sum', 'Sum'), @@ -115,41 +61,67 @@ class TileTile(Model): ('median', 'Median'), ], string='Function') field_id = fields.Many2one( - comodel_name='ir.model.fields', string='Field', + 'ir.model.fields', + string='Field', domain="[('model_id', '=', model_id)," " ('ttype', 'in', ['float', 'int'])]") - active = fields.Boolean( - compute='_get_tile_info', readonly=True, search='_search_active') - background_color = fields.Char(default='#0E6C7E', oldname='color') - font_color = fields.Char(default='#FFFFFF') - sequence = fields.Integer(default=0, required=True) + helper = fields.Char(compute='_compute_function_helper') + + @api.one + def _compute_data(self): + self.count = 0 + self.computed_value = 0 + # Compute count item + model = self.env[self.model_id.model] + eval_context = self._get_eval_context() + self.count = model.search_count(eval(self.domain, eval_context)) + # Compute datas for field_id depending of field_function + if self.field_function and self.field_id and self.count != 0: + records = model.search(eval(self.domain, eval_context)) + vals = [x[self.field_id.name] for x in records] + if self.field_function == 'min': + self.computed_value = min(vals) + elif self.field_function == 'max': + self.computed_value = max(vals) + elif self.field_function == 'sum': + self.computed_value = sum(vals) + elif self.field_function == 'avg': + self.computed_value = sum(vals) / len(vals) + elif self.field_function == 'median': + self.computed_value = self.median(vals) + + @api.one + @api.onchange('field_function', 'field_id') + def _compute_function_helper(self): + self.helper = '' + if self.field_function and self.field_id: + desc = self.field_id.field_description + helpers = { + 'min': "Minimum value of '%s'", + 'max': "Maximum value of '%s'", + 'sum': "Total value of '%s'", + 'avg': "Average value of '%s'", + 'median': "Median value of '%s'", + } + self.helper = _(helpers.get(self.field_function, '')) % desc + + # Constraints and onchanges + @api.one + @api.constrains('model_id', 'field_id') + def _check_model_id_field_id(self): + if self.field_id and self.field_id.model_id.id != self.model_id.id: + raise ValidationError( + _("Please select a field of the selected model.")) + + @api.one + @api.constrains('field_id', 'field_function') + def _check_field_id_field_function(self): + if self.field_id and not self.field_function or\ + self.field_function and not self.field_id: + raise ValidationError( + _("Please set both: 'Field' and 'Function'.")) - # Constraint Section - def _check_model_id_field_id(self, cr, uid, ids, context=None): - for t in self.browse(cr, uid, ids, context=context): - if t.field_id and t.field_id.model_id.id != t.model_id.id: - return False - return True - - def _check_field_id_field_function(self, cr, uid, ids, context=None): - for t in self.browse(cr, uid, ids, context=context): - if t.field_id and not t.field_function or\ - t.field_function and not t.field_id: - return False - return True - - _constraints = [ - ( - _check_model_id_field_id, - "Error ! Please select a field of the selected model.", - ['model_id', 'field_id']), - ( - _check_field_id_field_function, - "Error ! Please set both fields: 'Field' and 'Function'.", - ['field_id', 'field_function']), - ] - - # View / action Section + # Action methods @api.multi def open_link(self): res = { diff --git a/web_dashboard_tile/static/src/js/custom_js.js b/web_dashboard_tile/static/src/js/custom_js.js index c192ef29..df16cee7 100644 --- a/web_dashboard_tile/static/src/js/custom_js.js +++ b/web_dashboard_tile/static/src/js/custom_js.js @@ -35,13 +35,13 @@ _.mixin({ var self = this; this.$('#add_dashboard_tile').on('click', this, function (){ self.save_tile(); - }) + }); }, render_data: function(dashboard_choices){ var selection = instance.web.qweb.render( "SearchView.addtodashboard.selection", { selections: dashboard_choices}); - this.$("form input").before(selection) + this.$("form input").before(selection); }, save_tile: function () { var self = this; @@ -97,4 +97,4 @@ _.mixin({ } }); -} +}; From a5cd01e6ecfa40fe4936a9d4e098d1502bf06dc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Todorovich?= Date: Fri, 12 Aug 2016 11:50:15 -0300 Subject: [PATCH 2/7] FIX: Non-db action dictionaries should provide either multiple view modes or a single view mode and an optional view id. --- web_dashboard_tile/models/tile_tile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_dashboard_tile/models/tile_tile.py b/web_dashboard_tile/models/tile_tile.py index e011105c..1a5c617b 100644 --- a/web_dashboard_tile/models/tile_tile.py +++ b/web_dashboard_tile/models/tile_tile.py @@ -138,7 +138,7 @@ class TileTile(models.Model): } if self.action_id: res.update(self.action_id.read( - ['view_type', 'view_mode', 'view_id', 'type'])[0]) + ['view_type', 'view_mode', 'type'])[0]) # FIXME: restore original Domain + Filter would be better return res From 4370d4217efa8c040e4bd7e2074dda68e0f0384d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Todorovich?= Date: Fri, 12 Aug 2016 12:08:38 -0300 Subject: [PATCH 3/7] Show/Hide Add tile form under 'Add to Dashboard' menu --- web_dashboard_tile/static/src/css/tile.css | 26 ++++++++++++------- .../static/src/xml/custom_xml.xml | 2 +- web_dashboard_tile/views/tile.xml | 2 +- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/web_dashboard_tile/static/src/css/tile.css b/web_dashboard_tile/static/src/css/tile.css index 1d057cee..9bfdf0ae 100644 --- a/web_dashboard_tile/static/src/css/tile.css +++ b/web_dashboard_tile/static/src/css/tile.css @@ -1,24 +1,24 @@ -.openerp .oe_kanban_view .oe_dashbaord_tile{ +.openerp .oe_kanban_view .oe_dashboard_tile { width: 150px; height: 150px; border: 0; border-radius: 0; } -.openerp .oe_kanban_view .oe_dashbaord_tile .tile_label, -.openerp .oe_kanban_view .oe_dashbaord_tile .tile_count_without_computed_value, -.openerp .oe_kanban_view .oe_dashbaord_tile .tile_count_with_computed_value, -.openerp .oe_kanban_view .oe_dashbaord_tile .tile_computed_value { +.openerp .oe_kanban_view .oe_dashboard_tile .tile_label, +.openerp .oe_kanban_view .oe_dashboard_tile .tile_count_without_computed_value, +.openerp .oe_kanban_view .oe_dashboard_tile .tile_count_with_computed_value, +.openerp .oe_kanban_view .oe_dashboard_tile .tile_computed_value { width: 140px; text-align: center; } -.openerp .oe_kanban_view .oe_dashbaord_tile .tile_label{ +.openerp .oe_kanban_view .oe_dashboard_tile .tile_label{ padding: 5px; font-size: 15px; } -.openerp .oe_kanban_view .oe_dashbaord_tile .tile_count_without_computed_value{ +.openerp .oe_kanban_view .oe_dashboard_tile .tile_count_without_computed_value{ font-size: 52px; font-weight: bold; position: absolute; @@ -26,7 +26,7 @@ bottom: 5px; } -.openerp .oe_kanban_view .oe_dashbaord_tile .tile_count_with_computed_value{ +.openerp .oe_kanban_view .oe_dashboard_tile .tile_count_with_computed_value{ font-size: 38px; font-weight: bold; position: absolute; @@ -34,7 +34,7 @@ bottom: 30px; } -.openerp .oe_kanban_view .oe_dashbaord_tile .tile_computed_value{ +.openerp .oe_kanban_view .oe_dashboard_tile .tile_computed_value{ font-size: 18px; font-weight: bold; position: absolute; @@ -42,3 +42,11 @@ bottom: 5px; font-style: italic; } + +.openerp .oe_searchview_drawer .oe_searchview_dashboard .oe_dashboard_tile_form { + display: none; +} + +.openerp .oe_searchview_drawer .oe_opened .oe_dashboard_tile_form { + display: block; +} \ No newline at end of file diff --git a/web_dashboard_tile/static/src/xml/custom_xml.xml b/web_dashboard_tile/static/src/xml/custom_xml.xml index 3e0c2316..38a0e3e9 100644 --- a/web_dashboard_tile/static/src/xml/custom_xml.xml +++ b/web_dashboard_tile/static/src/xml/custom_xml.xml @@ -2,7 +2,7 @@ -
+
diff --git a/web_dashboard_tile/views/tile.xml b/web_dashboard_tile/views/tile.xml index 36411ad8..5eef1756 100644 --- a/web_dashboard_tile/views/tile.xml +++ b/web_dashboard_tile/views/tile.xml @@ -61,7 +61,7 @@ -
+
From 757a1812b5ddcd2b13a67706f9b749003f9c4015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Todorovich?= Date: Thu, 18 Aug 2016 12:19:40 -0300 Subject: [PATCH 4/7] Add new line to __init__.py --- web_dashboard_tile/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_dashboard_tile/__init__.py b/web_dashboard_tile/__init__.py index 9bc26bb1..1d098b58 100644 --- a/web_dashboard_tile/__init__.py +++ b/web_dashboard_tile/__init__.py @@ -4,4 +4,4 @@ # © 2015-Today GRAP # License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html -from . import models \ No newline at end of file +from . import models From d96d8f4542f0cebcbfaad0051275c0baaf43d0c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Todorovich?= Date: Thu, 18 Aug 2016 12:29:58 -0300 Subject: [PATCH 5/7] Fixes --- web_dashboard_tile/README.rst | 2 ++ web_dashboard_tile/models/tile_tile.py | 9 ++++----- web_dashboard_tile/static/src/css/tile.css | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/web_dashboard_tile/README.rst b/web_dashboard_tile/README.rst index 8aa70f02..b6ee2989 100644 --- a/web_dashboard_tile/README.rst +++ b/web_dashboard_tile/README.rst @@ -34,6 +34,7 @@ possible future improvments =========================== * add icons; * support client side action (like inbox); +* restore original Domain + Filter when an action is set. Bug Tracker =========== @@ -52,6 +53,7 @@ Contributors * Markus Schneider * Sylvain Le Gal (https://twitter.com/legalsylvain) +* Iván Todorovich Maintainer ---------- diff --git a/web_dashboard_tile/models/tile_tile.py b/web_dashboard_tile/models/tile_tile.py index 1a5c617b..7dc72758 100644 --- a/web_dashboard_tile/models/tile_tile.py +++ b/web_dashboard_tile/models/tile_tile.py @@ -35,7 +35,7 @@ class TileTile(models.Model): 'datetime': datetime, 'relativedelta': relativedelta, 'context_today': _context_today, - 'current_date': time.strftime('%Y-%m-%d'), + 'current_date': fields.Date.today(), }) return context @@ -111,13 +111,13 @@ class TileTile(models.Model): def _check_model_id_field_id(self): if self.field_id and self.field_id.model_id.id != self.model_id.id: raise ValidationError( - _("Please select a field of the selected model.")) + _("Please select a field from the selected model.")) @api.one @api.constrains('field_id', 'field_function') def _check_field_id_field_function(self): - if self.field_id and not self.field_function or\ - self.field_function and not self.field_id: + validations = self.field_id, self.field_function + if any(validations) and not all(validations): raise ValidationError( _("Please set both: 'Field' and 'Function'.")) @@ -139,7 +139,6 @@ class TileTile(models.Model): if self.action_id: res.update(self.action_id.read( ['view_type', 'view_mode', 'type'])[0]) - # FIXME: restore original Domain + Filter would be better return res @api.model diff --git a/web_dashboard_tile/static/src/css/tile.css b/web_dashboard_tile/static/src/css/tile.css index 9bfdf0ae..5649c188 100644 --- a/web_dashboard_tile/static/src/css/tile.css +++ b/web_dashboard_tile/static/src/css/tile.css @@ -49,4 +49,4 @@ .openerp .oe_searchview_drawer .oe_opened .oe_dashboard_tile_form { display: block; -} \ No newline at end of file +} From 5dfe52840452953c71b4e4fd4236de173ad3213d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Todorovich?= Date: Mon, 22 Aug 2016 10:40:06 -0300 Subject: [PATCH 6/7] bring back active field --- web_dashboard_tile/models/tile_tile.py | 66 ++++++++++++++++++-------- 1 file changed, 47 insertions(+), 19 deletions(-) diff --git a/web_dashboard_tile/models/tile_tile.py b/web_dashboard_tile/models/tile_tile.py index 7dc72758..eaadae95 100644 --- a/web_dashboard_tile/models/tile_tile.py +++ b/web_dashboard_tile/models/tile_tile.py @@ -11,7 +11,7 @@ from dateutil.relativedelta import relativedelta from openerp import api, fields, models from openerp.tools.safe_eval import safe_eval as eval from openerp.tools.translate import _ -from openerp.exceptions import ValidationError +from openerp.exceptions import ValidationError, except_orm class TileTile(models.Model): @@ -67,28 +67,34 @@ class TileTile(models.Model): " ('ttype', 'in', ['float', 'int'])]") helper = fields.Char(compute='_compute_function_helper') + active = fields.Boolean( + compute='_compute_active', + search='_search_active', + readonly=True) + @api.one def _compute_data(self): self.count = 0 self.computed_value = 0 - # Compute count item - model = self.env[self.model_id.model] - eval_context = self._get_eval_context() - self.count = model.search_count(eval(self.domain, eval_context)) - # Compute datas for field_id depending of field_function - if self.field_function and self.field_id and self.count != 0: - records = model.search(eval(self.domain, eval_context)) - vals = [x[self.field_id.name] for x in records] - if self.field_function == 'min': - self.computed_value = min(vals) - elif self.field_function == 'max': - self.computed_value = max(vals) - elif self.field_function == 'sum': - self.computed_value = sum(vals) - elif self.field_function == 'avg': - self.computed_value = sum(vals) / len(vals) - elif self.field_function == 'median': - self.computed_value = self.median(vals) + if self.active: + # Compute count item + model = self.env[self.model_id.model] + eval_context = self._get_eval_context() + self.count = model.search_count(eval(self.domain, eval_context)) + # Compute datas for field_id depending of field_function + if self.field_function and self.field_id and self.count != 0: + records = model.search(eval(self.domain, eval_context)) + vals = [x[self.field_id.name] for x in records] + if self.field_function == 'min': + self.computed_value = min(vals) + elif self.field_function == 'max': + self.computed_value = max(vals) + elif self.field_function == 'sum': + self.computed_value = sum(vals) + elif self.field_function == 'avg': + self.computed_value = sum(vals) / len(vals) + elif self.field_function == 'median': + self.computed_value = self.median(vals) @api.one @api.onchange('field_function', 'field_id') @@ -105,6 +111,28 @@ class TileTile(models.Model): } self.helper = _(helpers.get(self.field_function, '')) % desc + @api.one + def _compute_active(self): + ima = self.env['ir.model.access'] + self.active = ima.check(self.model_id.model, 'read', False) + + def _search_active(self, operator, value): + cr = self.env.cr + if operator != '=': + raise except_orm( + _('Unimplemented Feature. Search on Active field disabled.')) + ima = self.env['ir.model.access'] + ids = [] + cr.execute(""" + SELECT tt.id, im.model + FROM tile_tile tt + INNER JOIN ir_model im + ON tt.model_id = im.id""") + for result in cr.fetchall(): + if (ima.check(result[1], 'read', False) == value): + ids.append(result[0]) + return [('id', 'in', ids)] + # Constraints and onchanges @api.one @api.constrains('model_id', 'field_id') From 1346b74e1f2886954f136c11dfc38af922d1ba95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Todorovich?= Date: Mon, 29 Aug 2016 19:32:54 -0300 Subject: [PATCH 7/7] Update README --- web_dashboard_tile/README.rst | 43 ++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/web_dashboard_tile/README.rst b/web_dashboard_tile/README.rst index b6ee2989..821a4c70 100644 --- a/web_dashboard_tile/README.rst +++ b/web_dashboard_tile/README.rst @@ -1,17 +1,17 @@ -Add Tiles to Dashboard -====================== +Dashboard Tiles +=============== -module to give you a dashboard where you can configure tile from any view +Adds a dashboard where you can configure tiles from any view and add them as short cut. * Tile can be: - * displayed only for a user; - * global for all users (In that case, some tiles will be hidden if - the current user doesn't have access to the given model); -* The tile displays items count of a given model restricted to a given domain; -* Optionnaly, the tile can display the result of a function of a field; - * Function is one of sum/avg/min/max/median; - * Field must be integer or float; + * Displayed only for a user. + * Global for all users (In that case, some tiles will be hidden if + the current user doesn't have access to the given model). +* The tile displays items count of a given model restricted to a given domain. +* Optionally, the tile can display the result of a function of a field + * Function is one of sum/avg/min/max/median. + * Field must be integer or float. Usage ===== @@ -24,17 +24,18 @@ Usage .. image:: ./static/src/img/screenshot_action_click.png -Kown issues/limits -================== -* can not edit tile from dashboard (color, sequence, function, ...); -* context are ignored; -* combine domain of menue and filter so can not restore origin filter; - -possible future improvments -=========================== -* add icons; -* support client side action (like inbox); -* restore original Domain + Filter when an action is set. +Known issues +============ +* Can not edit tile from dashboard (color, sequence, function, ...). +* Original context is ignored. +* Original domain and filter are not restored. +* To preserve a relative date domain, you have to manually edit the tile's domain from `Configuration > User Interface > Dashboard Tile`. You can use the same variables available in filters (`uid`, `context_today()`, `current_date`, `time`, `datetime`, `relativedelta`). + +Roadmap +======= +* Add icons. +* Support client side action (like inbox). +* Restore original Domain + Filter when an action is set. Bug Tracker ===========