@ -1,33 +1,4 @@
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
<< << << < HEAD
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2010-2013 OpenERP s.a. (<http://openerp.com>).
# Copyright (C) 2014 initOS GmbH & Co. KG (<http://www.initos.com>).
# Copyright (C) 2015-Today GRAP
# Author Markus Schneider <markus.schneider at initos.com>
# @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 <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import api , fields
from openerp.models import Model
from openerp.exceptions import except_orm
== == == =
# © 2010-2013 OpenERP s.a. (<http://openerp.com>).
# © 2010-2013 OpenERP s.a. (<http://openerp.com>).
# © 2014 initOS GmbH & Co. KG (<http://www.initos.com>).
# © 2014 initOS GmbH & Co. KG (<http://www.initos.com>).
# © 2015-Today GRAP
# © 2015-Today GRAP
@ -40,13 +11,10 @@ from collections import OrderedDict
from openerp import api , fields , models
from openerp import api , fields , models
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.safe_eval import safe_eval as eval
>> >> >> > 3 ab676d . . . [ IMP ] [ 8.0 ] [ web_dashboard_tile ] Refactor ( see changes in description ) ( #476)
from openerp.tools.translate import _
from openerp.tools.translate import _
from openerp.exceptions import ValidationError , except_orm
<< << << < HEAD
class TileTile ( Model ) :
== == == =
def median ( vals ) :
def median ( vals ) :
# https://docs.python.org/3/library/statistics.html#statistics.median
# https://docs.python.org/3/library/statistics.html#statistics.median
# TODO : refactor, using statistics.median when Odoo will be available
# TODO : refactor, using statistics.median when Odoo will be available
@ -75,7 +43,7 @@ FIELD_FUNCTIONS = OrderedDict([
' help ' : _ ( " Total value of ' %s ' " ) } ) ,
' help ' : _ ( " Total value of ' %s ' " ) } ) ,
( ' avg ' , {
( ' avg ' , {
' name ' : ' Average ' ,
' name ' : ' Average ' ,
' func ' : lambda vals : sum ( vals ) / len ( vals ) ,
' func ' : lambda vals : sum ( vals ) / len ( vals ) ,
' help ' : _ ( " Minimum value of ' %s ' " ) } ) ,
' help ' : _ ( " Minimum value of ' %s ' " ) } ) ,
( ' median ' , {
( ' median ' , {
' name ' : ' Median ' ,
' name ' : ' Median ' ,
@ -89,56 +57,10 @@ FIELD_FUNCTION_SELECTION = [
class TileTile ( models . Model ) :
class TileTile ( models . Model ) :
>> >> >> > 3 ab676d . . . [ IMP ] [ 8.0 ] [ web_dashboard_tile ] Refactor ( see changes in description ) ( #476)
_name = ' tile.tile '
_name = ' tile.tile '
_description = ' Dashboard Tile '
_description = ' Dashboard Tile '
_order = ' sequence, name '
_order = ' sequence, name '
<< << << < HEAD
def median ( self , aList ) :
# 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
== == == =
def _get_eval_context ( self ) :
def _get_eval_context ( self ) :
def _context_today ( ) :
def _context_today ( ) :
return fields . Date . from_string ( fields . Date . context_today ( self ) )
return fields . Date . from_string ( fields . Date . context_today ( self ) )
@ -238,13 +160,13 @@ class TileTile(models.Model):
self . primary_function != ' count ' ,
self . primary_function != ' count ' ,
self . secondary_function and
self . secondary_function and
self . secondary_function != ' count '
self . secondary_function != ' count '
] ) :
records = model . search ( eval ( domain , eval_context ) )
] ) :
records = model . search ( eval ( domain , eval_context ) )
for f in [ ' primary_ ' , ' secondary_ ' ] :
for f in [ ' primary_ ' , ' secondary_ ' ] :
f_function = f + ' function '
f_field_id = f + ' field_id '
f_format = f + ' format '
f_value = f + ' value '
f_function = f + ' function '
f_field_id = f + ' field_id '
f_format = f + ' format '
f_value = f + ' value '
value = 0
value = 0
if self [ f_function ] == ' count ' :
if self [ f_function ] == ' count ' :
value = count
value = count
@ -268,9 +190,9 @@ class TileTile(models.Model):
' secondary_function ' , ' secondary_field_id ' )
' secondary_function ' , ' secondary_field_id ' )
def _compute_helper ( self ) :
def _compute_helper ( self ) :
for f in [ ' primary_ ' , ' secondary_ ' ] :
for f in [ ' primary_ ' , ' secondary_ ' ] :
f_function = f + ' function '
f_field_id = f + ' field_id '
f_helper = f + ' helper '
f_function = f + ' function '
f_field_id = f + ' field_id '
f_helper = f + ' helper '
self [ f_helper ] = ' '
self [ f_helper ] = ' '
field_func = FIELD_FUNCTIONS . get ( self [ f_function ] , { } )
field_func = FIELD_FUNCTIONS . get ( self [ f_function ] , { } )
help = field_func . get ( ' help ' , False )
help = field_func . get ( ' help ' , False )
@ -285,15 +207,13 @@ class TileTile(models.Model):
def _compute_active ( self ) :
def _compute_active ( self ) :
ima = self . env [ ' ir.model.access ' ]
ima = self . env [ ' ir.model.access ' ]
self . active = ima . check ( self . model_id . model , ' read ' , False )
self . active = ima . check ( self . model_id . model , ' read ' , False )
>> >> >> > 3 ab676d . . . [ IMP ] [ 8.0 ] [ web_dashboard_tile ] Refactor ( see changes in description ) ( #476)
def _search_active ( self , operator , value ) :
def _search_active ( self , operator , value ) :
cr = self . env . cr
cr = self . env . cr
if operator != ' = ' :
if operator != ' = ' :
raise except_orm (
raise except_orm (
' Unimplemented Feature ' ,
' Search on Active field disabled. ' )
ima_obj = self . env [ ' ir.model.access ' ]
_ ( ' Unimplemented Feature. Search on Active field disabled. ' ) )
ima = self . env [ ' ir.model.access ' ]
ids = [ ]
ids = [ ]
cr . execute ( """
cr . execute ( """
SELECT tt . id , im . model
SELECT tt . id , im . model
@ -301,40 +221,10 @@ class TileTile(models.Model):
INNER JOIN ir_model im
INNER JOIN ir_model im
ON tt . model_id = im . id """ )
ON tt . model_id = im . id """ )
for result in cr . fetchall ( ) :
for result in cr . fetchall ( ) :
if ( ima_obj . check ( result [ 1 ] , ' read ' , False ) == value ) :
if ( ima . check ( result [ 1 ] , ' read ' , False ) == value ) :
ids . append ( result [ 0 ] )
ids . append ( result [ 0 ] )
return [ ( ' id ' , ' in ' , ids ) ]
return [ ( ' id ' , ' in ' , ids ) ]
<< << << < HEAD
# 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 ' )
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 = [
( ' min ' , ' Minimum ' ) ,
( ' max ' , ' Maximum ' ) ,
( ' sum ' , ' Sum ' ) ,
( ' avg ' , ' Average ' ) ,
( ' median ' , ' Median ' ) ,
] , string = ' Function ' )
field_id = fields . Many2one (
comodel_name = ' 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 )
== == == =
# Constraints and onchanges
# Constraints and onchanges
@api.one
@api.one
@api.constrains ( ' model_id ' , ' primary_field_id ' , ' secondary_field_id ' )
@api.constrains ( ' model_id ' , ' primary_field_id ' , ' secondary_field_id ' )
@ -344,9 +234,9 @@ class TileTile(models.Model):
self . primary_field_id . model_id . id != self . model_id . id ,
self . primary_field_id . model_id . id != self . model_id . id ,
self . secondary_field_id and
self . secondary_field_id and
self . secondary_field_id . model_id . id != self . model_id . id
self . secondary_field_id . model_id . id != self . model_id . id
] ) :
raise ValidationError (
_ ( " Please select a field from the selected model. " ) )
] ) :
raise ValidationError (
_ ( " Please select a field from the selected model. " ) )
@api.onchange ( ' model_id ' )
@api.onchange ( ' model_id ' )
def _onchange_model_id ( self ) :
def _onchange_model_id ( self ) :
@ -359,34 +249,8 @@ class TileTile(models.Model):
self . primary_field_id = False
self . primary_field_id = False
if self . secondary_function in [ False , ' count ' ] :
if self . secondary_function in [ False , ' count ' ] :
self . secondary_field_id = False
self . secondary_field_id = False
>> >> >> > 3 ab676d . . . [ IMP ] [ 8.0 ] [ web_dashboard_tile ] Refactor ( see changes in description ) ( #476)
# 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
@api.multi
def open_link ( self ) :
def open_link ( self ) :
res = {
res = {
@ -403,8 +267,7 @@ class TileTile(models.Model):
}
}
if self . action_id :
if self . action_id :
res . update ( self . action_id . read (
res . update ( self . action_id . read (
[ ' view_type ' , ' view_mode ' , ' view_id ' , ' type ' ] ) [ 0 ] )
# FIXME: restore original Domain + Filter would be better
[ ' view_type ' , ' view_mode ' , ' type ' ] ) [ 0 ] )
return res
return res
@api.model
@api.model