From c9464672dedca5697810f67235c8f07b60fffd06 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Thu, 14 Feb 2013 09:20:38 +0100 Subject: [PATCH 0001/1311] [ADD] inital hr_goal files bzr revid: mat@openerp.com-20130214082038-4b62m2rlugszfpiv --- addons/hr_goal/__init__.py | 1 + addons/hr_goal/__openerp__.py | 39 +++++++++ addons/hr_goal/goal.py | 129 +++++++++++++++++++++++++++++ addons/hr_goal/view/criteria.xml | 17 ++++ addons/hr_goal/view/definition.xml | 15 ++++ addons/hr_goal/view/instance.xml | 13 +++ addons/hr_goal/view/preset.xml | 13 +++ 7 files changed, 227 insertions(+) create mode 100644 addons/hr_goal/__init__.py create mode 100644 addons/hr_goal/__openerp__.py create mode 100644 addons/hr_goal/goal.py create mode 100644 addons/hr_goal/view/criteria.xml create mode 100644 addons/hr_goal/view/definition.xml create mode 100644 addons/hr_goal/view/instance.xml create mode 100644 addons/hr_goal/view/preset.xml diff --git a/addons/hr_goal/__init__.py b/addons/hr_goal/__init__.py new file mode 100644 index 00000000000..7c0f769d0b0 --- /dev/null +++ b/addons/hr_goal/__init__.py @@ -0,0 +1 @@ +import goal \ No newline at end of file diff --git a/addons/hr_goal/__openerp__.py b/addons/hr_goal/__openerp__.py new file mode 100644 index 00000000000..187fb15b121 --- /dev/null +++ b/addons/hr_goal/__openerp__.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2013 Tiny SPRL (). +# +# 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 . +# +############################################################################## +{ + 'name': 'Goal', + 'version': '1.0', + 'author': 'OpenERP SA', + 'category': 'Human Resources', + 'depends': ['base', 'hr'], + 'description': """HR Goal definition + +Defined goals to users""", + + 'data': [ + 'view/criteria.xml', + 'view/definition.xml', + 'view/instance.xml', + 'view/preset.xml', + ], + 'installable': True, + 'application': True, +} diff --git a/addons/hr_goal/goal.py b/addons/hr_goal/goal.py new file mode 100644 index 00000000000..19551b2c24c --- /dev/null +++ b/addons/hr_goal/goal.py @@ -0,0 +1,129 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2013 Tiny SPRL (). +# +# 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.osv import fields, osv + +from datetime import date + +class hr_goal_criteria(osv.Model): + """Goal criteria definition + + A criteria defining a way to set an objective and evaluate it + Each module wanting to be able to set goals to the users needs to create + a new goal_criteria + """ + _name = 'hr.goal.criteria' + _description = 'Goal criteria' + + def get_evaluated_field_value(self, cr, user, ids, vals, context=None): + """Return the type of the 'evaluated_field' field""" + for item in self.browse(cr, user, ids, context=context): + return item.evaluated_field.ttype + + _columns = { + 'name': fields.char('Name'), + 'description': fields.char('Description'), + 'evaluated_field': fields.many2one('ir.model.fields', + string='Evaluated field'), + } + +class hr_goal(osv.Model): + """Goal instance for a user + + An individual goal for a user on a specified time period + """ + + _name = 'hr.goal.instance' + _description = 'Goal instance' + + _columns = { + 'criteria_id' : fields.many2one('hr.goal.criteria', string='Criteria'), + 'user_id' : fields.many2one('res.users', string='User'), + 'start_date' : fields.date('Start date'), + 'end_date' : fields.date('End date'), + 'to_reach' : fields.char('To reach'), + 'current' : fields.char('Current'), + } + + def _compute_default_end_date(self, cr, uid, ids, field_name, arg, + context=None): + hr_goal = self.browse(cr, uid, ids, context) + if hr_goal.start_date: + return hr_goal.start_date + datetime.timedelta(days=1) + else: + return fields.date.today() + datetime.timedelta(days=1) + + _defaults = { + 'start_date': fields.date.today, + 'end_date': _compute_default_end_date, + 'current': "", + } + + + +class hr_goal_definition(osv.Model): + """Goal definition + + Predifined goal for 'hr_goal_preset' + """ + + _name = 'hr.goal.definition' + _description = 'Goal definition' + + _columns = { + 'criteria_id' : fields.many2one('hr.goal.criteria', + string='Criteria'), + 'default_to_reach' : fields.char('Default value to reach'), + } + + +class hr_goal_preset(osv.Model): + """Goal preset + + Set of predifined goals to be able to automate goal settings or + quickly apply several goals manually + + If both 'group_id' and 'period' are defined, the set will be assigned to the + group for each period (eg: every 1st of each month if 'monthly' is selected) + """ + + _name = 'hr.goal.preset' + _description = 'Goal preset' + + _columns = { + 'name' : fields.char('Set name'), + 'definition_id' : fields.many2many('hr.goal.definition', + string='Definition'), + 'group_id' : fields.many2one('res.groups', string='Group'), + 'period' : fields.selection( + ( + ('n','No automatic assigment'), + ('d','Daily'), + ('m','Monthly'), + ('y', 'Yearly') + ), + string='Period', + description='Period of automatic goal assigment, ignored if no group is selected'), + } + + _defaults = { + 'period': 'n', + } \ No newline at end of file diff --git a/addons/hr_goal/view/criteria.xml b/addons/hr_goal/view/criteria.xml new file mode 100644 index 00000000000..8e47f656c10 --- /dev/null +++ b/addons/hr_goal/view/criteria.xml @@ -0,0 +1,17 @@ + + + + + Criterias + hr.goal.criteria + tree,form + + + + + + + + + \ No newline at end of file diff --git a/addons/hr_goal/view/definition.xml b/addons/hr_goal/view/definition.xml new file mode 100644 index 00000000000..47241f6710b --- /dev/null +++ b/addons/hr_goal/view/definition.xml @@ -0,0 +1,15 @@ + + + + + Goal definitions + hr.goal.definition + tree,form + + + + + + + \ No newline at end of file diff --git a/addons/hr_goal/view/instance.xml b/addons/hr_goal/view/instance.xml new file mode 100644 index 00000000000..d89f2f1ca22 --- /dev/null +++ b/addons/hr_goal/view/instance.xml @@ -0,0 +1,13 @@ + + + + + Goals + hr.goal.instance + tree,form + + + + + \ No newline at end of file diff --git a/addons/hr_goal/view/preset.xml b/addons/hr_goal/view/preset.xml new file mode 100644 index 00000000000..270149b812e --- /dev/null +++ b/addons/hr_goal/view/preset.xml @@ -0,0 +1,13 @@ + + + + + Presets + hr.goal.preset + tree,form + + + + + \ No newline at end of file From 13bef9108d6781a7936aa79ad88fe030a319ee0e Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Thu, 14 Feb 2013 10:21:51 +0100 Subject: [PATCH 0002/1311] [IMP] usage of float instead of char for goals bzr revid: mat@openerp.com-20130214092151-5vn2hki2td0lirc6 --- addons/hr_goal/goal.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/addons/hr_goal/goal.py b/addons/hr_goal/goal.py index 19551b2c24c..0fd5da3a16a 100644 --- a/addons/hr_goal/goal.py +++ b/addons/hr_goal/goal.py @@ -33,11 +33,6 @@ class hr_goal_criteria(osv.Model): _name = 'hr.goal.criteria' _description = 'Goal criteria' - def get_evaluated_field_value(self, cr, user, ids, vals, context=None): - """Return the type of the 'evaluated_field' field""" - for item in self.browse(cr, user, ids, context=context): - return item.evaluated_field.ttype - _columns = { 'name': fields.char('Name'), 'description': fields.char('Description'), @@ -59,8 +54,8 @@ class hr_goal(osv.Model): 'user_id' : fields.many2one('res.users', string='User'), 'start_date' : fields.date('Start date'), 'end_date' : fields.date('End date'), - 'to_reach' : fields.char('To reach'), - 'current' : fields.char('Current'), + 'to_reach' : fields.float('To reach'), + 'current' : fields.float('Current'), } def _compute_default_end_date(self, cr, uid, ids, field_name, arg, @@ -91,7 +86,7 @@ class hr_goal_definition(osv.Model): _columns = { 'criteria_id' : fields.many2one('hr.goal.criteria', string='Criteria'), - 'default_to_reach' : fields.char('Default value to reach'), + 'default_to_reach' : fields.float('Default value to reach'), } @@ -126,4 +121,4 @@ class hr_goal_preset(osv.Model): _defaults = { 'period': 'n', - } \ No newline at end of file + } From 759bd008b9e72b6d9cf03adc654da45bbd92c65d Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Thu, 14 Feb 2013 18:07:36 +0100 Subject: [PATCH 0003/1311] [IMP] match new specifications of gamification module bzr revid: mat@openerp.com-20130214170736-rzzji6pbk24i3u25 --- addons/hr_goal/__openerp__.py | 16 +-- addons/hr_goal/goal.py | 220 ++++++++++++++++++++----------- addons/hr_goal/view/criteria.xml | 17 --- addons/hr_goal/view/type.xml | 16 +++ 4 files changed, 164 insertions(+), 105 deletions(-) delete mode 100644 addons/hr_goal/view/criteria.xml create mode 100644 addons/hr_goal/view/type.xml diff --git a/addons/hr_goal/__openerp__.py b/addons/hr_goal/__openerp__.py index 187fb15b121..9d3a299a926 100644 --- a/addons/hr_goal/__openerp__.py +++ b/addons/hr_goal/__openerp__.py @@ -19,20 +19,18 @@ # ############################################################################## { - 'name': 'Goal', + 'name': 'Gamification', 'version': '1.0', 'author': 'OpenERP SA', 'category': 'Human Resources', - 'depends': ['base', 'hr'], - 'description': """HR Goal definition - -Defined goals to users""", + 'depends': ['mail'], + 'description': """Gamification of goals""", 'data': [ - 'view/criteria.xml', - 'view/definition.xml', - 'view/instance.xml', - 'view/preset.xml', + 'view/type.xml', + # 'view/definition.xml', + # 'view/instance.xml', + # 'view/preset.xml', ], 'installable': True, 'application': True, diff --git a/addons/hr_goal/goal.py b/addons/hr_goal/goal.py index 0fd5da3a16a..5f2c420add9 100644 --- a/addons/hr_goal/goal.py +++ b/addons/hr_goal/goal.py @@ -23,102 +23,164 @@ from openerp.osv import fields, osv from datetime import date -class hr_goal_criteria(osv.Model): - """Goal criteria definition +GAMIFICATION_GOAL_STATUS = [ + ('inprogress','In progress'), + ('reached','Reached'), + ('failed','Failed'), +] - A criteria defining a way to set an objective and evaluate it - Each module wanting to be able to set goals to the users needs to create - a new goal_criteria - """ - _name = 'hr.goal.criteria' - _description = 'Goal criteria' +# I don't see why it would be different but just in case... +GAMIFICATION_PLAN_STATUS = GAMIFICATION_GOAL_STATUS - _columns = { - 'name': fields.char('Name'), +GAMIFICATION_PERIOD_STATUS = [ + ('once','No automatic assigment'), + ('daily','Daily'), + ('weekly','Weekly'), + ('monthly','Monthly'), + ('yearly', 'Yearly') +] + +class gamification_goal_type(osv.Model): + """Goal type definition + + A goal type defining a way to set an objective and evaluate it + Each module wanting to be able to set goals to the users needs to create + a new gamification_goal_type + """ + _name = 'gamification.goal.type' + _description = 'Gamification goal type' + + _columns = { + 'name': fields.char('Name', required=True), 'description': fields.char('Description'), - 'evaluated_field': fields.many2one('ir.model.fields', - string='Evaluated field'), + 'computation_mode': fields.selection( + ( + ('s','Sum'), + ('c','Count'), + ('m','Manually'), + ), + string="Mode of computation", + description="""How is computed the goal value : +- 'Sum' for the total of the values if the 'Evaluated field' +- 'Count' for the number of entries +- 'Manually' for user defined values""", + required=True), + 'object': fields.many2one('ir.model', + string='Object', + description='The object type for the field to evaluate' ), + 'field': fields.many2one('ir.model.fields', + string='Evaluated field', + description='The field containing the value to evaluate' ), + 'field_date': fields.many2one('ir.model.fields', + string='Evaluated date field', + description='The date to use for the time period evaluated'), + 'domain': fields.char("Domain"), # how to apply it ? + 'condition' : fields.selection( + ( + ('minus','<='), + ('plus','>=') + ), + string='Validation condition', + description='A goal is considered as completed when the current value is compared to the value to reach'), + 'sequence' : fields.integer('Sequence', + description='Sequence number for ordering', + required=True), + } + + _defaults = { + 'sequence': 0, + 'condition': 'plus', } -class hr_goal(osv.Model): - """Goal instance for a user - An individual goal for a user on a specified time period - """ +class gamification_goal(osv.Model): + """Goal instance for a user - _name = 'hr.goal.instance' - _description = 'Goal instance' + An individual goal for a user on a specified time period + """ - _columns = { - 'criteria_id' : fields.many2one('hr.goal.criteria', string='Criteria'), - 'user_id' : fields.many2one('res.users', string='User'), - 'start_date' : fields.date('Start date'), - 'end_date' : fields.date('End date'), - 'to_reach' : fields.float('To reach'), - 'current' : fields.float('Current'), - } + _name = 'gamification.goal' + _description = 'Gamification goal instance' + _inherit = 'mail.thread' - def _compute_default_end_date(self, cr, uid, ids, field_name, arg, - context=None): - hr_goal = self.browse(cr, uid, ids, context) - if hr_goal.start_date: - return hr_goal.start_date + datetime.timedelta(days=1) - else: - return fields.date.today() + datetime.timedelta(days=1) + _columns = { + 'type_id' : fields.many2one('gamification.goal.type', + string='Goal type', + required=True), + 'user_id' : fields.many2one('res.users', string='User', required=True), + 'plan_id' : fields.many2one('gamification.goal.plan', + string='Goal plan'), + 'start_date' : fields.date('Start date'), + 'end_date' : fields.date('End date'), # no start and end = always active + 'target_goal' : fields.float('To reach', + required=True, + track_visibility = 'always'), # no goal = global index + 'current' : fields.float('Current', + required=True, + track_visibility = 'always'), + 'status': fields.selection(GAMIFICATION_GOAL_STATUS, + string='Status', + required=True, + track_visibility = 'always'), + } - _defaults = { + _defaults = { 'start_date': fields.date.today, - 'end_date': _compute_default_end_date, - 'current': "", + 'current': 0, + 'status': 'inprogress', } +class gamification_goal_plan(osv.Model): + """Ga;ification goal plan -class hr_goal_definition(osv.Model): - """Goal definition + Set of predifined goals to be able to automate goal settings or + quickly apply several goals manually to a group of users - Predifined goal for 'hr_goal_preset' - """ + If 'user_ids' is defined and 'period' is different than 'one', the set will + be assigned to the users for each period (eg: every 1st of each month if + 'monthly' is selected) + """ - _name = 'hr.goal.definition' - _description = 'Goal definition' + _name = 'gamification.goal.plan' + _description = 'Gamification goal plan' - _columns = { - 'criteria_id' : fields.many2one('hr.goal.criteria', - string='Criteria'), - 'default_to_reach' : fields.float('Default value to reach'), - } + _columns = { + 'name' : fields.char('Plan name', required=True), + 'user_ids' : fields.many2many('res.users', + string='Definition', + description="list of users to which the goal will be set"), + 'group_id' : fields.many2one('res.groups', string='Group'), + 'period' : fields.selection(GAMIFICATION_PERIOD_STATUS, + string='Period', + description='Period of automatic goal assigment, will be done manually if none is selected', + required=True), + 'status': fields.selection(GAMIFICATION_PLAN_STATUS, + string='Status', + required=True), + } - -class hr_goal_preset(osv.Model): - """Goal preset - - Set of predifined goals to be able to automate goal settings or - quickly apply several goals manually - - If both 'group_id' and 'period' are defined, the set will be assigned to the - group for each period (eg: every 1st of each month if 'monthly' is selected) - """ - - _name = 'hr.goal.preset' - _description = 'Goal preset' - - _columns = { - 'name' : fields.char('Set name'), - 'definition_id' : fields.many2many('hr.goal.definition', - string='Definition'), - 'group_id' : fields.many2one('res.groups', string='Group'), - 'period' : fields.selection( - ( - ('n','No automatic assigment'), - ('d','Daily'), - ('m','Monthly'), - ('y', 'Yearly') - ), - string='Period', - description='Period of automatic goal assigment, ignored if no group is selected'), - } - - _defaults = { - 'period': 'n', + _defaults = { + 'period': 'once', + 'status': 'inprogress', + } + + +class gamification_goal_planline(osv.Model): + """Gamification goal planline + + Predifined goal for 'gamification_goal_plan' + These are generic list of goals with only the target goal defined + Should only be created for the gamification_goal_plan object + """ + + _name = 'gamification.goal.planline' + _description = 'Gamification generic goal for plan' + + _columns = { + 'plan_id' : fields.many2one('gamification.goal.plan', + string='Plan'), + 'type_id' : fields.many2one('gamification.goal.type', + string='Goal type'), + 'target_goal' : fields.float('Target value to reach'), } diff --git a/addons/hr_goal/view/criteria.xml b/addons/hr_goal/view/criteria.xml deleted file mode 100644 index 8e47f656c10..00000000000 --- a/addons/hr_goal/view/criteria.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - Criterias - hr.goal.criteria - tree,form - - - - - - - - - \ No newline at end of file diff --git a/addons/hr_goal/view/type.xml b/addons/hr_goal/view/type.xml new file mode 100644 index 00000000000..5f33de1761e --- /dev/null +++ b/addons/hr_goal/view/type.xml @@ -0,0 +1,16 @@ + + + + + + Gamification + gamification.goal.type + tree,form + + + + + + + + \ No newline at end of file From bad8fff69c61d1b6e5759865de6b9a500b8cffba Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 15 Feb 2013 10:16:38 +0100 Subject: [PATCH 0004/1311] [REF] better definition of gamification models bzr revid: mat@openerp.com-20130215091638-f1ipm1p9cp2s88p9 --- addons/hr_goal/goal.py | 49 +++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/addons/hr_goal/goal.py b/addons/hr_goal/goal.py index 5f2c420add9..59cdc7c5f72 100644 --- a/addons/hr_goal/goal.py +++ b/addons/hr_goal/goal.py @@ -40,6 +40,18 @@ GAMIFICATION_PERIOD_STATUS = [ ('yearly', 'Yearly') ] +GAMIFICATION_COMPUTATION_MODE = [ + ('sum','Sum'), + ('count','Count'), + ('manually','Manually') +] + +GAMIFICATION_VALIDATION_CONDITION = [ + ('minus','<='), + ('plus','>=') +] + + class gamification_goal_type(osv.Model): """Goal type definition @@ -52,44 +64,37 @@ class gamification_goal_type(osv.Model): _columns = { 'name': fields.char('Name', required=True), - 'description': fields.char('Description'), - 'computation_mode': fields.selection( - ( - ('s','Sum'), - ('c','Count'), - ('m','Manually'), - ), + 'description': fields.text('Description'), + 'computation_mode': fields.selection(GAMIFICATION_COMPUTATION_MODE, string="Mode of computation", - description="""How is computed the goal value : -- 'Sum' for the total of the values if the 'Evaluated field' -- 'Count' for the number of entries + help="""How is computed the goal value :\n +- 'Sum' for the total of the values if the 'Evaluated field'\n +- 'Count' for the number of entries\n - 'Manually' for user defined values""", required=True), 'object': fields.many2one('ir.model', string='Object', - description='The object type for the field to evaluate' ), + help='The object type for the field to evaluate' ), 'field': fields.many2one('ir.model.fields', string='Evaluated field', - description='The field containing the value to evaluate' ), + help='The field containing the value to evaluate' ), 'field_date': fields.many2one('ir.model.fields', string='Evaluated date field', - description='The date to use for the time period evaluated'), + help='The date to use for the time period evaluated'), 'domain': fields.char("Domain"), # how to apply it ? - 'condition' : fields.selection( - ( - ('minus','<='), - ('plus','>=') - ), + 'condition' : fields.selection(GAMIFICATION_VALIDATION_CONDITION, string='Validation condition', - description='A goal is considered as completed when the current value is compared to the value to reach'), + help='A goal is considered as completed when the current value is compared to the value to reach', + required=True), 'sequence' : fields.integer('Sequence', - description='Sequence number for ordering', + help='Sequence number for ordering', required=True), } _defaults = { 'sequence': 0, 'condition': 'plus', + 'computation_mode':'manually', } @@ -149,11 +154,11 @@ class gamification_goal_plan(osv.Model): 'name' : fields.char('Plan name', required=True), 'user_ids' : fields.many2many('res.users', string='Definition', - description="list of users to which the goal will be set"), + help="list of users to which the goal will be set"), 'group_id' : fields.many2one('res.groups', string='Group'), 'period' : fields.selection(GAMIFICATION_PERIOD_STATUS, string='Period', - description='Period of automatic goal assigment, will be done manually if none is selected', + help='Period of automatic goal assigment, will be done manually if none is selected', required=True), 'status': fields.selection(GAMIFICATION_PLAN_STATUS, string='Status', From 77a3ff7ca7d63874dcad3106b83ce620031e0a8c Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 15 Feb 2013 10:24:07 +0100 Subject: [PATCH 0005/1311] [IMP] views for gamification goal types in Settings menu bzr revid: mat@openerp.com-20130215092407-gyu3112u4kkpf980 --- addons/hr_goal/__openerp__.py | 1 + addons/hr_goal/view/type.xml | 46 ++++++++++++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/addons/hr_goal/__openerp__.py b/addons/hr_goal/__openerp__.py index 9d3a299a926..08854e9cf23 100644 --- a/addons/hr_goal/__openerp__.py +++ b/addons/hr_goal/__openerp__.py @@ -27,6 +27,7 @@ 'description': """Gamification of goals""", 'data': [ + 'view/menu.xml', 'view/type.xml', # 'view/definition.xml', # 'view/instance.xml', diff --git a/addons/hr_goal/view/type.xml b/addons/hr_goal/view/type.xml index 5f33de1761e..bfcf7c93b62 100644 --- a/addons/hr_goal/view/type.xml +++ b/addons/hr_goal/view/type.xml @@ -3,14 +3,52 @@ - Gamification + Goal types gamification.goal.type tree,form - - - + + Goal types list + gamification.goal.type + + + + + + + + + + + + + Goal types form + gamification.goal.type + +
+ +

+ +

+ + + + + + + + + + + + + + +
+
+
+
\ No newline at end of file From e14c3a95d8424ccc01754ec3e9d84fa6180e52be Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 15 Feb 2013 11:42:14 +0100 Subject: [PATCH 0006/1311] [ADD] gamification:view for the goal object bzr revid: mat@openerp.com-20130215104214-ebpw2uglqg7h4byc --- addons/hr_goal/__openerp__.py | 8 ++-- addons/hr_goal/goal.py | 1 - addons/hr_goal/view/goal.xml | 67 ++++++++++++++++++++++++++++++++ addons/hr_goal/view/instance.xml | 13 ------- addons/hr_goal/view/type.xml | 2 +- 5 files changed, 71 insertions(+), 20 deletions(-) create mode 100644 addons/hr_goal/view/goal.xml delete mode 100644 addons/hr_goal/view/instance.xml diff --git a/addons/hr_goal/__openerp__.py b/addons/hr_goal/__openerp__.py index 08854e9cf23..cc2a70dffbb 100644 --- a/addons/hr_goal/__openerp__.py +++ b/addons/hr_goal/__openerp__.py @@ -27,12 +27,10 @@ 'description': """Gamification of goals""", 'data': [ - 'view/menu.xml', 'view/type.xml', - # 'view/definition.xml', - # 'view/instance.xml', - # 'view/preset.xml', - ], + 'view/goal.xml', + 'view/menu.xml', + ], 'installable': True, 'application': True, } diff --git a/addons/hr_goal/goal.py b/addons/hr_goal/goal.py index 59cdc7c5f72..88c26a1767c 100644 --- a/addons/hr_goal/goal.py +++ b/addons/hr_goal/goal.py @@ -130,7 +130,6 @@ class gamification_goal(osv.Model): } _defaults = { - 'start_date': fields.date.today, 'current': 0, 'status': 'inprogress', } diff --git a/addons/hr_goal/view/goal.xml b/addons/hr_goal/view/goal.xml new file mode 100644 index 00000000000..3b60aac1976 --- /dev/null +++ b/addons/hr_goal/view/goal.xml @@ -0,0 +1,67 @@ + + + + + Goals + gamification.goal + tree,form,calendar + + + + Goal list + gamification.goal + + + + + + + + + + + + + + + Goal form + gamification.goal + +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + Goal calendar + gamification.goal + + + + + + + +
+
\ No newline at end of file diff --git a/addons/hr_goal/view/instance.xml b/addons/hr_goal/view/instance.xml deleted file mode 100644 index d89f2f1ca22..00000000000 --- a/addons/hr_goal/view/instance.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - Goals - hr.goal.instance - tree,form - - - - - \ No newline at end of file diff --git a/addons/hr_goal/view/type.xml b/addons/hr_goal/view/type.xml index bfcf7c93b62..c189685e4e2 100644 --- a/addons/hr_goal/view/type.xml +++ b/addons/hr_goal/view/type.xml @@ -2,7 +2,7 @@ - + Goal types gamification.goal.type tree,form From 357c08da1704fd83f6638088b55710161cc2a6bf Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 15 Feb 2013 11:44:17 +0100 Subject: [PATCH 0007/1311] [FIX] add gamification menu view bzr revid: mat@openerp.com-20130215104417-zisq806feo4l45hy --- addons/hr_goal/view/menu.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 addons/hr_goal/view/menu.xml diff --git a/addons/hr_goal/view/menu.xml b/addons/hr_goal/view/menu.xml new file mode 100644 index 00000000000..e699f445591 --- /dev/null +++ b/addons/hr_goal/view/menu.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file From 5dfd8de04dd3cf3de4e4ffc84fb0407036c9c232 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 15 Feb 2013 12:03:58 +0100 Subject: [PATCH 0008/1311] [REF] changing gamification goal plan status possibilities bzr revid: mat@openerp.com-20130215110358-o52snkry52mtdm3z --- addons/hr_goal/goal.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/addons/hr_goal/goal.py b/addons/hr_goal/goal.py index 88c26a1767c..ae2c773fff9 100644 --- a/addons/hr_goal/goal.py +++ b/addons/hr_goal/goal.py @@ -29,8 +29,11 @@ GAMIFICATION_GOAL_STATUS = [ ('failed','Failed'), ] -# I don't see why it would be different but just in case... -GAMIFICATION_PLAN_STATUS = GAMIFICATION_GOAL_STATUS +GAMIFICATION_PLAN_STATUS = [ + ('draft','Draft'), + ('inprogress','In progress'), + ('done','Done'), +] GAMIFICATION_PERIOD_STATUS = [ ('once','No automatic assigment'), From ca712230e2b0cf2927971ce3a8715e9ae8a015e7 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 15 Feb 2013 14:09:21 +0100 Subject: [PATCH 0009/1311] [ADD] plan views for gamification moduel bzr revid: mat@openerp.com-20130215130921-ca64o47syxn6vob1 --- addons/hr_goal/__openerp__.py | 1 + addons/hr_goal/goal.py | 6 ++- addons/hr_goal/view/definition.xml | 15 ------ addons/hr_goal/view/menu.xml | 12 ++--- addons/hr_goal/view/plan.xml | 73 ++++++++++++++++++++++++++++++ addons/hr_goal/view/preset.xml | 13 ------ 6 files changed, 85 insertions(+), 35 deletions(-) delete mode 100644 addons/hr_goal/view/definition.xml create mode 100644 addons/hr_goal/view/plan.xml delete mode 100644 addons/hr_goal/view/preset.xml diff --git a/addons/hr_goal/__openerp__.py b/addons/hr_goal/__openerp__.py index cc2a70dffbb..896a5b4eb01 100644 --- a/addons/hr_goal/__openerp__.py +++ b/addons/hr_goal/__openerp__.py @@ -29,6 +29,7 @@ 'data': [ 'view/type.xml', 'view/goal.xml', + 'view/plan.xml', 'view/menu.xml', ], 'installable': True, diff --git a/addons/hr_goal/goal.py b/addons/hr_goal/goal.py index ae2c773fff9..03a1f741440 100644 --- a/addons/hr_goal/goal.py +++ b/addons/hr_goal/goal.py @@ -155,8 +155,12 @@ class gamification_goal_plan(osv.Model): _columns = { 'name' : fields.char('Plan name', required=True), 'user_ids' : fields.many2many('res.users', - string='Definition', + string='Users', help="list of users to which the goal will be set"), + 'planline_ids' : fields.one2many('gamification.goal.planline', + 'plan_id', + string='Planline', + help="list of goals that will be set"), 'group_id' : fields.many2one('res.groups', string='Group'), 'period' : fields.selection(GAMIFICATION_PERIOD_STATUS, string='Period', diff --git a/addons/hr_goal/view/definition.xml b/addons/hr_goal/view/definition.xml deleted file mode 100644 index 47241f6710b..00000000000 --- a/addons/hr_goal/view/definition.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - Goal definitions - hr.goal.definition - tree,form - - - - - - - \ No newline at end of file diff --git a/addons/hr_goal/view/menu.xml b/addons/hr_goal/view/menu.xml index e699f445591..f26feb1ffef 100644 --- a/addons/hr_goal/view/menu.xml +++ b/addons/hr_goal/view/menu.xml @@ -1,13 +1,13 @@ - - - - - - + + + + + + \ No newline at end of file diff --git a/addons/hr_goal/view/plan.xml b/addons/hr_goal/view/plan.xml new file mode 100644 index 00000000000..3d9760bd37d --- /dev/null +++ b/addons/hr_goal/view/plan.xml @@ -0,0 +1,73 @@ + + + + + + Goal plans + gamification.goal.plan + tree,form + + + + Goal plans list + gamification.goal.plan + + + + + + + + + + + Goal plan form + gamification.goal.plan + +
+ +

+ +

+ + + + + + + + + + + + + + + + + + + + +
+ X +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
\ No newline at end of file diff --git a/addons/hr_goal/view/preset.xml b/addons/hr_goal/view/preset.xml deleted file mode 100644 index 270149b812e..00000000000 --- a/addons/hr_goal/view/preset.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - Presets - hr.goal.preset - tree,form - - - - - \ No newline at end of file From 92c03ea75cd87381b09d01ac0dd7bafa922dfe9a Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 15 Feb 2013 14:32:34 +0100 Subject: [PATCH 0010/1311] [ADD] report for goal plans bzr revid: mat@openerp.com-20130215133234-kp49lzfd5tjj1o7c --- addons/hr_goal/goal.py | 30 +++++++++++++++++++++++++++++- addons/hr_goal/view/plan.xml | 10 ++++++++-- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/addons/hr_goal/goal.py b/addons/hr_goal/goal.py index 03a1f741440..259b483583b 100644 --- a/addons/hr_goal/goal.py +++ b/addons/hr_goal/goal.py @@ -54,6 +54,19 @@ GAMIFICATION_VALIDATION_CONDITION = [ ('plus','>=') ] +GAMIFICATION_REPORT_MODE = [ + ('board','Leader board'), + ('progressbar','Personal progressbar') +] + +GAMIFICATION_REPORT_FREQ = [ + ('onchange','On change'), + ('daily','Daily'), + ('weekly','Weekly'), + ('monthly','Monthly'), + ('yearly', 'Yearly') +] + class gamification_goal_type(osv.Model): """Goal type definition @@ -161,7 +174,9 @@ class gamification_goal_plan(osv.Model): 'plan_id', string='Planline', help="list of goals that will be set"), - 'group_id' : fields.many2one('res.groups', string='Group'), + 'autojoin_group_id' : fields.many2one('res.groups', + string='Group', + help='Group of users whose members will automatically be added to the users'), 'period' : fields.selection(GAMIFICATION_PERIOD_STATUS, string='Period', help='Period of automatic goal assigment, will be done manually if none is selected', @@ -169,11 +184,24 @@ class gamification_goal_plan(osv.Model): 'status': fields.selection(GAMIFICATION_PLAN_STATUS, string='Status', required=True), + 'report_mode':fields.selection(GAMIFICATION_REPORT_MODE, + string="Mode", + help='How is displayed the results, shared or in a signle progressbar', + required=True), + 'report_message_frequency':fields.selection(GAMIFICATION_REPORT_FREQ, + string="Frequency", + required=True), + 'report_message_group_id' : fields.many2one('mail.group', + string='Group', + help='Group that will receive the report in addition to the user'), + 'report_header' : fields.text('Report header'), } _defaults = { 'period': 'once', 'status': 'inprogress', + 'report_mode' : 'progressbar', + 'report_message_frequency' : 'onchange', } diff --git a/addons/hr_goal/view/plan.xml b/addons/hr_goal/view/plan.xml index 3d9760bd37d..8d177c5201b 100644 --- a/addons/hr_goal/view/plan.xml +++ b/addons/hr_goal/view/plan.xml @@ -30,10 +30,16 @@ - + + + + + + + @@ -41,7 +47,7 @@ - + From 2004f2bcd7dcaebfcac66ded0f24530a00b1b67e Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 15 Feb 2013 14:59:40 +0100 Subject: [PATCH 0011/1311] [IMP] editable planline for gaming module bzr revid: mat@openerp.com-20130215135940-mji5ype1wsteduob --- addons/hr_goal/view/plan.xml | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/addons/hr_goal/view/plan.xml b/addons/hr_goal/view/plan.xml index 8d177c5201b..d8e392020f1 100644 --- a/addons/hr_goal/view/plan.xml +++ b/addons/hr_goal/view/plan.xml @@ -43,7 +43,12 @@ - + + + + + + @@ -75,5 +80,18 @@
+ + + + Goal planline list + gamification.goal.planline + + + + + + + +
\ No newline at end of file From 8768bd81501adbda8bbe697eb74007333a78fa5e Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 15 Feb 2013 15:16:51 +0100 Subject: [PATCH 0012/1311] [IMP] gamification: use state instead of status bzr revid: mat@openerp.com-20130215141651-5ktcyekk9subj6ji --- addons/hr_goal/goal.py | 20 ++++++++++---------- addons/hr_goal/view/goal.xml | 12 +++++++++--- addons/hr_goal/view/plan.xml | 6 +++--- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/addons/hr_goal/goal.py b/addons/hr_goal/goal.py index 259b483583b..1808f0a9ec8 100644 --- a/addons/hr_goal/goal.py +++ b/addons/hr_goal/goal.py @@ -23,19 +23,19 @@ from openerp.osv import fields, osv from datetime import date -GAMIFICATION_GOAL_STATUS = [ +GAMIFICATION_GOAL_STATE = [ ('inprogress','In progress'), ('reached','Reached'), ('failed','Failed'), ] -GAMIFICATION_PLAN_STATUS = [ +GAMIFICATION_PLAN_STATE = [ ('draft','Draft'), ('inprogress','In progress'), ('done','Done'), ] -GAMIFICATION_PERIOD_STATUS = [ +GAMIFICATION_PERIOD_STATE = [ ('once','No automatic assigment'), ('daily','Daily'), ('weekly','Weekly'), @@ -139,15 +139,15 @@ class gamification_goal(osv.Model): 'current' : fields.float('Current', required=True, track_visibility = 'always'), - 'status': fields.selection(GAMIFICATION_GOAL_STATUS, - string='Status', + 'state': fields.selection(GAMIFICATION_GOAL_STATE, + string='State', required=True, track_visibility = 'always'), } _defaults = { 'current': 0, - 'status': 'inprogress', + 'state': 'inprogress', } @@ -177,12 +177,12 @@ class gamification_goal_plan(osv.Model): 'autojoin_group_id' : fields.many2one('res.groups', string='Group', help='Group of users whose members will automatically be added to the users'), - 'period' : fields.selection(GAMIFICATION_PERIOD_STATUS, + 'period' : fields.selection(GAMIFICATION_PERIOD_STATE, string='Period', help='Period of automatic goal assigment, will be done manually if none is selected', required=True), - 'status': fields.selection(GAMIFICATION_PLAN_STATUS, - string='Status', + 'state': fields.selection(GAMIFICATION_PLAN_STATE, + string='State', required=True), 'report_mode':fields.selection(GAMIFICATION_REPORT_MODE, string="Mode", @@ -199,7 +199,7 @@ class gamification_goal_plan(osv.Model): _defaults = { 'period': 'once', - 'status': 'inprogress', + 'state': 'inprogress', 'report_mode' : 'progressbar', 'report_message_frequency' : 'onchange', } diff --git a/addons/hr_goal/view/goal.xml b/addons/hr_goal/view/goal.xml index 3b60aac1976..84ecbc7b0a5 100644 --- a/addons/hr_goal/view/goal.xml +++ b/addons/hr_goal/view/goal.xml @@ -11,14 +11,14 @@ Goal list gamification.goal - + - + @@ -28,6 +28,12 @@ gamification.goal
+
+
@@ -42,7 +48,7 @@ - + diff --git a/addons/hr_goal/view/plan.xml b/addons/hr_goal/view/plan.xml index d8e392020f1..a4ab2148f1b 100644 --- a/addons/hr_goal/view/plan.xml +++ b/addons/hr_goal/view/plan.xml @@ -12,10 +12,10 @@ Goal plans list gamification.goal.plan - + - + @@ -32,7 +32,7 @@ - + From beefb1ef740977536e98cdb66df1ee60ffdc9db6 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 15 Feb 2013 15:29:41 +0100 Subject: [PATCH 0013/1311] [IMP] gamification: workflow buttons bzr revid: mat@openerp.com-20130215142941-laoi71wkjlychxy1 --- addons/hr_goal/view/plan.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/addons/hr_goal/view/plan.xml b/addons/hr_goal/view/plan.xml index a4ab2148f1b..6a23f5c42bc 100644 --- a/addons/hr_goal/view/plan.xml +++ b/addons/hr_goal/view/plan.xml @@ -25,6 +25,12 @@ gamification.goal.plan +
+

From 44fa903585bc95a881bb50eab8a1e0fb3c3cc7ba Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 15 Feb 2013 15:37:35 +0100 Subject: [PATCH 0014/1311] [IMP] gamification: no state field in forms bzr revid: mat@openerp.com-20130215143735-l92r0s1bc51nopx8 --- addons/hr_goal/view/goal.xml | 1 - addons/hr_goal/view/plan.xml | 1 - 2 files changed, 2 deletions(-) diff --git a/addons/hr_goal/view/goal.xml b/addons/hr_goal/view/goal.xml index 84ecbc7b0a5..ab4ee3f8b3d 100644 --- a/addons/hr_goal/view/goal.xml +++ b/addons/hr_goal/view/goal.xml @@ -48,7 +48,6 @@ - diff --git a/addons/hr_goal/view/plan.xml b/addons/hr_goal/view/plan.xml index 6a23f5c42bc..8618f64c37a 100644 --- a/addons/hr_goal/view/plan.xml +++ b/addons/hr_goal/view/plan.xml @@ -38,7 +38,6 @@ - From aeb3ea30b3652ce7abba287347875e45e7cfe255 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 15 Feb 2013 15:53:48 +0100 Subject: [PATCH 0015/1311] [FIX] gamification: fix form id for plan bzr revid: mat@openerp.com-20130215145348-kfrh6eaj3z2k65bh --- addons/hr_goal/view/plan.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/hr_goal/view/plan.xml b/addons/hr_goal/view/plan.xml index 8618f64c37a..dca2c545354 100644 --- a/addons/hr_goal/view/plan.xml +++ b/addons/hr_goal/view/plan.xml @@ -20,7 +20,7 @@ - + Goal plan form gamification.goal.plan From 2e0186dab07363220f75f91fa8b5236d142587a7 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 15 Feb 2013 15:54:15 +0100 Subject: [PATCH 0016/1311] [ADD] gamification: add completeness progressbar bzr revid: mat@openerp.com-20130215145415-39cx5v3pf8mn5ye7 --- addons/hr_goal/goal.py | 14 ++++++++++++++ addons/hr_goal/view/goal.xml | 1 + 2 files changed, 15 insertions(+) diff --git a/addons/hr_goal/goal.py b/addons/hr_goal/goal.py index 1808f0a9ec8..cc4c525deae 100644 --- a/addons/hr_goal/goal.py +++ b/addons/hr_goal/goal.py @@ -107,6 +107,7 @@ class gamification_goal_type(osv.Model): required=True), } + _order = 'sequence' _defaults = { 'sequence': 0, 'condition': 'plus', @@ -124,6 +125,16 @@ class gamification_goal(osv.Model): _description = 'Gamification goal instance' _inherit = 'mail.thread' + def _get_completeness(self, cr, uid, ids, field_name, arg, context=None): + res = {} + for goal in self.browse(cr, uid, ids, context): + # more 100% ? + if goal.target_goal > 0: + res[goal.id] = 100.0 * goal.current / goal.target_goal + else: + res[goal.id] = 0.0 + return res + _columns = { 'type_id' : fields.many2one('gamification.goal.type', string='Goal type', @@ -139,6 +150,9 @@ class gamification_goal(osv.Model): 'current' : fields.float('Current', required=True, track_visibility = 'always'), + 'completeness': fields.function(_get_completeness, + type='float', + string='Occupation'), 'state': fields.selection(GAMIFICATION_GOAL_STATE, string='State', required=True, diff --git a/addons/hr_goal/view/goal.xml b/addons/hr_goal/view/goal.xml index ab4ee3f8b3d..c711927044c 100644 --- a/addons/hr_goal/view/goal.xml +++ b/addons/hr_goal/view/goal.xml @@ -18,6 +18,7 @@ + From 73b558e3c37a5d74815937feb8309bf0ef334b8c Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 15 Feb 2013 16:05:20 +0100 Subject: [PATCH 0017/1311] [REF] gamification: change name of folder bzr revid: mat@openerp.com-20130215150520-50fnb7rx690t8yq9 --- addons/{hr_goal => gamification}/__init__.py | 0 addons/{hr_goal => gamification}/__openerp__.py | 0 addons/{hr_goal => gamification}/goal.py | 0 addons/{hr_goal => gamification}/view/goal.xml | 0 addons/{hr_goal => gamification}/view/menu.xml | 0 addons/{hr_goal => gamification}/view/plan.xml | 0 addons/{hr_goal => gamification}/view/type.xml | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename addons/{hr_goal => gamification}/__init__.py (100%) rename addons/{hr_goal => gamification}/__openerp__.py (100%) rename addons/{hr_goal => gamification}/goal.py (100%) rename addons/{hr_goal => gamification}/view/goal.xml (100%) rename addons/{hr_goal => gamification}/view/menu.xml (100%) rename addons/{hr_goal => gamification}/view/plan.xml (100%) rename addons/{hr_goal => gamification}/view/type.xml (100%) diff --git a/addons/hr_goal/__init__.py b/addons/gamification/__init__.py similarity index 100% rename from addons/hr_goal/__init__.py rename to addons/gamification/__init__.py diff --git a/addons/hr_goal/__openerp__.py b/addons/gamification/__openerp__.py similarity index 100% rename from addons/hr_goal/__openerp__.py rename to addons/gamification/__openerp__.py diff --git a/addons/hr_goal/goal.py b/addons/gamification/goal.py similarity index 100% rename from addons/hr_goal/goal.py rename to addons/gamification/goal.py diff --git a/addons/hr_goal/view/goal.xml b/addons/gamification/view/goal.xml similarity index 100% rename from addons/hr_goal/view/goal.xml rename to addons/gamification/view/goal.xml diff --git a/addons/hr_goal/view/menu.xml b/addons/gamification/view/menu.xml similarity index 100% rename from addons/hr_goal/view/menu.xml rename to addons/gamification/view/menu.xml diff --git a/addons/hr_goal/view/plan.xml b/addons/gamification/view/plan.xml similarity index 100% rename from addons/hr_goal/view/plan.xml rename to addons/gamification/view/plan.xml diff --git a/addons/hr_goal/view/type.xml b/addons/gamification/view/type.xml similarity index 100% rename from addons/hr_goal/view/type.xml rename to addons/gamification/view/type.xml From 33be2c1b8aff3f5d631b824686a802ba423f48f3 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 15 Feb 2013 17:02:04 +0100 Subject: [PATCH 0018/1311] [IMP] gamification: change of state when using buttons bzr revid: mat@openerp.com-20130215160204-0jt6yw01lytjgyto --- addons/gamification/goal.py | 24 ++++++++++++++++++++++-- addons/gamification/view/goal.xml | 6 +++--- addons/gamification/view/plan.xml | 6 +++--- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/addons/gamification/goal.py b/addons/gamification/goal.py index cc4c525deae..e77eb5ff922 100644 --- a/addons/gamification/goal.py +++ b/addons/gamification/goal.py @@ -164,6 +164,16 @@ class gamification_goal(osv.Model): 'state': 'inprogress', } + def action_reach(self, cr, uid, ids, context=None): + return self.write(cr, uid, ids, {'state': 'reached'}, context=context) + + def action_fail(self, cr, uid, ids, context=None): + return self.write(cr, uid, ids, {'state': 'failed'}, context=context) + + def action_cancel(self, cr, uid, ids, context=None): + return self.write(cr, uid, ids, {'state': 'inprogress'}, context=context) + + class gamification_goal_plan(osv.Model): """Ga;ification goal plan @@ -206,18 +216,28 @@ class gamification_goal_plan(osv.Model): string="Frequency", required=True), 'report_message_group_id' : fields.many2one('mail.group', - string='Group', + string='Report to', help='Group that will receive the report in addition to the user'), 'report_header' : fields.text('Report header'), } _defaults = { 'period': 'once', - 'state': 'inprogress', + 'state': 'draft', 'report_mode' : 'progressbar', 'report_message_frequency' : 'onchange', } + def action_start(self, cr, uid, ids, context=None): + return self.write(cr, uid, ids, {'state': 'inprogress'}, context=context) + + def action_close(self, cr, uid, ids, context=None): + return self.write(cr, uid, ids, {'state': 'done'}, context=context) + + def action_cancel(self, cr, uid, ids, context=None): + return self.write(cr, uid, ids, {'state': 'draft'}, context=context) + + class gamification_goal_planline(osv.Model): """Gamification goal planline diff --git a/addons/gamification/view/goal.xml b/addons/gamification/view/goal.xml index c711927044c..80a44e92c2d 100644 --- a/addons/gamification/view/goal.xml +++ b/addons/gamification/view/goal.xml @@ -30,9 +30,9 @@
-
diff --git a/addons/gamification/view/plan.xml b/addons/gamification/view/plan.xml index dca2c545354..f29ca2f7c62 100644 --- a/addons/gamification/view/plan.xml +++ b/addons/gamification/view/plan.xml @@ -26,9 +26,9 @@
-
From 974d4b6af08e12e28323829b2231677eb7840c72 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 15 Feb 2013 17:07:18 +0100 Subject: [PATCH 0019/1311] [IMP] gamification: hide done-fail state for goals bzr revid: mat@openerp.com-20130215160718-4n10mbzaxdaabp6e --- addons/gamification/view/goal.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/gamification/view/goal.xml b/addons/gamification/view/goal.xml index 80a44e92c2d..560e7d51c3f 100644 --- a/addons/gamification/view/goal.xml +++ b/addons/gamification/view/goal.xml @@ -33,7 +33,7 @@

+ + +
+
+ @@ -111,7 +125,8 @@ - + + diff --git a/addons/gamification/view/type.xml b/addons/gamification/view/type.xml index 8c3ec05f7f1..0740e5735cc 100644 --- a/addons/gamification/view/type.xml +++ b/addons/gamification/view/type.xml @@ -58,7 +58,10 @@ gamification.goal.type - + + + + From 0e23b01ffe921486273dc018e54ca22d170f9853 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Tue, 19 Feb 2013 15:37:22 +0100 Subject: [PATCH 0033/1311] [IMP] gamification: ondelete rules and better constraint bzr revid: mat@openerp.com-20130219143722-om96otvoemmfryyh --- addons/gamification/goal.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/addons/gamification/goal.py b/addons/gamification/goal.py index 0986ceaa731..e823e529e62 100644 --- a/addons/gamification/goal.py +++ b/addons/gamification/goal.py @@ -142,10 +142,12 @@ class gamification_goal(osv.Model): _columns = { 'type_id' : fields.many2one('gamification.goal.type', string='Goal Type', - required=True), + required=True, + ondelete="cascade"), 'user_id' : fields.many2one('res.users', string='User', required=True), 'plan_id' : fields.many2one('gamification.goal.plan', - string='Goal Plan'), + string='Goal Plan', + ondelete="cascade"), 'start_date' : fields.date('Start Date'), 'end_date' : fields.date('End Date'), # no start and end = always active 'target_goal' : fields.float('To Reach', @@ -244,13 +246,13 @@ class gamification_goal_plan(osv.Model): def _check_nonzero_users(self, cr, uid, ids, context=None): "checks that there is at least one user set" for plan in self.browse(cr, uid, ids, context): - if len(plan.user_ids) < 1: + if len(plan.user_ids) < 1 and plan.state != 'draft': return False return True _constraints = [ (_check_nonzero_planline, "At least one planline is required to create a goal plan", ['planline_ids']), - (_check_nonzero_users, "At least one user is required to create a goal plan", ['user_ids']), + (_check_nonzero_users, "At least one user is required to create a non-draft goal plan", ['user_ids']), ] def action_start(self, cr, uid, ids, context=None): @@ -309,10 +311,12 @@ class gamification_goal_planline(osv.Model): _columns = { 'plan_id' : fields.many2one('gamification.goal.plan', - string='Plan'), + string='Plan', + ondelete="cascade"), 'type_id' : fields.many2one('gamification.goal.type', string='Goal Type', - required=True), + required=True, + ondelete="cascade"), 'target_goal' : fields.float('Target Value to Reach', required=True), 'sequence_type' : fields.related('type_id','sequence', From d3750e5a8a43ad8b3eb49702fe74801f881776b7 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Tue, 19 Feb 2013 17:30:48 +0100 Subject: [PATCH 0034/1311] [IMP] gamification: default help messages and better default filter bzr revid: mat@openerp.com-20130219163048-d6cb69034nzbmj86 --- addons/gamification/goal.py | 22 ++++++++++------------ addons/gamification/view/goal.xml | 9 +++++++++ addons/gamification/view/plan.xml | 17 ++++++++++++++--- addons/gamification/view/type.xml | 9 +++++++++ 4 files changed, 42 insertions(+), 15 deletions(-) diff --git a/addons/gamification/goal.py b/addons/gamification/goal.py index e823e529e62..8fb9c4d7091 100644 --- a/addons/gamification/goal.py +++ b/addons/gamification/goal.py @@ -21,25 +21,23 @@ from openerp.osv import fields, osv -from datetime import date - GAMIFICATION_GOAL_STATE = [ - ('inprogress','In progress'), - ('reached','Reached'), - ('failed','Failed'), + ('inprogress', 'In progress'), + ('reached', 'Reached'), + ('failed', 'Failed'), ] GAMIFICATION_PLAN_STATE = [ - ('draft','Draft'), - ('inprogress','In progress'), - ('done','Done'), + ('draft', 'Draft'), + ('inprogress', 'In progress'), + ('done', 'Done'), ] GAMIFICATION_PERIOD_STATE = [ - ('once','Manual'), - ('daily','Daily'), - ('weekly','Weekly'), - ('monthly','Monthly'), + ('once', 'Manual'), + ('daily', 'Daily'), + ('weekly', 'Weekly'), + ('monthly', 'Monthly'), ('yearly', 'Yearly') ] diff --git a/addons/gamification/view/goal.xml b/addons/gamification/view/goal.xml index e582ff853f0..a788fdecd87 100644 --- a/addons/gamification/view/goal.xml +++ b/addons/gamification/view/goal.xml @@ -6,6 +6,15 @@ gamification.goal tree,form,calendar {'search_default_my_in_progress': 1} + +

+ Click to create a goal. +

+

+ A goal is defined by a user and a goal type. + Goals can be created automatically by using goal plans. +

+
diff --git a/addons/gamification/view/plan.xml b/addons/gamification/view/plan.xml index dc79a6b44fc..109b8ef3319 100644 --- a/addons/gamification/view/plan.xml +++ b/addons/gamification/view/plan.xml @@ -6,7 +6,18 @@ Goal Plans gamification.goal.plan tree,form - {'search_default_in_progress': 1} + {'search_default_draft_active': 1} + +

+ Click to create a goal plan. +

+

+ Goal plans allow to create and assign to users easily a list of goals. + A goal plan is a predifined list of goals types with a target value. + The plan can use a period (weekly, monthly...) for automatic creation of goals. + The goals are created for the specified users or memeber of the group. +

+
@@ -123,8 +134,8 @@ gamification.goal.plan - + diff --git a/addons/gamification/view/type.xml b/addons/gamification/view/type.xml index 0740e5735cc..9a6f1029ef2 100644 --- a/addons/gamification/view/type.xml +++ b/addons/gamification/view/type.xml @@ -6,6 +6,15 @@ Goal Types gamification.goal.type tree,form + +

+ Click to create a goal type. +

+

+ A goal type is a technical model of goal defining a condition to reach. + The dates, values to reach or users are defined in goal instance. +

+
From 6313de4dc8f86a19f92ea737e7d2df9f91066fb2 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Tue, 19 Feb 2013 17:52:15 +0100 Subject: [PATCH 0035/1311] [ADD] gamification: add remind and last update value bzr revid: mat@openerp.com-20130219165215-kscj3kpld3h8rz41 --- addons/gamification/goal.py | 4 ++++ addons/gamification/view/plan.xml | 1 + 2 files changed, 5 insertions(+) diff --git a/addons/gamification/goal.py b/addons/gamification/goal.py index 8fb9c4d7091..bc0057a6613 100644 --- a/addons/gamification/goal.py +++ b/addons/gamification/goal.py @@ -161,6 +161,8 @@ class gamification_goal(osv.Model): string='State', required=True, track_visibility = 'always'), + 'last_update' : fields.date('Last Update', + help="In case of manual goal, reminders are sent if the goal as not been updated for a while (defined in goal plan). Ignored in case of non-manual goal or goal not linked to a plan."), # } _defaults = { @@ -225,6 +227,8 @@ class gamification_goal_plan(osv.Model): string='Report to', help='Group that will receive the report in addition to the user'), 'report_header' : fields.text('Report Header'), + 'remind_update_delays' : fields.integer('Remind delays', + help="The number of days after which the user assigned to a manual goal will be reminded. Never reminded if no value is specified.") } _defaults = { diff --git a/addons/gamification/view/plan.xml b/addons/gamification/view/plan.xml index 109b8ef3319..123513fe9f6 100644 --- a/addons/gamification/view/plan.xml +++ b/addons/gamification/view/plan.xml @@ -74,6 +74,7 @@ +
From 48fed40b103544200e041749f5b1519a7db795c6 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Tue, 19 Feb 2013 17:56:09 +0100 Subject: [PATCH 0036/1311] [IMP] gamification: differenciate inprogress and inprogress to update bzr revid: mat@openerp.com-20130219165609-q7gqbuf0wm7v8egl --- addons/gamification/goal.py | 1 + addons/gamification/view/goal.xml | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/addons/gamification/goal.py b/addons/gamification/goal.py index bc0057a6613..0ffb77da3b3 100644 --- a/addons/gamification/goal.py +++ b/addons/gamification/goal.py @@ -23,6 +23,7 @@ from openerp.osv import fields, osv GAMIFICATION_GOAL_STATE = [ ('inprogress', 'In progress'), + ('inprogress_update', 'In progress (to update)'), ('reached', 'Reached'), ('failed', 'Failed'), ] diff --git a/addons/gamification/view/goal.xml b/addons/gamification/view/goal.xml index a788fdecd87..8c695d68d53 100644 --- a/addons/gamification/view/goal.xml +++ b/addons/gamification/view/goal.xml @@ -41,10 +41,10 @@
-
@@ -90,7 +90,7 @@ + domain="[('user_id', '=', uid),('state', 'in', ('inprogress', 'inprogress_update'))]"/> From 55541a3d3b4984a80b45e40ce06b5af5e86366dc Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Wed, 20 Feb 2013 13:33:28 +0100 Subject: [PATCH 0037/1311] [REF] gamification: minor changes for better consistency bzr revid: mat@openerp.com-20130220123328-n13psgprp4ci14vn --- addons/gamification/goal.py | 139 ++++++++++++++++-------------- addons/gamification/view/goal.xml | 13 ++- addons/gamification/view/plan.xml | 29 +++---- addons/gamification/view/type.xml | 6 +- 4 files changed, 98 insertions(+), 89 deletions(-) diff --git a/addons/gamification/goal.py b/addons/gamification/goal.py index 0ffb77da3b3..ba452d7636f 100644 --- a/addons/gamification/goal.py +++ b/addons/gamification/goal.py @@ -20,52 +20,7 @@ ############################################################################## from openerp.osv import fields, osv - -GAMIFICATION_GOAL_STATE = [ - ('inprogress', 'In progress'), - ('inprogress_update', 'In progress (to update)'), - ('reached', 'Reached'), - ('failed', 'Failed'), -] - -GAMIFICATION_PLAN_STATE = [ - ('draft', 'Draft'), - ('inprogress', 'In progress'), - ('done', 'Done'), -] - -GAMIFICATION_PERIOD_STATE = [ - ('once', 'Manual'), - ('daily', 'Daily'), - ('weekly', 'Weekly'), - ('monthly', 'Monthly'), - ('yearly', 'Yearly') -] - -GAMIFICATION_COMPUTATION_MODE = [ - ('sum','Sum'), - ('count','Count'), - ('manually','Manually') -] - -GAMIFICATION_VALIDATION_CONDITION = [ - ('minus','<='), - ('plus','>=') -] - -GAMIFICATION_REPORT_MODE = [ - ('board','Leader board'), - ('progressbar','Personal progressbar') -] - -GAMIFICATION_REPORT_FREQ = [ - ('never','Never'), - ('onchange','On change'), - ('daily','Daily'), - ('weekly','Weekly'), - ('monthly','Monthly'), - ('yearly', 'Yearly') -] +from openerp.tools.safe_eval import safe_eval class gamification_goal_type(osv.Model): @@ -82,7 +37,11 @@ class gamification_goal_type(osv.Model): _columns = { 'name': fields.char('Type Name', required=True), 'description': fields.text('Description'), - 'computation_mode': fields.selection(GAMIFICATION_COMPUTATION_MODE, + 'computation_mode': fields.selection([ + ('sum','Sum'), + ('count','Count'), + ('manually','Manually') + ], string="Mode of Computation", help="""How is computed the goal value :\n - 'Sum' for the total of the values if the 'Evaluated field'\n @@ -101,7 +60,10 @@ class gamification_goal_type(osv.Model): 'domain': fields.char("Domain", help="Technical filters rules to apply", required=True), # how to apply it ? - 'condition' : fields.selection(GAMIFICATION_VALIDATION_CONDITION, + 'condition' : fields.selection([ + ('minus','<='), + ('plus','>=') + ], string='Validation Condition', help='A goal is considered as completed when the current value is compared to the value to reach', required=True), @@ -118,6 +80,15 @@ class gamification_goal_type(osv.Model): 'domain':"[]", } + + +def compute_goal_completeness(current, target_goal): + # more than 100% case is handled by the widget + if target_goal > 0: + return 100.0 * current / target_goal + else: + return 0.0 + class gamification_goal(osv.Model): """Goal instance for a user @@ -131,11 +102,7 @@ class gamification_goal(osv.Model): def _get_completeness(self, cr, uid, ids, field_name, arg, context=None): res = {} for goal in self.browse(cr, uid, ids, context): - # more than 100% case is handled by the widget - if goal.target_goal > 0: - res[goal.id] = 100.0 * goal.current / goal.target_goal - else: - res[goal.id] = 0.0 + res[goal.id] = compute_goal_completeness(goal.current, goal.target_goal) return res _columns = { @@ -144,7 +111,7 @@ class gamification_goal(osv.Model): required=True, ondelete="cascade"), 'user_id' : fields.many2one('res.users', string='User', required=True), - 'plan_id' : fields.many2one('gamification.goal.plan', + 'planline_id' : fields.many2one('gamification.goal.planline', string='Goal Plan', ondelete="cascade"), 'start_date' : fields.date('Start Date'), @@ -157,8 +124,14 @@ class gamification_goal(osv.Model): track_visibility = 'always'), 'completeness': fields.function(_get_completeness, type='float', - string='Occupation'), - 'state': fields.selection(GAMIFICATION_GOAL_STATE, + string='Completeness'), + 'state': fields.selection([ + ('inprogress', 'In progress'), + ('inprogress_update', 'In progress (to update)'), + ('reached', 'Reached'), + ('failed', 'Failed'), + ('canceled', 'Canceled'), + ], string='State', required=True, track_visibility = 'always'), @@ -210,18 +183,38 @@ class gamification_goal_plan(osv.Model): 'autojoin_group_id' : fields.many2one('res.groups', string='Group', help='Group of users whose members will automatically be added to the users'), - 'period' : fields.selection(GAMIFICATION_PERIOD_STATE, - string='Period', + 'period' : fields.selection([ + ('once', 'Manual'), + ('daily', 'Daily'), + ('weekly', 'Weekly'), + ('monthly', 'Monthly'), + ('yearly', 'Yearly') + ], + string='Periodicity', help='Period of automatic goal assigment, will be done manually if none is selected', required=True), - 'state': fields.selection(GAMIFICATION_PLAN_STATE, + 'state': fields.selection([ + ('draft', 'Draft'), + ('inprogress', 'In progress'), + ('done', 'Done'), + ], string='State', required=True), - 'report_mode':fields.selection(GAMIFICATION_REPORT_MODE, - string="Mode", - help='How is displayed the results, shared or in a signle progressbar', + 'visibility_mode':fields.selection([ + ('board','Leader board'), + ('progressbar','Personal progressbar') + ], + string="Visibility", + help='How are displayed the results, shared or in a single progressbar', required=True), - 'report_message_frequency':fields.selection(GAMIFICATION_REPORT_FREQ, + 'report_message_frequency':fields.selection([ + ('never','Never'), + ('onchange','On change'), + ('daily','Daily'), + ('weekly','Weekly'), + ('monthly','Monthly'), + ('yearly', 'Yearly') + ], string="Frequency", required=True), 'report_message_group_id' : fields.many2one('mail.group', @@ -235,7 +228,7 @@ class gamification_goal_plan(osv.Model): _defaults = { 'period': 'once', 'state': 'draft', - 'report_mode' : 'progressbar', + 'visibility_mode' : 'progressbar', 'report_message_frequency' : 'onchange', } @@ -287,6 +280,24 @@ class gamification_goal_plan(osv.Model): return self.write(cr, uid, ids, {'state': 'inprogress'}, context=context) + def generate_goals_from_plan(self, cr, uid, ids, context=None): + """Generate the lsit of goals fron a plan""" + for plan in self.browse(cr, uid, ids, context): + for planline in plan.planline_ids: + for user in plan.user_ids: + goal_obj = self.pool.get('gamification.goal') + current = compute_current_value(planline.type_id, user_id) + goal_id = goal_obj.create(cr, uid, { + 'type_id': planline.type_id, + 'user_id': user.id, + 'start_date':0, + 'end_date':0, + 'target_goal':planline.target_goal, + 'state':'inprogress', + 'last_update':fields.date.today, + }, context=context) + + class gamification_goal_planline(osv.Model): """Gamification goal planline diff --git a/addons/gamification/view/goal.xml b/addons/gamification/view/goal.xml index 8c695d68d53..20a30607359 100644 --- a/addons/gamification/view/goal.xml +++ b/addons/gamification/view/goal.xml @@ -5,7 +5,6 @@ Goals gamification.goal tree,form,calendar - {'search_default_my_in_progress': 1}

Click to create a goal. @@ -30,7 +29,7 @@ - + @@ -41,7 +40,8 @@

-
@@ -58,6 +58,7 @@ + From 0cc2ab390e586df29ea941cec2512e7d5cf50354 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Thu, 21 Feb 2013 14:50:52 +0100 Subject: [PATCH 0046/1311] [FIX] gamification: days and not day bzr revid: mat@openerp.com-20130221135052-yh4mu7huzjab04tt --- addons/gamification/cron.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/gamification/cron.xml b/addons/gamification/cron.xml index b222d0e6d8c..d1ca200111e 100644 --- a/addons/gamification/cron.xml +++ b/addons/gamification/cron.xml @@ -7,7 +7,7 @@ Run Goal Checker 1 - day + days -1 gamification.goal @@ -20,7 +20,7 @@ Run Goal Plan Checker 1 - day + days -1 gamification.goal.plan From 14d9a9b573ad6f76aa34fcdd8046bb3ecc15ef1e Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 22 Feb 2013 10:47:45 +0100 Subject: [PATCH 0047/1311] [IMP] gamification: minor improvements and fix bzr revid: mat@openerp.com-20130222094745-jxe0m4i11qbbe1vp --- addons/gamification/cron.xml | 5 ++--- addons/gamification/goal.py | 26 ++++++++++++++++---------- addons/gamification/view/goal.xml | 9 +++++++-- addons/gamification/view/plan.xml | 12 ++++++------ 4 files changed, 31 insertions(+), 21 deletions(-) diff --git a/addons/gamification/cron.xml b/addons/gamification/cron.xml index d1ca200111e..4388ac88ff9 100644 --- a/addons/gamification/cron.xml +++ b/addons/gamification/cron.xml @@ -5,7 +5,6 @@ Run Goal Checker - 1 days -1 @@ -15,10 +14,9 @@ () - Run Goal Plan Checker - 1 days -1 @@ -27,5 +25,6 @@ _update_all () + \ No newline at end of file diff --git a/addons/gamification/goal.py b/addons/gamification/goal.py index aa9bf282788..c40a67b4eee 100644 --- a/addons/gamification/goal.py +++ b/addons/gamification/goal.py @@ -167,8 +167,7 @@ class gamification_goal(osv.Model): def _update_all(self, cr, uid, ids=False, context=None): """Update every goal in progress""" if not ids: - ids = self.search(cr, uid, [('state', 'in', ('inprogress','inprogress_update'))]) - print("_update_all", ids) + ids = self.search(cr, uid, [('state', 'in', ('inprogress','inprogress_update', 'reached'))]) return self.update(cr, uid, ids, context=context) def update(self, cr, uid, ids, context=None, force_update=False): @@ -180,11 +179,16 @@ class gamification_goal(osv.Model): :param force_update: if false, only goals in progress are checked.""" for goal in self.browse(cr, uid, ids, context=context or {}): - if not force_update and goal.state not in ('inprogress','inprogress_update'): # reached ? + if not force_update and goal.state not in ('inprogress','inprogress_update','reached'): + # skip if goal failed or canceled + continue + if goal.state == 'reached' and goal.end_date and fields.date.today() > goal.end_date: + # only a goal reached but not passed the end date will still be + # checked (to be able to improve the score) continue if goal.type_id.computation_mode == 'manually': - towrite = {'current': current} + towrite = {'current':goal.current} # check for remind to update if goal.remind_update_delay and goal.last_update: delta_max = timedelta(days=goal.remind_update_delay) @@ -195,8 +199,8 @@ class gamification_goal(osv.Model): obj = self.pool.get(goal.type_id.model_id.model) field_date_name = goal.type_id.field_date_id.name - domain = safe_eval(goal.type_id.domain) - domain.append(('user_id', '=', goal.user_id.id)) + domain = safe_eval(goal.type_id.domain, + {'user_id': goal.user_id.id}) if goal.start_date: domain.append((field_date_name, '>=', goal.start_date)) if goal.end_date: @@ -244,10 +248,11 @@ class gamification_goal(osv.Model): ('user_id', '=', user_id), ('start_date', '=', start_date.isoformat())] goal_ids = obj.search(cr, uid, domain, context=context) + print(domain, goal_ids) if len(goal_ids) > 0: # already exist, skip return True - + print("creating goal for", planline_id, user_id, start_date) planline = self.pool.get('gamification.goal.planline').browse(cr, uid, planline_id, context) values = { 'type_id':planline.type_id.id, @@ -327,7 +332,7 @@ class gamification_goal_plan(osv.Model): string='Group', help='Group of users whose members will automatically be added to the users'), 'period' : fields.selection([ - ('once', 'Manual'), + ('once', 'No Periodicity'), ('daily', 'Daily'), ('weekly', 'Weekly'), ('monthly', 'Monthly'), @@ -397,8 +402,9 @@ class gamification_goal_plan(osv.Model): def _update_all(self, cr, uid, ids=False, context=None): """Update every plan in progress""" if not ids: - ids = self.search(cr, uid, [('state', '=', 'inprogress')]) - print("_update_all", ids) + ids = self.search(cr, uid, [('state', '=', 'inprogress'), + ('period', '!=', 'once')]) + print("_update_all plans", ids) return self.generate_goals_from_plan(cr, uid, ids, context=context) def action_start(self, cr, uid, ids, context=None): diff --git a/addons/gamification/view/goal.xml b/addons/gamification/view/goal.xml index 9c641de2711..ed964452d9e 100644 --- a/addons/gamification/view/goal.xml +++ b/addons/gamification/view/goal.xml @@ -20,7 +20,7 @@ Goal List gamification.goal - + @@ -57,7 +57,12 @@ - + + diff --git a/addons/gamification/view/plan.xml b/addons/gamification/view/plan.xml index e83ca145124..66af613c2fd 100644 --- a/addons/gamification/view/plan.xml +++ b/addons/gamification/view/plan.xml @@ -53,12 +53,12 @@ -

-
-
- -

+
+
From be75098490cb7b59664eaef2d299237eef231212 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 22 Feb 2013 10:56:15 +0100 Subject: [PATCH 0048/1311] [REF] gamification: remove force_update parameter bzr revid: mat@openerp.com-20130222095615-r79xrz3awc886ko6 --- addons/gamification/goal.py | 15 +++++---------- addons/gamification/view/goal.xml | 2 +- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/addons/gamification/goal.py b/addons/gamification/goal.py index c40a67b4eee..95341d4a004 100644 --- a/addons/gamification/goal.py +++ b/addons/gamification/goal.py @@ -170,16 +170,15 @@ class gamification_goal(osv.Model): ids = self.search(cr, uid, [('state', 'in', ('inprogress','inprogress_update', 'reached'))]) return self.update(cr, uid, ids, context=context) - def update(self, cr, uid, ids, context=None, force_update=False): + def update(self, cr, uid, ids, context=None): """Update the goals to recomputes values and change of states If a goal reaches the target value, the status is set to reach If the end date is passed (at least +1 day, time not considered) without - the target value being reached, the goal is set as failed - :param force_update: if false, only goals in progress are checked.""" + the target value being reached, the goal is set as failed.""" for goal in self.browse(cr, uid, ids, context=context or {}): - if not force_update and goal.state not in ('inprogress','inprogress_update','reached'): + if goal.state not in ('inprogress','inprogress_update','reached'): # skip if goal failed or canceled continue if goal.state == 'reached' and goal.end_date and fields.date.today() > goal.end_date: @@ -277,7 +276,7 @@ class gamification_goal(osv.Model): values['remind_update_delay'] = planline.plan_id.remind_update_delay new_goal_id = obj.create(cr, uid, values, context) - self.update(cr, uid, [new_goal_id], context=context, force_update=True) + self.update(cr, uid, [new_goal_id], context=context) def cancel_goals_from_plan(self, cr, uid, ids, planline_id, context=None): @@ -299,10 +298,6 @@ class gamification_goal(osv.Model): def action_cancel(self, cr, uid, ids, context=None): return self.write(cr, uid, ids, {'state': 'inprogress'}, context=context) - def action_refresh(self, cr, uid, ids, context=None): - """Update the state of goal, force to recomputes values""" - return self.update(cr, uid, ids, context=context, force_update=True) - class gamification_goal_plan(osv.Model): """Gamification goal plan @@ -427,7 +422,7 @@ class gamification_goal_plan(osv.Model): for planline in plan.planline_ids: goal_obj = self.pool.get('gamification.goal') goal_ids = goal_obj.search(cr, uid, [('planline_id', '=', planline.id)] , context=context) - goal_obj.update(cr, uid, goal_ids, context=context, force_update=True) + goal_obj.update(cr, uid, goal_ids, context=context) return True diff --git a/addons/gamification/view/goal.xml b/addons/gamification/view/goal.xml index ed964452d9e..a66af16d6a9 100644 --- a/addons/gamification/view/goal.xml +++ b/addons/gamification/view/goal.xml @@ -40,7 +40,7 @@
-
+ ]]>
+ + + From d9999731f74ef031f25b11aa893160f28ba3b4af Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Tue, 26 Feb 2013 12:34:03 +0100 Subject: [PATCH 0054/1311] [ADD] gamification: send email for individual report bzr revid: mat@openerp.com-20130226113403-2hw21ush1gw1kis0 --- addons/gamification/__init__.py | 3 +- addons/gamification/goal.py | 49 +++++++++++-------- .../gamification/report/report_progress.xml | 25 ++++------ addons/gamification/res_users.py | 2 - addons/gamification/view/plan.xml | 1 + 5 files changed, 41 insertions(+), 39 deletions(-) diff --git a/addons/gamification/__init__.py b/addons/gamification/__init__.py index a406bf3a621..540d22424d0 100644 --- a/addons/gamification/__init__.py +++ b/addons/gamification/__init__.py @@ -1,2 +1,3 @@ import goal -import res_users \ No newline at end of file +import res_users +import report diff --git a/addons/gamification/goal.py b/addons/gamification/goal.py index b01553ab768..ba9f665188c 100644 --- a/addons/gamification/goal.py +++ b/addons/gamification/goal.py @@ -2,26 +2,28 @@ ############################################################################## # # OpenERP, Open Source Management Solution -# Copyright (C) 2004-2013 Tiny SPRL (). +# Copyright (C) 2010-Today OpenERP SA () # # 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. +# it under the terms of the GNU 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. +# GNU 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 . +# You should have received a copy of the GNU General Public License +# along with this program. If not, see # ############################################################################## from openerp.osv import fields, osv from openerp.tools.safe_eval import safe_eval +from mako.template import Template as MakoTemplate + from datetime import date, timedelta import calendar import itertools @@ -476,7 +478,6 @@ class gamification_goal_plan(osv.Model): :param ids: ids of plans to which the users will be added :param user_ids: ids of the users to add""" - print("subscibe users", ids, user_ids) for plan in self.browse(cr,uid, ids, context): subscription = [user.id for user in plan.user_ids] subscription.extend(user_ids) @@ -489,21 +490,30 @@ class gamification_goal_plan(osv.Model): """Post report about the progress of the goals""" goal_obj = self.pool.get('gamification.goal') - + for plan in self.browse(cr, uid, ids, context=context): if plan.visibility_mode == 'board': # generate a shared report pass else: + if not plan.report_message_group_id: + continue + # generate individual reports - report_title = "Individual Report for {0} - {1}".format(plan.name, fields.date.today()) + template_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'gamification', 'email_template_gamification_individual')[1] + for user in plan.user_ids: + goal_ids = self.get_current_related_user_goals(cr, uid, plan.id, user.id, context) + if len(goal_ids) == 0: + continue + + template_context = dict(context) + template_context['goals'] = goal_obj.browse(cr, uid, goal_ids, context=context) + template_context['user'] = user + self.pool.get('email.template').send_mail(cr, uid, template_id, plan.id, context=template_context) - for goal in goal_obj.browse(cr, uid, goal_ids, context=context): - pass - def get_current_related_user_goals(self, cr, uid, plan_id, user_id, context=None): """Get the ids of goals linked to a plan for the current instance @@ -512,7 +522,7 @@ class gamification_goal_plan(osv.Model): return the goals started the 1st of this month). """ - plan in self.browse(cr, uid, plan_id, context=context) + plan = self.browse(cr, uid, plan_id, context=context) today = date.today() if plan.period == 'daily': start_date = today @@ -542,11 +552,13 @@ class gamification_goal_plan(osv.Model): related_goal_ids.append(goal_ids[0]) elif len(goal_ids) == 0: # this goal has been deleted - raise osv.except_osv(_('Warning!'), _('Planline {0} has no goal present for user {1} at date {2}'.format(planline.id, user.id, start_date))) + raise osv.except_osv('Warning!','Planline {0} has no goal present for user {1} at date {2}'.format(planline.id, user.id, start_date)) else: # more than one goal ? - raise osv.except_osv(_('Warning!'), _('Duplicate goals for planline {0}, user {1}, date {2}'.format(planline.id, user.id, start_date))) + raise osv.except_osv('Warning!', 'Duplicate goals for planline {0}, user {1}, date {2}'.format(planline.id, user.id, start_date)) related_goal_ids.extend(goal_ids) + return related_goal_ids + class gamification_goal_planline(osv.Model): """Gamification goal planline @@ -589,7 +601,4 @@ class gamification_goal_planline(osv.Model): store={ 'gamification.goal.type': (_get_planline_types, ['sequence'], 10), }), - } - -# class gamification_goal_report(osv.Model): -# _name = 'gamification.goal.report' + } \ No newline at end of file diff --git a/addons/gamification/report/report_progress.xml b/addons/gamification/report/report_progress.xml index 67d7e564098..b8a2c3b22db 100644 --- a/addons/gamification/report/report_progress.xml +++ b/addons/gamification/report/report_progress.xml @@ -8,30 +8,23 @@ form form,tree - - - - - + - + Individual Report - Send by Email noreply@localhost - ${object.title} - ${object.partner_id.id} - + Individual report - ${object.name} + ${object.report_message_group_id.alias_id.name_get()[0][1]} + - Individual_Report_${(object.number or '').replace('/','_')}_${object.state == 'draft' and 'draft' or ''} +
+

Individual report - ${object.name}

-

${report.title}

- -

Hello ${report.user}, bellow are your latest results for the plan ${report.plan_name}.

+

Hello ${ctx['user'].name}, bellow are your latest results for the plan ${object.name}.

@@ -40,7 +33,7 @@ - % for goal in report: + % for goal in ctx['goals']: diff --git a/addons/gamification/res_users.py b/addons/gamification/res_users.py index f2f54d9b324..38d4950b185 100644 --- a/addons/gamification/res_users.py +++ b/addons/gamification/res_users.py @@ -9,7 +9,6 @@ class res_users_gamification_group(osv.Model): _inherit = ['res.users'] def write(self, cr, uid, ids, vals, context=None): - print("Overwrite res_users_gamification_group", ids) write_res = super(res_users_gamification_group, self).write(cr, uid, ids, vals, context=context) if vals.get('groups_id'): # form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]} @@ -31,7 +30,6 @@ class res_groups_gamification_group(osv.Model): _inherit = 'res.groups' def write(self, cr, uid, ids, vals, context=None): - print("Overwrite res_groups_gamification_group", ids) write_res = super(res_groups_gamification_group, self).write(cr, uid, ids, vals, context=context) if vals.get('users'): # form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]} diff --git a/addons/gamification/view/plan.xml b/addons/gamification/view/plan.xml index 66af613c2fd..f4b3313c76e 100644 --- a/addons/gamification/view/plan.xml +++ b/addons/gamification/view/plan.xml @@ -50,6 +50,7 @@
Current Completeness
${goal.type_id.name} ${goal.target_goal}
@@ -43,6 +43,43 @@ % endfor
+
+ ]]>
+
+ + + + Leaderboard Goal Report - Send by Email + noreply@localhost + ${object.name} group report + ${object.report_message_group_id.alias_id.name_get()[0][1]} + + + +

Group report - ${object.name}

+ +

Hello, + + Bellow are the latest results for the plan ${object.name} for the group ${object.report_message_group_id.name}

+ + % for planline in ctx['planlines']: +

${planline['goal_type']}

+ + + + + + % for idx, goal in planline['list']: + + + + + + % endfor +
#UserCompletenessCurrent
${idx+1}${goal.completeness}${goal.current}/${goal.target_goal}
+ % endfor + ]]>
From 5a341ab9708f74e93c4f29593256a46a79a63275 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Tue, 26 Feb 2013 16:13:28 +0100 Subject: [PATCH 0059/1311] [FIX] gamification: add user name in report board bzr revid: mat@openerp.com-20130226151328-4lnq7ku5y8waedkl --- addons/gamification/report/report_progress.xml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/addons/gamification/report/report_progress.xml b/addons/gamification/report/report_progress.xml index 5027cf5e877..08fa3cad3c8 100644 --- a/addons/gamification/report/report_progress.xml +++ b/addons/gamification/report/report_progress.xml @@ -71,9 +71,14 @@ Completeness Current % for idx, goal in planline['list']: - + = 100: + style="font-weight:bold;"" + % endif + > ${idx+1} - ${goal.completeness} + ${goal.user.name} + ${goal.completeness}% ${goal.current}/${goal.target_goal} % endfor From 240ed326b20f6a4303380b011c4634d9a81e3326 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Tue, 26 Feb 2013 16:23:25 +0100 Subject: [PATCH 0060/1311] [IMP] gamification: overwrite write method for goal bzr revid: mat@openerp.com-20130226152325-5n3ruxk6jsju18uu --- addons/gamification/goal.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/addons/gamification/goal.py b/addons/gamification/goal.py index de7f74462b1..2491f474c26 100644 --- a/addons/gamification/goal.py +++ b/addons/gamification/goal.py @@ -272,6 +272,13 @@ class gamification_goal(osv.Model): return self.write(cr, uid, ids, {'state': 'inprogress'}, context=context) + def write(self, cr, uid, ids, vals, context=None): + for goal in self.browse(cr, uid, ids, vals): + vals['last_update'] = fields.date.today() + write_res = super(gamification_goal, self).write(cr, uid, ids, vals, context=context) + return write_res + + class gamification_goal_plan(osv.Model): """Gamification goal plan @@ -536,7 +543,7 @@ class gamification_goal_plan(osv.Model): # most complete first, current if same percentage (eg: if several 100%) sorted_planline_goals = enumerate(sorted(planlines_stats, key=lambda k: (k['completeness'], k['current']), reverse=True)) template_context['planlines'].append({'goal_type':planline.type_id.name, 'list':sorted_planline_goals}) - + self.pool.get('email.template').send_mail(cr, uid, template_id, plan.id, context=template_context) else: From 6cc39b566ad46ce4bc9775c74dda97e1563b981e Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Tue, 26 Feb 2013 16:32:47 +0100 Subject: [PATCH 0061/1311] [ADD] gamification: refresh button for manual goal bzr revid: mat@openerp.com-20130226153247-ak48q3dooyc71doi --- addons/gamification/goal.py | 3 ++- addons/gamification/view/goal.xml | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/addons/gamification/goal.py b/addons/gamification/goal.py index 2491f474c26..fedf4c7ea36 100644 --- a/addons/gamification/goal.py +++ b/addons/gamification/goal.py @@ -273,8 +273,9 @@ class gamification_goal(osv.Model): def write(self, cr, uid, ids, vals, context=None): + """Overwrite the write method to update the last_update field to today""" for goal in self.browse(cr, uid, ids, vals): - vals['last_update'] = fields.date.today() + vals['last_update'] = fields.date.today() write_res = super(gamification_goal, self).write(cr, uid, ids, vals, context=context) return write_res diff --git a/addons/gamification/view/goal.xml b/addons/gamification/view/goal.xml index 926d02f9c71..52def276977 100644 --- a/addons/gamification/view/goal.xml +++ b/addons/gamification/view/goal.xml @@ -47,6 +47,9 @@ +
+
From ce777dde1cdea831edfc6dee7e3ff212b973353a Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Wed, 27 Feb 2013 09:46:08 +0100 Subject: [PATCH 0062/1311] [IMP] gamification: better consistency for plan bzr revid: mat@openerp.com-20130227084608-79t5fyjzweq5zq1j --- addons/gamification/goal.py | 20 ++++++++++++---- addons/gamification/view/plan.xml | 38 ++++++++++++++++++++++++++----- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/addons/gamification/goal.py b/addons/gamification/goal.py index fedf4c7ea36..32186ff0a15 100644 --- a/addons/gamification/goal.py +++ b/addons/gamification/goal.py @@ -305,7 +305,7 @@ class gamification_goal_plan(osv.Model): help="list of goals that will be set", required=True), 'autojoin_group_id' : fields.many2one('res.groups', - string='Group', + string='Auto-join Group', help='Group of users whose members will automatically be added to the users'), 'period' : fields.selection([ ('once', 'No Periodicity'), @@ -342,8 +342,8 @@ class gamification_goal_plan(osv.Model): string="Frequency", required=True), 'report_message_group_id' : fields.many2one('mail.group', - string='Report to', - help='Group that will receive the report in addition to the user'), + string='Send a copy to', + help='Group that will receive a copy of the report in addition to the user'), 'report_header' : fields.text('Report Header'), 'remind_update_delay' : fields.integer('Remind delay', help="The number of days after which the user assigned to a manual goal will be reminded. Never reminded if no value is specified.") @@ -375,6 +375,16 @@ class gamification_goal_plan(osv.Model): (_check_nonzero_users, "At least one user is required to create a non-draft goal plan", ['user_ids']), ] + def write(self, cr, uid, ids, vals, context=None): + """Overwrite the write method to add the user of groups""" + write_res = super(gamification_goal_plan, self).write(cr, uid, ids, vals, context=context) + + # add users when change the group auto-subscription + if 'autojoin_group_id' in vals: + new_group = self.pool.get('res.groups').browse(cr, uid, vals['autojoin_group_id'], context=context) + self.plan_subscribe_users(cr, uid, ids, [user.id for user in new_group.users], context=context) + return write_res + def _update_all(self, cr, uid, ids=False, context=None): """Update every plan in progress""" if not ids: @@ -487,7 +497,7 @@ class gamification_goal_plan(osv.Model): return True - def plan_subscribe_users(self, cr, uid, ids, user_ids, context=None): + def plan_subscribe_users(self, cr, uid, ids, new_user_ids, context=None): """ Add the following users to plans :param ids: ids of plans to which the users will be added @@ -495,7 +505,7 @@ class gamification_goal_plan(osv.Model): for plan in self.browse(cr,uid, ids, context): subscription = [user.id for user in plan.user_ids] - subscription.extend(user_ids) + subscription.extend(new_user_ids) unified_subscription = list(set(subscription)) self.write(cr, uid, ids, {'user_ids': [(4, uid) for uid in unified_subscription]}, context=context) return True diff --git a/addons/gamification/view/plan.xml b/addons/gamification/view/plan.xml index f4b3313c76e..ce2e60c5d1e 100644 --- a/addons/gamification/view/plan.xml +++ b/addons/gamification/view/plan.xml @@ -70,12 +70,38 @@ - + - - - - + + @@ -90,7 +116,7 @@ - + From 86bb3fbb2a814879df99d60196eb914a9d426175 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Wed, 27 Feb 2013 10:42:13 +0100 Subject: [PATCH 0063/1311] [FIX] gamification: show related goals from plan correctly bzr revid: mat@openerp.com-20130227094213-jbhg1g5xiv1va2fz --- addons/gamification/goal.py | 29 ++++++++++++++++++++++++++--- addons/gamification/view/plan.xml | 5 ++--- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/addons/gamification/goal.py b/addons/gamification/goal.py index 32186ff0a15..3776b008e56 100644 --- a/addons/gamification/goal.py +++ b/addons/gamification/goal.py @@ -447,6 +447,29 @@ class gamification_goal_plan(osv.Model): return True + def action_show_related_goals(self, cr, uid, ids, context=None): + """ This opens goal view with a restriction to the list of goals from this plan only + @return: the goal view + """ + # get ids of related goals + goal_obj = self.pool.get('gamification.goal') + related_goal_ids = [] + for plan in self.browse(cr, uid, ids, context=context): + for planline in plan.planline_ids: + goal_ids = goal_obj.search(cr, uid, [('planline_id', '=', planline.id)], context=context) + related_goal_ids.extend(goal_ids) + + # process the new view + if context is None: + context = {} + res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid ,'gamification','goals_from_plan_act', context=context) + res['context'] = context + res['context'].update({ + 'default_id': related_goal_ids + }) + res['domain'] = [('id','in', related_goal_ids)] + return res + def generate_goals_from_plan(self, cr, uid, ids, context=None): """Generate the lsit of goals fron a plan""" for plan in self.browse(cr, uid, ids, context): @@ -571,6 +594,7 @@ class gamification_goal_plan(osv.Model): self.pool.get('email.template').send_mail(cr, uid, template_id, plan.id, context=template_context) return True + def get_current_related_goals(self, cr, uid, plan_id, user_id, context=None): """Get the ids of goals linked to a plan for the current instance @@ -593,9 +617,8 @@ class gamification_goal_plan(osv.Model): domain.append(('start_date', '=', start_date.isoformat())) goal_ids = goal_obj.search(cr, uid, domain, context=context) - if len(goal_ids) == 1: - related_goal_ids.append(goal_ids[0]) - elif len(goal_ids) == 0: + related_goal_ids.append(goal_ids[0]) + if len(goal_ids) == 0: # this goal has been deleted raise osv.except_osv('Warning!','Planline {0} has no goal present for user {1} at date {2}'.format(planline.id, user.id, start_date)) else: # more than one goal ? diff --git a/addons/gamification/view/plan.xml b/addons/gamification/view/plan.xml index ce2e60c5d1e..265deaaecd8 100644 --- a/addons/gamification/view/plan.xml +++ b/addons/gamification/view/plan.xml @@ -31,12 +31,11 @@
- + gamification.goal form Related Goals tree,form - {'search_default_plan_id': [active_id], 'default_plan_id': active_id} @@ -63,7 +62,7 @@
-
From 504b6c4ca7e29afe04f216519b982ed1f986a0f2 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Wed, 27 Feb 2013 11:59:56 +0100 Subject: [PATCH 0064/1311] [ADD] gamification: send reminder for update bzr revid: mat@openerp.com-20130227105956-pu0xjsswucjou5o9 --- addons/gamification/__openerp__.py | 1 + addons/gamification/goal.py | 26 ++++++++++--------- addons/gamification/report/reminder.xml | 23 ++++++++++++++++ .../gamification/report/report_progress.xml | 11 -------- addons/gamification/view/goal.xml | 2 +- 5 files changed, 39 insertions(+), 24 deletions(-) create mode 100644 addons/gamification/report/reminder.xml diff --git a/addons/gamification/__openerp__.py b/addons/gamification/__openerp__.py index 855a18cb749..26cc1696fc2 100644 --- a/addons/gamification/__openerp__.py +++ b/addons/gamification/__openerp__.py @@ -33,6 +33,7 @@ 'view/menu.xml', 'cron.xml', 'report/report_progress.xml', + 'report/reminder.xml', ], 'installable': True, 'application': True, diff --git a/addons/gamification/goal.py b/addons/gamification/goal.py index 3776b008e56..704ba2bad11 100644 --- a/addons/gamification/goal.py +++ b/addons/gamification/goal.py @@ -24,7 +24,7 @@ from openerp.tools.safe_eval import safe_eval from mako.template import Template as MakoTemplate -from datetime import date, timedelta +from datetime import date, datetime, timedelta import calendar import itertools @@ -115,13 +115,6 @@ class gamification_goal_type(osv.Model): -def compute_goal_completeness(current, target_goal): - # more than 100% case is handled by the widget - if target_goal > 0: - return 100.0 * current / target_goal - else: - return 0.0 - class gamification_goal(osv.Model): """Goal instance for a user @@ -132,9 +125,14 @@ class gamification_goal(osv.Model): _inherit = 'mail.thread' def _get_completeness(self, cr, uid, ids, field_name, arg, context=None): + """Return the percentage of completeness of the goal, between 0 and 100""" res = {} for goal in self.browse(cr, uid, ids, context): - res[goal.id] = compute_goal_completeness(goal.current, goal.target_goal) + if goal.current > 0: + res[goal.id] = min(100, round(100.0 * goal.current / goal.target_goal, 2)) + else: + res[goal.id] = 0.0 + return res def on_change_type_id(self, cr, uid, ids, type_id=False, context=None): @@ -194,7 +192,6 @@ class gamification_goal(osv.Model): } - def _update_all(self, cr, uid, ids=False, context=None): """Update every goal in progress""" if not ids: @@ -208,7 +205,7 @@ class gamification_goal(osv.Model): If a goal reaches the target value, the status is set to reach If the end date is passed (at least +1 day, time not considered) without the target value being reached, the goal is set as failed.""" - + for goal in self.browse(cr, uid, ids, context=context or {}): if goal.state not in ('inprogress','inprogress_update','reached'): # skip if goal failed or canceled @@ -223,9 +220,14 @@ class gamification_goal(osv.Model): # check for remind to update if goal.remind_update_delay and goal.last_update: delta_max = timedelta(days=goal.remind_update_delay) - if fields.date.today() - goal.last_update > delta_max: + last_update = datetime.strptime(goal.last_update,'%Y-%m-%d').date() + if date.today() - last_update > delta_max: towrite['state'] = 'inprogress_update' + # generate a remind report + template_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'gamification', 'email_template_goal_reminder')[1] + self.pool.get('email.template').send_mail(cr, uid, template_id, goal.id, context=context) + else: # count or sum obj = self.pool.get(goal.type_id.model_id.model) field_date_name = goal.type_id.field_date_id.name diff --git a/addons/gamification/report/reminder.xml b/addons/gamification/report/reminder.xml new file mode 100644 index 00000000000..d184b6518de --- /dev/null +++ b/addons/gamification/report/reminder.xml @@ -0,0 +1,23 @@ + + + + + Goal Update Reminder - Send by Email + noreply@localhost + Goal Update Reminder + ${object.user_id.email} + + + +

Hello ${object.user_id.name}

+ +

You have not updated your progress for the goal ${object.type_id.name} (currently reached at ${object.completeness}%) for at least ${object.remind_update_delay}. Do not forget to do it.

+ +

If you have not changed your score yet, you can use the button "The current value is up to date" to indicate so.

+ + ]]>
+
+ +
+
diff --git a/addons/gamification/report/report_progress.xml b/addons/gamification/report/report_progress.xml index 08fa3cad3c8..08e37df1dec 100644 --- a/addons/gamification/report/report_progress.xml +++ b/addons/gamification/report/report_progress.xml @@ -1,16 +1,5 @@ - - - - Email Templates - email.template - form - form,tree - - - - diff --git a/addons/gamification/view/goal.xml b/addons/gamification/view/goal.xml index 52def276977..c983076c9ad 100644 --- a/addons/gamification/view/goal.xml +++ b/addons/gamification/view/goal.xml @@ -48,7 +48,7 @@
-
From e57a761dc7ca0463b8dffac7d8423ec362745513 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Wed, 27 Feb 2013 12:12:20 +0100 Subject: [PATCH 0065/1311] [IMP] gamification: add start goal button for draft goals bzr revid: mat@openerp.com-20130227111220-ipgvl5djlndcnxpw --- addons/gamification/goal.py | 5 ++++- addons/gamification/view/goal.xml | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/addons/gamification/goal.py b/addons/gamification/goal.py index 704ba2bad11..2b120eeaedf 100644 --- a/addons/gamification/goal.py +++ b/addons/gamification/goal.py @@ -208,7 +208,7 @@ class gamification_goal(osv.Model): for goal in self.browse(cr, uid, ids, context=context or {}): if goal.state not in ('inprogress','inprogress_update','reached'): - # skip if goal failed or canceled + # skip if goal draft, failed or canceled continue if goal.state == 'reached' and goal.end_date and fields.date.today() > goal.end_date: # only a goal reached but not passed the end date will still be @@ -263,6 +263,9 @@ class gamification_goal(osv.Model): self.write(cr, uid, [goal.id], towrite, context=context) return True + def action_start(self, cr, uid, ids, context=None): + self.write(cr, uid, ids, {'state': 'inprogress'}, context=context) + return self.update(cr, uid, ids, context=context) def action_reach(self, cr, uid, ids, context=None): return self.write(cr, uid, ids, {'state': 'reached'}, context=context) diff --git a/addons/gamification/view/goal.xml b/addons/gamification/view/goal.xml index c983076c9ad..a15e53a074e 100644 --- a/addons/gamification/view/goal.xml +++ b/addons/gamification/view/goal.xml @@ -40,7 +40,8 @@
-
-
-
@@ -69,9 +66,14 @@ - + - +
diff --git a/addons/gamification/view/plan.xml b/addons/gamification/view/plan.xml index 265deaaecd8..fbf4dc4a3e3 100644 --- a/addons/gamification/view/plan.xml +++ b/addons/gamification/view/plan.xml @@ -47,8 +47,8 @@ + + ' % i for i in webmain.manifest_list(req, 'js',db= db)), + 'css': "\n ".join('' % i for i in webmain.manifest_list(req, 'css', db=db)), + 'modules': simplejson.dumps(webmain.module_boot(req, db)), + 'init': 'var wc = new s.web.WebClient();wc.appendTo($(document.body)); s.base_calendar.meeting_invitation();' + } diff --git a/addons/base_calendar/static/src/js/base_calendar.js b/addons/base_calendar/static/src/js/base_calendar.js new file mode 100644 index 00000000000..0c7642bdf0b --- /dev/null +++ b/addons/base_calendar/static/src/js/base_calendar.js @@ -0,0 +1,6 @@ +openerp.base_calendar = function(openerp) { + + openerp.base_calendar.meeting_invitation = function(){ + console.log("daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + } +}; From 5cf8cef63d1450aaf3bb6b65f5d020409a247290 Mon Sep 17 00:00:00 2001 From: "Randhir Mayatra (OpenERP)" Date: Fri, 31 May 2013 16:46:18 +0530 Subject: [PATCH 0372/1311] [IMP] remove extra code bzr revid: rma@tinyerp.com-20130531111618-d6y2o446c6ad1fuf --- addons/base_calendar/service.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/addons/base_calendar/service.py b/addons/base_calendar/service.py index 0d6686f1f47..0d781ddbe0d 100644 --- a/addons/base_calendar/service.py +++ b/addons/base_calendar/service.py @@ -5,10 +5,7 @@ import openerp.addons.web.controllers.main as webmain class crm_meetting_importstatus(oeweb.Controller): _cp_path = '/meeting' @oeweb.httprequest - def meeting_invitation(self, req, dbname='test',res_id,uid): - url = self.pool.get('ir.config_parameter').get_param(cr, uid, 'web.base.url') - safe_url = urllib.quote_plus(url,debname,res_id,uid,':/?&;=') - + def meeting_invitation(self, req, db='may_29'): return webmain.html_template % { 'js': "\n ".join('' % i for i in webmain.manifest_list(req, 'js',db= db)), 'css': "\n ".join('' % i for i in webmain.manifest_list(req, 'css', db=db)), From 28c8d47912ae918ab930af3f18d4e8040b9e6242 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 31 May 2013 14:26:38 +0200 Subject: [PATCH 0373/1311] [ADD] business description of module bzr revid: mat@openerp.com-20130531122638-2h18azr1kcr929vq --- addons/gamification/html/index.html | 86 +++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 addons/gamification/html/index.html diff --git a/addons/gamification/html/index.html b/addons/gamification/html/index.html new file mode 100644 index 00000000000..890efa0d1ec --- /dev/null +++ b/addons/gamification/html/index.html @@ -0,0 +1,86 @@ +
+
+
+

Drive Engagement with Gamification

+

Leverage natural desire for competition

+

+ Reinforce good habits and improve win rates with real-time recognition and rewards inspired by game mechanics. Align teams around clear business objectives with challenges, personal objectives and team leader boards. +

+
+

Leaderboards

+
+ +
+

+ Promote leaders and competition amongst sales team with performance ratios. +

+
+
+

Personnal Objectives

+
+ +
+

+ Assign clear goals to users to align them with the company objectives. +

+
+
+

Visual Information

+
+ +
+

+ See in an glance the progress of each user. +

+
+
+
+ + +
+
+

Create custom Challenges

+
+

+Use predefined goals to generate easily your own challenges. Assign it to a team or individual users. Receive feedback as often as needed: daily, weekly... Repeat it automatically to compare progresses through time. +

+
+
+
+ +
+
+
+
+ +
+
+

Motivate with Badges

+
+
+ +
+
+
+

+Inspire achievement with recognition of coworker's good work by rewarding badges. These can be deserved manually or upon completion of challenges. Add fun to the competition with rare badges. +

+
+
+
+ +
+
+

Adapt to any module

+
+

+Create goals linked to any module. The evaluation system is very flexible and can be used for many different tasks : sales evaluation, creation of events, project completion or even helping new users to complete their profile. +

+
+
+
+ +
+
+
+
From a8eb9301e4284bb575031befe2eee5a0dba3b152 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Mon, 3 Jun 2013 18:03:14 +0200 Subject: [PATCH 0374/1311] [REF] gamification: code review of badge.py bzr revid: qdp-launchpad@openerp.com-20130603160314-zprlgye2rpd140pv --- addons/gamification/badge.py | 168 ++++++++----------------- addons/hr_gamification/gamification.py | 5 +- 2 files changed, 57 insertions(+), 116 deletions(-) diff --git a/addons/gamification/badge.py b/addons/gamification/badge.py index 2b681dca91e..fd63deceaf7 100644 --- a/addons/gamification/badge.py +++ b/addons/gamification/badge.py @@ -20,9 +20,7 @@ ############################################################################## from openerp.osv import fields, osv -from openerp import tools from openerp.tools.translate import _ -from openerp.tools.safe_eval import safe_eval from templates import TemplateHelper from datetime import date @@ -30,25 +28,22 @@ import logging _logger = logging.getLogger(__name__) - class gamification_badge_user(osv.Model): """User having received a badge""" _name = 'gamification.badge.user' _description = 'Gamification user badge' + _order = "create_date desc" _columns = { 'user_id': fields.many2one('res.users', string="User", required=True), 'badge_id': fields.many2one('gamification.badge', string='Badge', required=True), 'comment': fields.text('Comment'), - 'badge_name': fields.related('badge_id', 'name', type="char", string="Badge Name"), 'create_date': fields.datetime('Created', readonly=True), - 'create_uid': fields.many2one('res.users', 'Creator', readonly=True), + 'create_uid': fields.many2one('res.users', 'Creator', readonly=True), } - _order = "create_date desc" - class gamification_badge(osv.Model): """Badge object that users can send and receive""" @@ -57,70 +52,50 @@ class gamification_badge(osv.Model): _description = 'Gamification badge' _inherit = ['mail.thread'] - def _get_image(self, cr, uid, ids, name, args, context=None): - result = dict.fromkeys(ids, False) + + def _get_unique_global_list(self, cr, uid, ids, name, args, context=None): + """Return the list of unique res.users ids having received this badge""" + result = dict.fromkeys(ids, {'': False, '': False}) for obj in self.browse(cr, uid, ids, context=context): - result[obj.id] = tools.image_get_resized_images(obj.image) + res = set() + for owner in obj.owner_ids: + res.add(owner.user_id.id) + res = list(res) + result[obj.id]['unique_owner_ids'] = res + result[obj.id]['stat_count_distinct'] = len(res) return result + #TODO: use multi attribute to compute all the functional fields in a row for global/monthly/user wise (or not) stats def _get_global_count(self, cr, uid, ids, name, args, context=None): """Return the number of time this badge has been granted""" result = dict.fromkeys(ids, False) for obj in self.browse(cr, uid, ids, context=context): - result[obj.id] = len(self.pool.get('gamification.badge.user').search( - cr, uid, [('badge_id', '=', obj.id)], context=context)) result[obj.id] = len(obj.owner_ids) return result - - def _get_unique_global_list(self, cr, uid, ids, name, args, context=None): - """Return the list of unique res.users ids having received this badge""" - result = dict.fromkeys(ids, False) - for obj in self.browse(cr, uid, ids, context=context): - res = self.pool.get('gamification.badge.user').read_group( - cr, uid, domain=[('badge_id', '=', obj.id)], - fields=['badge_id', 'user_id'], - groupby=['user_id'], context=context) - result[obj.id] = [badge_user['user_id'][0] for badge_user in res] - return result - - def _get_unique_global_count(self, cr, uid, ids, name, args, context=None): - """Return the number of time this badge has been granted to individual users""" - result = dict.fromkeys(ids, False) - for obj in self.browse(cr, uid, ids, context=context): - res = self.pool.get('gamification.badge.user').read_group( - cr, uid, domain=[('badge_id', '=', obj.id)], - fields=['badge_id', 'user_id'], - groupby=['user_id'], context=context) - result[obj.id] = len(res) - return result - def _get_month_count(self, cr, uid, ids, name, args, context=None): """Return the number of time this badge has been granted this month""" + badge_user_obj = self.pool.get('gamification.badge.user') result = dict.fromkeys(ids, False) - first_month_day = date.today().replace(day=1).isoformat() + first_month_day = date.today().replace(day=1).isoformat()#TODO: this isn't good. Must use DEFAULT_SERVER_DATE_FORMAT for obj in self.browse(cr, uid, ids, context=context): - result[obj.id] = len(self.pool.get('gamification.badge.user').search( - cr, uid, [('badge_id', '=', obj.id), - ('create_date', '>=', first_month_day)], context=context)) + result[obj.id] = badge_user_obj.search(cr, uid, [('badge_id', '=', obj.id), ('create_date', '>=', first_month_day)], context=context, count=True) return result def _get_global_my_count(self, cr, uid, ids, name, args, context=None): """Return the number of time this badge has been granted to the current user""" + badge_user_obj = self.pool.get('gamification.badge.user') result = dict.fromkeys(ids, False) for obj in self.browse(cr, uid, ids, context=context): - result[obj.id] = len(self.pool.get('gamification.badge.user').search( - cr, uid, [('badge_id', '=', obj.id), ('user_id', '=', uid)], - context=context)) + result[obj.id] = badge_user_obj.search(cr, uid, [('badge_id', '=', obj.id), ('user_id', '=', uid)], context=context, count=True) return result def _get_month_my_count(self, cr, uid, ids, name, args, context=None): """Return the number of time this badge has been granted to the current user this month""" + badge_user_obj = self.pool.get('gamification.badge.user') result = dict.fromkeys(ids, False) - first_month_day = date.today().replace(day=1).isoformat() + first_month_day = date.today().replace(day=1).isoformat()#TODO: this isn't good. Must use DEFAULT_SERVER_DATE_FORMAT for obj in self.browse(cr, uid, ids, context=context): - result[obj.id] = len(self.pool.get('gamification.badge.user').search( - cr, uid, [('badge_id', '=', obj.id), ('user_id', '=', uid), - ('create_date', '>=', first_month_day)], context=context)) + result[obj.id] = badge_user_obj.search(cr, uid, [('badge_id', '=', obj.id), ('user_id', '=', uid), ('create_date', '>=', first_month_day)], context=context, count=True) return result def _get_month_my_sent(self, cr, uid, ids, name, args, context=None): @@ -142,20 +117,20 @@ class gamification_badge(osv.Model): """ result = dict.fromkeys(ids, False) for badge in self.browse(cr, uid, ids, context=context): - if self.can_grant_badge(cr, uid, uid, badge.id, context) != 1: + if self._can_grant_badge(cr, uid, uid, badge.id, context) != 1: + #if the user cannot grant this badge at all, result is 0 result[badge.id] = 0 elif not badge.rule_max: + #if there is no limitation, -1 is returned which mean 'infinite' result[badge.id] = -1 else: result[badge.id] = badge.rule_max_number - badge.stat_my_monthly_sending - return result _columns = { 'name': fields.char('Badge', required=True, translate=True), 'description': fields.text('Description'), - 'image': fields.binary("Image", - help="This field holds the image used for the badge, limited to 256x256"), + 'image': fields.binary("Image", help="This field holds the image used for the badge, limited to 256x256"), # image_select: selection with a on_change to fill image with predefined picts 'rule_auth': fields.selection([ ('everyone', 'Everyone'), @@ -163,7 +138,7 @@ class gamification_badge(osv.Model): ('having', 'People having some badges'), ('nobody', 'No one, assigned through challenges'), ], - string="Allowed to Grant", + string="Allowance to Grant", help="Who can grant this badge", required=True), 'rule_auth_user_ids': fields.many2many('res.users', 'rel_badge_auth_users', @@ -186,10 +161,8 @@ class gamification_badge(osv.Model): string='Remaining Sending Allowed', help="If a maxium is set"), 'plan_ids': fields.one2many('gamification.goal.plan', 'reward_id', - string="Reward for Challenges"), + string="Reward of Challenges"), - 'compute_code': fields.char('Compute Code', - help="The name of the python method that will be executed to verify if a user can receive this badge."), 'goal_type_ids': fields.many2many('gamification.goal.type', string='Goals Linked', help="The users that have succeeded theses goals will receive automatically the badge."), @@ -199,15 +172,17 @@ class gamification_badge(osv.Model): 'unique_owner_ids': fields.function(_get_unique_global_list, string='Unique Owners', help="The list of unique users having received this badge.", + multi='unique_users', type="many2many", relation="res.users"), 'stat_count': fields.function(_get_global_count, string='Total', type="integer", help="The number of time this badge has been received."), - 'stat_count_distinct': fields.function(_get_unique_global_count, + 'stat_count_distinct': fields.function(_get_unique_global_list, type="integer", string='Number of users', - help="The number of time this badge has been received by individual users."), + multi='unique_users', + help="The number of time this badge has been received by unique users."), 'stat_this_month': fields.function(_get_month_count, type="integer", string='Monthly total', @@ -226,10 +201,9 @@ class gamification_badge(osv.Model): 'stat_count_distinct': 0, 'stat_this_month': 0, 'rule_auth': 'everyone', - 'compute_code': "self.nobody(cr, uid, context)" } - def send_badge(self, cr, uid, badge_id, badge_user_ids, user_from=None, context=None): + def send_badge(self, cr, uid, badge_id, badge_user_ids, user_from=False, context=None): """Send a notification to a user for receiving a badge Does NOT verify constrains on badge granting. @@ -237,36 +211,22 @@ class gamification_badge(osv.Model): The stats counters are incremented :param badge_id: id of the badge to deserve :param badge_user_ids: list(int) of badge users that will receive the badge - :param user_from: res.users object that has sent the badge + :param user_from: optional id of the res.users object that has sent the badge """ - context = context or {} badge = self.browse(cr, uid, badge_id, context=context) template_env = TemplateHelper() res = None for badge_user in self.pool.get('gamification.badge.user').browse(cr, uid, badge_user_ids, context=context): - values = {'badge_user': badge_user} - - if user_from: - values['user_from'] = user_from - else: - values['user_from'] = False + values = {'badge_user': badge_user, 'user_from': user_from} body_html = template_env.get_template('badge_received.mako').render(values) - context['badge_user'] = badge_user - - res = self.message_post(cr, uid, badge.id, - body=body_html, - type='comment', - subtype='mt_comment', - context=context) - + res = self.message_post(cr, uid, badge.id, body=body_html, type='comment', subtype='mt_comment', context=context) return res def check_granting(self, cr, uid, user_from_id, badge_id, context=None): - """Check the user can grant a badge and raise the appropriate exception + """Check the user 'user_from_id' can grant the badge 'badge_id' and raise the appropriate exception if not""" - context = context or {} - status_code = self.can_grant_badge(cr, uid, user_from_id, badge_id, context) + status_code = self._can_grant_badge(cr, uid, user_from_id, badge_id, context=context) if status_code == 1: return True elif status_code == 2: @@ -281,7 +241,7 @@ class gamification_badge(osv.Model): _logger.exception("Unknown badge status code: %d" % int(status_code)) return False - def can_grant_badge(self, cr, uid, user_from_id, badge_id, context=None): + def _can_grant_badge(self, cr, uid, user_from_id, badge_id, context=None): """Check if a user can grant a badge to another user :param user_from_id: the id of the res.users trying to send the badge @@ -293,75 +253,57 @@ class gamification_badge(osv.Model): 4: don't have the required badges 5: user's monthly limit reached """ - context = context or {} badge = self.browse(cr, uid, badge_id, context=context) if badge.rule_auth == 'nobody': return 2 - elif badge.rule_auth == 'users': - if user_from_id not in [user.id for user in badge.rule_auth_user_ids]: - return 3 + elif badge.rule_auth == 'users' and user_from_id not in [user.id for user in badge.rule_auth_user_ids]: + return 3 elif badge.rule_auth == 'having': - badge_users = self.pool.get('gamification.badge.user').search( - cr, uid, [('user_id', '=', user_from_id)], context=context) - - if len(badge_users) == 0: - # the user_from has no badges - return 4 - - owners = [owner.id for owner in badge.owner_ids] - granted = False - for badge_user in badge_users: - if badge_user in owners: - granted = True - break - if not granted: - return 4 - - # else badge.rule_auth == 'everyone' -> no check + all_user_badges = self.pool.get('gamification.badge.user').search(cr, uid, [('user_id', '=', user_from_id)], context=context) + for required_badge in badge.rule_auth_badge_ids: + if required_badge.id not in all_user_badges: + return 4 if badge.rule_max and badge.stat_my_monthly_sending >= badge.rule_max_number: - # sent the maximum number of time this month return 5 + # badge.rule_auth == 'everyone' -> no check return 1 class grant_badge_wizard(osv.TransientModel): + """ Wizard allowing to grant a badge to a user""" + _name = 'gamification.badge.user.wizard' _columns = { 'user_id': fields.many2one("res.users", string='User', required=True), - 'badge_id': fields.many2one("gamification.badge", string='Badge', required=True), + 'badge_id': fields.many2one("gamification.badge", string='Badge', required=True), 'comment': fields.text('Comment'), } def action_grant_badge(self, cr, uid, ids, context=None): """Wizard action for sending a badge to a chosen user""" - if context is None: - context = {} badge_obj = self.pool.get('gamification.badge') badge_user_obj = self.pool.get('gamification.badge.user') for wiz in self.browse(cr, uid, ids, context=context): if uid == wiz.user_id.id: - raise osv.except_osv(_('Warning!'), _('You can not send a badge to yourself')) + raise osv.except_osv(_('Warning!'), _('You can not grant a badge to yourself')) - if badge_obj.check_granting(cr, uid, - user_from_id=uid, - badge_id=wiz.badge_id.id, - context=context): + #check if the badge granting is legitimate + if badge_obj.check_granting(cr, uid, user_from_id=uid, badge_id=wiz.badge_id.id, context=context): + #create the badge values = { 'user_id': wiz.user_id.id, 'badge_id': wiz.badge_id.id, 'comment': wiz.comment, } badge_user = badge_user_obj.create(cr, uid, values, context=context) + #notify the user + badge_obj.send_badge(cr, uid, wiz.badge_id.id, [badge_user], user_from=uid, context=context) - user_from = self.pool.get('res.users').browse(cr, uid, uid, context=context) - - badge_obj.send_badge(cr, uid, wiz.badge_id.id, [badge_user], user_from=user_from, context=context) - - return {} +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/hr_gamification/gamification.py b/addons/hr_gamification/gamification.py index bbda1e23ab1..ea86ba3b779 100644 --- a/addons/hr_gamification/gamification.py +++ b/addons/hr_gamification/gamification.py @@ -106,7 +106,7 @@ class hr_grant_badge_wizard(osv.TransientModel): if uid == wiz.user_id.id: raise osv.except_osv(_('Warning!'), _('You can not send a badge to yourself')) - if badge_obj.can_grant_badge(cr, uid, + if badge_obj._can_grant_badge(cr, uid, user_from_id=uid, badge_id=wiz.badge_id.id, context=context): @@ -119,9 +119,8 @@ class hr_grant_badge_wizard(osv.TransientModel): } badge_user = badge_user_obj.create(cr, uid, values, context=context) - user_from = self.pool.get('res.users').browse(cr, uid, uid, context=context) - badge_obj.send_badge(cr, uid, wiz.badge_id.id, [badge_user], user_from=user_from, context=context) + badge_obj.send_badge(cr, uid, wiz.badge_id.id, [badge_user], user_from=uid, context=context) return {} From 3887f78cef37b681ad67cd0cc96477c7b1e3ecd8 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Mon, 3 Jun 2013 22:30:53 +0200 Subject: [PATCH 0375/1311] [REF] gamification: code review (WIP) bzr revid: qdp-launchpad@openerp.com-20130603203053-dy7ia09rl0i1sxmd --- addons/gamification/doc/goal.rst | 4 +- addons/gamification/goal.py | 80 +++++++------------ addons/gamification/goal_type_data.py | 4 +- addons/gamification/goal_view.xml | 2 +- addons/gamification/plan.py | 2 +- addons/gamification/res_users.py | 4 +- .../static/src/xml/gamification.xml | 8 +- 7 files changed, 39 insertions(+), 65 deletions(-) diff --git a/addons/gamification/doc/goal.rst b/addons/gamification/doc/goal.rst index bdb5f71a253..e315931462f 100644 --- a/addons/gamification/doc/goal.rst +++ b/addons/gamification/doc/goal.rst @@ -33,7 +33,7 @@ Fields - ``last_update`` : the date of the last modification of this goal - ``computation_mode`` : related field from the linked goal type - ``type_description`` : related field from the linked goal type - - ``type_unit`` : related field from the linked goal type + - ``type_suffix`` : related field from the linked goal type - ``type_condition`` : related field from the linked goal type @@ -43,4 +43,4 @@ Methods - ``update`` : Compute the current value of goal and change states accordingly and send reminder if needed. - ``get_action`` : - Returns the action description specified for the goal modification. If an action XML ID is specified in the goal type definition it will be used. If a goal is manual, the action is a simple wizard to input a new value. \ No newline at end of file + Returns the action description specified for the goal modification. If an action XML ID is specified in the goal type definition it will be used. If a goal is manual, the action is a simple wizard to input a new value. diff --git a/addons/gamification/goal.py b/addons/gamification/goal.py index 2261f3a5490..561cd7a5807 100644 --- a/addons/gamification/goal.py +++ b/addons/gamification/goal.py @@ -41,17 +41,18 @@ class gamification_goal_type(osv.Model): """ _name = 'gamification.goal.type' _description = 'Gamification goal type' + _order = 'sequence' def _get_suffix(self, cr, uid, ids, field_name, arg, context=None): - res = dict.fromkeys(ids, 0) - for goal in self.browse(cr, uid, ids, context): - if goal.unit and not goal.monetary: - res[goal.id] = goal.unit + res = dict.fromkeys(ids, '') + for goal in self.browse(cr, uid, ids, context=context): + if goal.suffix and not goal.monetary: + res[goal.id] = goal.suffix elif goal.monetary: # use the current user's company currency user = self.pool.get('res.users').browse(cr, uid, uid, context) - if goal.unit: - res[goal.id] = "%s %s" % (user.company_id.currency_id.symbol, goal.unit) + if goal.suffix: + res[goal.id] = "%s %s" % (user.company_id.currency_id.symbol, goal.suffix) else: res[goal.id] = user.company_id.currency_id.symbol else: @@ -61,13 +62,9 @@ class gamification_goal_type(osv.Model): _columns = { 'name': fields.char('Goal Type', required=True, translate=True), 'description': fields.text('Goal Description'), - 'monetary': fields.boolean('Monetary Value', help="The target and current value are defined in the company currency."), - 'unit': fields.char('Suffix', - help="The unit of the target and current values", translate=True), - 'full_suffix': fields.function(_get_suffix, type="char", - string="Full Suffix", help="The currency and suffix field"), - + 'suffix': fields.char('Suffix', help="The unit of the target and current values", translate=True), + 'full_suffix': fields.function(_get_suffix, type="char", string="Full Suffix", help="The currency and suffix field"), 'computation_mode': fields.selection([ ('manually', 'Recorded manually'), ('count', 'Automatic: number of records'), @@ -75,13 +72,12 @@ class gamification_goal_type(osv.Model): ('python', 'Automatic: execute a specific Python code'), ], string="Computation Mode", - required=True), + required=True), #TODO: add help tooltip 'display_mode': fields.selection([ ('progress', 'Progressive (using numerical values)'), ('checkbox', 'Checkbox (done or not-done)'), ], string="Displayed as", required=True), - 'model_id': fields.many2one('ir.model', string='Model', help='The model object for the field to evaluate'), @@ -96,7 +92,6 @@ class gamification_goal_type(osv.Model): required=True), 'compute_code': fields.char('Compute Code', help="The name of the python method that will be executed to compute the current value. See the file gamification/goal_type_data.py for examples."), - 'condition': fields.selection([ ('higher', 'The higher the better'), ('lower', 'The lower the better') @@ -104,18 +99,13 @@ class gamification_goal_type(osv.Model): string='Goal Performance', help='A goal is considered as completed when the current value is compared to the value to reach', required=True), - 'sequence': fields.integer('Sequence', - help='Sequence number for ordering', - required=True), - - 'action_id': fields.many2one('ir.actions.act_window', - string="Action", + 'sequence': fields.integer('Sequence', help='Sequence number for ordering', required=True), + 'action_id': fields.many2one('ir.actions.act_window', string="Action", help="The action that will be called to update the goal value."), 'res_id_field': fields.char("ID Field of user", help="The field name on the user profile (res.users) containing the value for res_id for action.") } - _order = 'sequence' _defaults = { 'sequence': 1, 'condition': 'higher', @@ -137,19 +127,13 @@ class gamification_goal(osv.Model): def _get_completeness(self, cr, uid, ids, field_name, arg, context=None): """Return the percentage of completeness of the goal, between 0 and 100""" - res = {} - for goal in self.browse(cr, uid, ids, context): - if goal.type_condition == 'higher': - if goal.current > 0: - res[goal.id] = min(100, round(100.0 * goal.current / goal.target_goal, 2)) - else: - res[goal.id] = 0.0 - else: + res = dict.fromkeys(ids, 0.0) + for goal in self.browse(cr, uid, ids, context=context): + if goal.type_condition == 'higher' and goal.current > 0: + res[goal.id] = min(100, round(100.0 * goal.current / goal.target_goal, 2)) + elif goal.current < goal.target_goal: # a goal 'lower than' has only two values possible: 0 or 100% - if goal.current < goal.target_goal: - res[goal.id] = 100.0 - else: - res[goal.id] = 0.0 + res[goal.id] = 100.0 return res def on_change_type_id(self, cr, uid, ids, type_id=False, context=None): @@ -157,8 +141,7 @@ class gamification_goal(osv.Model): if not type_id: return {'value': {'type_id': False}} goal_type = goal_type.browse(cr, uid, type_id, context=context) - ret = {'value': {'computation_mode': goal_type.computation_mode, 'type_condition': goal_type.condition}} - return ret + return {'value': {'computation_mode': goal_type.computation_mode, 'type_condition': goal_type.condition}} _columns = { 'type_id': fields.many2one('gamification.goal.type', @@ -166,7 +149,7 @@ class gamification_goal(osv.Model): required=True, ondelete="cascade"), 'user_id': fields.many2one('res.users', string='User', required=True), - 'planline_id' : fields.many2one('gamification.goal.planline', + 'planline_id': fields.many2one('gamification.goal.planline', string='Goal Planline', ondelete="cascade"), 'plan_id': fields.related('planline_id', 'plan_id', @@ -207,7 +190,7 @@ class gamification_goal(osv.Model): 'type_description': fields.related('type_id', 'description', type='char', string='Type Description', readonly=True), - 'type_unit': fields.related('type_id', 'unit', + 'type_suffix': fields.related('type_id', 'suffix', type='char', string='Type Description', readonly=True), 'type_condition': fields.related('type_id', 'condition', type='char', string='Type Condition', readonly=True), @@ -224,6 +207,7 @@ class gamification_goal(osv.Model): } _order = 'create_date desc, end_date desc, type_id, id' +#TODO code review of this method def update(self, cr, uid, ids, context=None): """Update the goals to recomputes values and change of states @@ -232,7 +216,6 @@ class gamification_goal(osv.Model): If a goal reaches the target value, the status is set to reached If the end date is passed (at least +1 day, time not considered) without the target value being reached, the goal is set as failed.""" - if context is None: context = {} for goal in self.browse(cr, uid, ids, context=context): if goal.state in ('draft', 'canceled'): @@ -250,12 +233,8 @@ class gamification_goal(osv.Model): # generate a remind report template_env = TemplateHelper() - body_html = template_env.get_template('reminder.mako').render({'object':goal}) - self.message_post(cr, uid, goal.id, - body=body_html, - partner_ids=[goal.user_id.partner_id.id], - context=context, - subtype='mail.mt_comment') + body_html = template_env.get_template('reminder.mako').render({'object': goal}) + self.message_post(cr, uid, goal.id, body=body_html, partner_ids=[goal.user_id.partner_id.id], context=context, subtype='mail.mt_comment') elif goal.type_id.computation_mode == 'python': values = {'cr': cr, 'uid': goal.user_id.id, 'context': context, 'self': self.pool.get('gamification.goal.type')} @@ -294,10 +273,7 @@ class gamification_goal(osv.Model): towrite = {'current': len(res)} # check goal target reached - if (goal.type_id.condition == 'higher' \ - and towrite['current'] >= goal.target_goal) \ - or (goal.type_id.condition == 'lower' \ - and towrite['current'] <= goal.target_goal): + if (goal.type_id.condition == 'higher' and towrite['current'] >= goal.target_goal) or (goal.type_id.condition == 'lower' and towrite['current'] <= goal.target_goal): towrite['state'] = 'reached' # check goal failure @@ -418,19 +394,18 @@ class gamification_goal(osv.Model): values = {'state': 'reached', 'current': 1} self.write(cr, uid, goal_ids, values, context=context) +#TOCHECK: it's totally unclear for me what the 'current' and the update method... class goal_manual_wizard(osv.TransientModel): """Wizard type to update a manual goal""" _name = 'gamification.goal.wizard' _columns = { - 'goal_id': fields.many2one("gamification.goal", string='Goal'), + 'goal_id': fields.many2one("gamification.goal", string='Goal', required=True), 'current': fields.float('Current'), } def action_update_current(self, cr, uid, ids, context=None): """Wizard action for updating the current value""" - if context is None: - context = {} goal_obj = self.pool.get('gamification.goal') @@ -442,3 +417,4 @@ class goal_manual_wizard(osv.TransientModel): goal_obj.write(cr, uid, [wiz.goal_id.id], towrite, context=context) goal_obj.update(cr, uid, [wiz.goal_id.id], context=context) return {} +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/gamification/goal_type_data.py b/addons/gamification/goal_type_data.py index 6a04d39fdd3..944ca4fe92e 100644 --- a/addons/gamification/goal_type_data.py +++ b/addons/gamification/goal_type_data.py @@ -35,8 +35,6 @@ class gamification_goal_type_data(osv.Model): The model specified in 'xml_id' must inherit from mail.thread """ - if context is None: context = {} ref_obj = self.pool.get(xml_id) user = self.pool.get('res.users').browse(cr, uid, uid, context=context) - count = ref_obj.search(cr, uid, [('message_follower_ids', '=', user.partner_id.id)], count=True, context=context) - return count + return ref_obj.search(cr, uid, [('message_follower_ids', '=', user.partner_id.id)], count=True, context=context) diff --git a/addons/gamification/goal_view.xml b/addons/gamification/goal_view.xml index 073cbd69867..d1da5f027d1 100644 --- a/addons/gamification/goal_view.xml +++ b/addons/gamification/goal_view.xml @@ -250,7 +250,7 @@
- + diff --git a/addons/gamification/plan.py b/addons/gamification/plan.py index 0ae3dd36b2d..2541d9e500b 100644 --- a/addons/gamification/plan.py +++ b/addons/gamification/plan.py @@ -791,7 +791,7 @@ class gamification_goal_planline(osv.Model): }), 'type_condition': fields.related('type_id', 'condition', type="selection", readonly=True, string="Condition", selection=[('lower', '<='), ('higher', '>=')]), - 'type_unit': fields.related('type_id', 'unit', type="char", readonly=True, string="Unit"), + 'type_suffix': fields.related('type_id', 'suffix', type="char", readonly=True, string="Unit"), 'type_monetary': fields.related('type_id', 'monetary', type="boolean", readonly=True, string="Monetary"), 'type_full_suffix': fields.related('type_id', 'full_suffix', type="char", readonly=True, string="Suffix"), } diff --git a/addons/gamification/res_users.py b/addons/gamification/res_users.py index 18034880d68..1798282119c 100644 --- a/addons/gamification/res_users.py +++ b/addons/gamification/res_users.py @@ -56,7 +56,7 @@ class res_users_gamification_group(osv.Model): 'type_condition': planline_board['goal_type'].condition, 'type_computation_mode': planline_board['goal_type'].computation_mode, 'type_monetary': planline_board['goal_type'].monetary, - 'type_unit': planline_board['goal_type'].unit, + 'type_suffix': planline_board['goal_type'].suffix, 'type_action': True if planline_board['goal_type'].action_id else False, 'type_display': planline_board['goal_type'].display_mode, 'target_goal': planline_board['target_goal'], @@ -95,7 +95,7 @@ class res_users_gamification_group(osv.Model): 'type_description': goal.type_description, 'type_condition': goal.type_id.condition, 'type_monetary': goal.type_id.monetary, - 'type_unit': goal.type_id.unit, + 'type_suffix': goal.type_id.suffix, 'type_action': True if goal.type_id.action_id else False, 'type_display': goal.type_id.display_mode, 'state': goal.state, diff --git a/addons/gamification/static/src/xml/gamification.xml b/addons/gamification/static/src/xml/gamification.xml index 8aa577bb4fe..9f6bc1c81e9 100644 --- a/addons/gamification/static/src/xml/gamification.xml +++ b/addons/gamification/static/src/xml/gamification.xml @@ -34,7 +34,7 @@ Target: <= - + @@ -55,7 +55,7 @@ Target: <= - + @@ -73,11 +73,11 @@
- + - + From f48f396096abdc5f13f2256d2ddec2669703340a Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Wed, 5 Jun 2013 13:33:39 +0200 Subject: [PATCH 0376/1311] [FIX] gamification: correct function calls with multi bzr revid: mat@openerp.com-20130605113339-6tiytkoeebd5s89w --- addons/gamification/badge.py | 88 ++++++++++++++---------------------- 1 file changed, 34 insertions(+), 54 deletions(-) diff --git a/addons/gamification/badge.py b/addons/gamification/badge.py index fd63deceaf7..53e7eb84b05 100644 --- a/addons/gamification/badge.py +++ b/addons/gamification/badge.py @@ -20,6 +20,7 @@ ############################################################################## from openerp.osv import fields, osv +from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as DF from openerp.tools.translate import _ from templates import TemplateHelper @@ -52,60 +53,37 @@ class gamification_badge(osv.Model): _description = 'Gamification badge' _inherit = ['mail.thread'] - - def _get_unique_global_list(self, cr, uid, ids, name, args, context=None): - """Return the list of unique res.users ids having received this badge""" - result = dict.fromkeys(ids, {'': False, '': False}) + def _get_owners_info(self, cr, uid, ids, name, args, context=None): + """Return: + the list of unique res.users ids having received this badge + the total number of time this badge was granted + the total number of users this badge was granted to + """ + result = dict.fromkeys(ids, False) for obj in self.browse(cr, uid, ids, context=context): res = set() for owner in obj.owner_ids: res.add(owner.user_id.id) res = list(res) - result[obj.id]['unique_owner_ids'] = res - result[obj.id]['stat_count_distinct'] = len(res) + result[obj.id] = { + 'unique_owner_ids': res, + 'stat_count': len(obj.owner_ids), + 'stat_count_distinct': len(res) + } return result - #TODO: use multi attribute to compute all the functional fields in a row for global/monthly/user wise (or not) stats - def _get_global_count(self, cr, uid, ids, name, args, context=None): - """Return the number of time this badge has been granted""" + def _get_badge_user_stats(self, cr, uid, ids, name, args, context=None): + """Return stats related to badge users""" result = dict.fromkeys(ids, False) - for obj in self.browse(cr, uid, ids, context=context): - result[obj.id] = len(obj.owner_ids) - return result - def _get_month_count(self, cr, uid, ids, name, args, context=None): - """Return the number of time this badge has been granted this month""" badge_user_obj = self.pool.get('gamification.badge.user') - result = dict.fromkeys(ids, False) - first_month_day = date.today().replace(day=1).isoformat()#TODO: this isn't good. Must use DEFAULT_SERVER_DATE_FORMAT + first_month_day = date.today().replace(day=1).strftime(DF) for obj in self.browse(cr, uid, ids, context=context): - result[obj.id] = badge_user_obj.search(cr, uid, [('badge_id', '=', obj.id), ('create_date', '>=', first_month_day)], context=context, count=True) - return result - - def _get_global_my_count(self, cr, uid, ids, name, args, context=None): - """Return the number of time this badge has been granted to the current user""" - badge_user_obj = self.pool.get('gamification.badge.user') - result = dict.fromkeys(ids, False) - for obj in self.browse(cr, uid, ids, context=context): - result[obj.id] = badge_user_obj.search(cr, uid, [('badge_id', '=', obj.id), ('user_id', '=', uid)], context=context, count=True) - return result - - def _get_month_my_count(self, cr, uid, ids, name, args, context=None): - """Return the number of time this badge has been granted to the current user this month""" - badge_user_obj = self.pool.get('gamification.badge.user') - result = dict.fromkeys(ids, False) - first_month_day = date.today().replace(day=1).isoformat()#TODO: this isn't good. Must use DEFAULT_SERVER_DATE_FORMAT - for obj in self.browse(cr, uid, ids, context=context): - result[obj.id] = badge_user_obj.search(cr, uid, [('badge_id', '=', obj.id), ('user_id', '=', uid), ('create_date', '>=', first_month_day)], context=context, count=True) - return result - - def _get_month_my_sent(self, cr, uid, ids, name, args, context=None): - """Return the number of time this badge has been granted to the current user this month""" - result = dict.fromkeys(ids, False) - first_month_day = date.today().replace(day=1).isoformat() - for obj in self.browse(cr, uid, ids, context=context): - result[obj.id] = len(self.pool.get('gamification.badge.user').search( - cr, uid, [('badge_id', '=', obj.id), ('create_uid', '=', uid), - ('create_date', '>=', first_month_day)], context=context)) + result[obj.id] = { + 'stat_my': badge_user_obj.search(cr, uid, [('badge_id', '=', obj.id), ('user_id', '=', uid)], context=context, count=True), + 'stat_this_month': badge_user_obj.search(cr, uid, [('badge_id', '=', obj.id), ('create_date', '>=', first_month_day)], context=context, count=True), + 'stat_my_this_month': badge_user_obj.search(cr, uid, [('badge_id', '=', obj.id), ('user_id', '=', uid), ('create_date', '>=', first_month_day)], context=context, count=True), + 'stat_my_monthly_sending': badge_user_obj.search(cr, uid, [('badge_id', '=', obj.id), ('create_uid', '=', uid), ('create_date', '>=', first_month_day)], context=context, count=True) + } return result def _remaining_sending_calc(self, cr, uid, ids, name, args, context=None): @@ -153,9 +131,10 @@ class gamification_badge(osv.Model): help="Check to set a monthly limit per person of sending this badge"), 'rule_max_number': fields.integer('Limitation Number', help="The maximum number of time this badge can be sent per month per person."), - 'stat_my_monthly_sending': fields.function(_get_month_my_sent, + 'stat_my_monthly_sending': fields.function(_get_badge_user_stats, type="integer", string='My Monthly Sending Total', + multi='badge_users', help="The number of time the current user has sent this badge this month."), 'remaining_sending': fields.function(_remaining_sending_calc, type='integer', string='Remaining Sending Allowed', help="If a maxium is set"), @@ -169,37 +148,38 @@ class gamification_badge(osv.Model): 'owner_ids': fields.one2many('gamification.badge.user', 'badge_id', string='Owners', help='The list of instances of this badge granted to users'), - 'unique_owner_ids': fields.function(_get_unique_global_list, + 'unique_owner_ids': fields.function(_get_owners_info, string='Unique Owners', help="The list of unique users having received this badge.", multi='unique_users', type="many2many", relation="res.users"), - 'stat_count': fields.function(_get_global_count, string='Total', + 'stat_count': fields.function(_get_owners_info, string='Total', type="integer", + multi='unique_users', help="The number of time this badge has been received."), - 'stat_count_distinct': fields.function(_get_unique_global_list, + 'stat_count_distinct': fields.function(_get_owners_info, type="integer", string='Number of users', multi='unique_users', help="The number of time this badge has been received by unique users."), - 'stat_this_month': fields.function(_get_month_count, + 'stat_this_month': fields.function(_get_badge_user_stats, type="integer", string='Monthly total', + multi='badge_users', help="The number of time this badge has been received this month."), - 'stat_my': fields.function(_get_global_my_count, string='My Total', + 'stat_my': fields.function(_get_badge_user_stats, string='My Total', type="integer", + multi='badge_users', help="The number of time the current user has received this badge."), - 'stat_my_this_month': fields.function(_get_month_my_count, + 'stat_my_this_month': fields.function(_get_badge_user_stats, type="integer", string='My Monthly Total', + multi='badge_users', help="The number of time the current user has received this badge this month."), } _defaults = { - 'stat_count': 0, - 'stat_count_distinct': 0, - 'stat_this_month': 0, 'rule_auth': 'everyone', } From dceeb97c5f26d9c012c25c579eed7a58ab446f75 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Thu, 6 Jun 2013 10:39:28 +0200 Subject: [PATCH 0377/1311] [IMP] gamification: add tooltip and explain update method bzr revid: mat@openerp.com-20130606083928-k00b7ydioi3ed5ra --- addons/gamification/goal.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/addons/gamification/goal.py b/addons/gamification/goal.py index 561cd7a5807..a511b2d5bcc 100644 --- a/addons/gamification/goal.py +++ b/addons/gamification/goal.py @@ -72,7 +72,8 @@ class gamification_goal_type(osv.Model): ('python', 'Automatic: execute a specific Python code'), ], string="Computation Mode", - required=True), #TODO: add help tooltip + help="Defined how will be computed the goals. The result of the operation will be stored in the field 'Current'.", + required=True), 'display_mode': fields.selection([ ('progress', 'Progressive (using numerical values)'), ('checkbox', 'Checkbox (done or not-done)'), @@ -223,8 +224,8 @@ class gamification_goal(osv.Model): continue if goal.type_id.computation_mode == 'manually': + # no comoutation for manual goals, only check for reminders towrite = {'current': goal.current} - # check for remind to update if goal.remind_update_delay and goal.last_update: delta_max = timedelta(days=goal.remind_update_delay) last_update = datetime.strptime(goal.last_update, '%Y-%m-%d').date() @@ -237,6 +238,7 @@ class gamification_goal(osv.Model): self.message_post(cr, uid, goal.id, body=body_html, partner_ids=[goal.user_id.partner_id.id], context=context, subtype='mail.mt_comment') elif goal.type_id.computation_mode == 'python': + # execute the chosen method values = {'cr': cr, 'uid': goal.user_id.id, 'context': context, 'self': self.pool.get('gamification.goal.type')} result = safe_eval(goal.type_id.compute_code, values, {}) @@ -251,6 +253,7 @@ class gamification_goal(osv.Model): obj = self.pool.get(goal.type_id.model_id.model) field_date_name = goal.type_id.field_date_id.name + # eval the domain with user_id replaced by goal user domain = safe_eval(goal.type_id.domain, {'user_id': goal.user_id.id}) if goal.start_date and field_date_name: From 24c879ffd5f8e59552c5efa31933cdb68e7df2f7 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Tue, 4 Jun 2013 20:37:39 +0200 Subject: [PATCH 0378/1311] [REF] gamificiation: code review. WIP going on bzr revid: qdp-launchpad@openerp.com-20130604183739-uskl2x369w8hydhr --- addons/gamification/goal.py | 94 ++++++++++++-------------- addons/gamification/goal_base_data.xml | 2 +- addons/gamification/goal_type_data.py | 1 + addons/gamification/res_users.py | 11 +-- addons/gamification/templates.py | 10 ++- 5 files changed, 58 insertions(+), 60 deletions(-) diff --git a/addons/gamification/goal.py b/addons/gamification/goal.py index a511b2d5bcc..896b476f158 100644 --- a/addons/gamification/goal.py +++ b/addons/gamification/goal.py @@ -145,14 +145,11 @@ class gamification_goal(osv.Model): return {'value': {'computation_mode': goal_type.computation_mode, 'type_condition': goal_type.condition}} _columns = { - 'type_id': fields.many2one('gamification.goal.type', - string='Goal Type', - required=True, - ondelete="cascade"), + 'type_id': fields.many2one('gamification.goal.type', string='Goal Type', required=True, ondelete="cascade"), + #TODO: add some tooltip/grey label in view to explain that people can use 'user_id' in their domain + #TODO: actually it would be better to use 'user.id' in the domain definition, because it means user is a browse record and it's more flexible (i can do '[(country_id,=,user.partner_id.country_id.id)]) 'user_id': fields.many2one('res.users', string='User', required=True), - 'planline_id': fields.many2one('gamification.goal.planline', - string='Goal Planline', - ondelete="cascade"), + 'planline_id': fields.many2one('gamification.goal.planline', string='Goal Planline', ondelete="cascade"), 'plan_id': fields.related('planline_id', 'plan_id', string="Plan", type='many2one', @@ -163,12 +160,8 @@ class gamification_goal(osv.Model): 'target_goal': fields.float('To Reach', required=True, track_visibility='always'), # no goal = global index - 'current': fields.float('Current', - required=True, - track_visibility='always'), - 'completeness': fields.function(_get_completeness, - type='float', - string='Completeness'), + 'current': fields.float('Current Value', required=True, track_visibility='always'), + 'completeness': fields.function(_get_completeness, type='float', string='Completeness'), 'state': fields.selection([ ('draft', 'Draft'), ('inprogress', 'In progress'), @@ -181,24 +174,17 @@ class gamification_goal(osv.Model): required=True, track_visibility='always'), - 'computation_mode': fields.related('type_id', 'computation_mode', - type='char', - string="Type computation mode"), + 'computation_mode': fields.related('type_id', 'computation_mode', type='char', string="Type computation mode"), 'remind_update_delay': fields.integer('Remind delay', help="The number of days after which the user assigned to a manual goal will be reminded. Never reminded if no value is specified."), 'last_update': fields.date('Last Update', help="In case of manual goal, reminders are sent if the goal as not been updated for a while (defined in goal plan). Ignored in case of non-manual goal or goal not linked to a plan."), - 'type_description': fields.related('type_id', 'description', - type='char', string='Type Description', readonly=True), - 'type_suffix': fields.related('type_id', 'suffix', - type='char', string='Type Description', readonly=True), - 'type_condition': fields.related('type_id', 'condition', - type='char', string='Type Condition', readonly=True), - 'type_suffix': fields.related('type_id', 'full_suffix', - type="char", string="Suffix", readonly=True), - 'type_display': fields.related('type_id', 'display_mode', - type="char", string="Display Mode", readonly=True), + 'type_description': fields.related('type_id', 'description', type='char', string='Type Description', readonly=True), + 'type_suffix': fields.related('type_id', 'suffix', type='char', string='Type Description', readonly=True), + 'type_condition': fields.related('type_id', 'condition', type='char', string='Type Condition', readonly=True), + 'type_suffix': fields.related('type_id', 'full_suffix', type="char", string="Suffix", readonly=True), + 'type_display': fields.related('type_id', 'display_mode', type="char", string="Display Mode", readonly=True), } _defaults = { @@ -208,7 +194,6 @@ class gamification_goal(osv.Model): } _order = 'create_date desc, end_date desc, type_id, id' -#TODO code review of this method def update(self, cr, uid, ids, context=None): """Update the goals to recomputes values and change of states @@ -219,13 +204,16 @@ class gamification_goal(osv.Model): the target value being reached, the goal is set as failed.""" for goal in self.browse(cr, uid, ids, context=context): + #TODO: towrite may be falsy, to avoid useless write on the object. Please check the whole thing is still working + towrite = {} if goal.state in ('draft', 'canceled'): # skip if goal draft or canceled continue if goal.type_id.computation_mode == 'manually': - # no comoutation for manual goals, only check for reminders - towrite = {'current': goal.current} + + #TODO: put this code section in a separated method + # check for remind to update if goal.remind_update_delay and goal.last_update: delta_max = timedelta(days=goal.remind_update_delay) last_update = datetime.strptime(goal.last_update, '%Y-%m-%d').date() @@ -242,10 +230,9 @@ class gamification_goal(osv.Model): values = {'cr': cr, 'uid': goal.user_id.id, 'context': context, 'self': self.pool.get('gamification.goal.type')} result = safe_eval(goal.type_id.compute_code, values, {}) - if type(result) == float or type(result) == int or type(result) == long: + if type(result) in (float, int, long) and result != goal.current: towrite = {'current': result} else: - towrite = {'current': goal.current} _logger.exception(_('Unvalid return content from the evaluation of %s' % str(goal.type_id.compute_code))) # raise osv.except_osv(_('Error!'), _('Unvalid return content from the evaluation of %s' % str(goal.type_id.compute_code))) @@ -254,8 +241,9 @@ class gamification_goal(osv.Model): field_date_name = goal.type_id.field_date_id.name # eval the domain with user_id replaced by goal user - domain = safe_eval(goal.type_id.domain, - {'user_id': goal.user_id.id}) + domain = safe_eval(goal.type_id.domain, {'user_id': goal.user_id.id}) # TODO: {'user_id': goal.user_id} (see above comment) + + #add temporal clause(s) to the domain if fields are filled on the goal if goal.start_date and field_date_name: domain.append((field_date_name, '>=', goal.start_date)) if goal.end_date and field_date_name: @@ -263,26 +251,26 @@ class gamification_goal(osv.Model): if goal.type_id.computation_mode == 'sum': field_name = goal.type_id.field_id.name - res = obj.read_group(cr, uid, domain, [field_name], - [''], context=context) - if res[0][field_name]: - towrite = {'current': res[0][field_name]} - else: - # avoid false when no result - towrite = {'current': 0} + res = obj.read_group(cr, uid, domain, [field_name], [''], context=context) + new_value = res and res[0][field_name] or 0.0 else: # computation mode = count - res = obj.search(cr, uid, domain, context=context) - towrite = {'current': len(res)} + new_value = obj.search(cr, uid, domain, context=context, count=True) + + #avoid useless write if the new value is the same as the old one + if new_value != goal.current: + towrite = {'current': new_value} # check goal target reached - if (goal.type_id.condition == 'higher' and towrite['current'] >= goal.target_goal) or (goal.type_id.condition == 'lower' and towrite['current'] <= goal.target_goal): + #TODO: reached condition is wrong because it should check time constraints. + if (goal.type_id.condition == 'higher' and towrite.get('current', goal.current) >= goal.target_goal) or (goal.type_id.condition == 'lower' and towrite.get('current', goal.curren) <= goal.target_goal): towrite['state'] = 'reached' # check goal failure elif goal.end_date and fields.date.today() > goal.end_date: towrite['state'] = 'failed' - self.write(cr, uid, [goal.id], towrite, context=context) + if towrite: + self.write(cr, uid, [goal.id], towrite, context=context) return True def action_start(self, cr, uid, ids, context=None): @@ -315,6 +303,7 @@ class gamification_goal(osv.Model): def create(self, cr, uid, vals, context=None): """Overwrite the create method to add a 'just_created' field to True""" + #TODO: rename just_created into something more explicit (related to the effect, not to the cause) like 'avoid_onchange_log', for example context = context or {} context['just_created'] = True return super(gamification_goal, self).create(cr, uid, vals, context=context) @@ -350,14 +339,15 @@ class gamification_goal(osv.Model): """ goal = self.browse(cr, uid, goal_id, context=context) if goal.type_id.action_id: + #open a the action linked on the goal action = goal.type_id.action_id.read()[0] if goal.type_id.res_id_field: - current_user = self.pool.get('res.users').browse(cr, uid, uid, context) - # eg : company_id.id + current_user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + # this loop manages the cases where res_id_field is a browse record path (eg : company_id.currency_id.id) field_names = goal.type_id.res_id_field.split('.') - res = current_user.__getitem__(field_names[0]) - for field_name in field_names[1:]: + res = current_user + for field_name in field_names[:]: res = res.__getitem__(field_name) action['res_id'] = res @@ -368,12 +358,12 @@ class gamification_goal(osv.Model): views = [(view_id, mode)] break action['views'] = views - return action if goal.computation_mode == 'manually': + #open a wizard window to update the value manually action = { - 'name': "Update %s" % goal.type_id.name, + 'name': _("Update %s") % goal.type_id.name, 'id': goal_id, 'type': 'ir.actions.act_window', 'views': [[False, 'form']], @@ -384,6 +374,8 @@ class gamification_goal(osv.Model): return action return False + #TODO: i find a bit annoying to have this method on the goal class instead of being on only the goal type related. + # i wouldn't put this in a dedicated method... well i think something is wrong conceptually with this thing def changed_users_avatar(self, cr, uid, ids, context): """Warning a user changed his avatar. @@ -392,12 +384,12 @@ class gamification_goal(osv.Model): Set the state of every type_base_avatar linked to ids to 'reached' and set current value to 1. """ + goal_type_id = self.pool['ir.model.data'].get_object(cr, uid, 'gamification', 'type_base_avatar', context) goal_ids = self.search(cr, uid, [('type_id', '=', goal_type_id.id), ('user_id', 'in', ids)], context=context) values = {'state': 'reached', 'current': 1} self.write(cr, uid, goal_ids, values, context=context) -#TOCHECK: it's totally unclear for me what the 'current' and the update method... class goal_manual_wizard(osv.TransientModel): """Wizard type to update a manual goal""" diff --git a/addons/gamification/goal_base_data.xml b/addons/gamification/goal_base_data.xml index 77cf830335a..17886a6144c 100644 --- a/addons/gamification/goal_base_data.xml +++ b/addons/gamification/goal_base_data.xml @@ -16,7 +16,7 @@ Set your Avatar - In your user perference + In your user preference manually checkbox diff --git a/addons/gamification/goal_type_data.py b/addons/gamification/goal_type_data.py index 944ca4fe92e..24be886ab75 100644 --- a/addons/gamification/goal_type_data.py +++ b/addons/gamification/goal_type_data.py @@ -30,6 +30,7 @@ class gamification_goal_type_data(osv.Model): """ _inherit = 'gamification.goal.type' +#TODO: is it usefull to have this method in a standalone file? why not directly in the computation field of the related goal type? def number_following(self, cr, uid, xml_id="mail.thread", context=None): """Return the number of 'xml_id' objects the user is following diff --git a/addons/gamification/res_users.py b/addons/gamification/res_users.py index 1798282119c..c0458263e05 100644 --- a/addons/gamification/res_users.py +++ b/addons/gamification/res_users.py @@ -1,6 +1,6 @@ from openerp.osv import osv - +#TODO add copyright and last line for vim users class res_users_gamification_group(osv.Model): """ Update of res.users class - if adding groups to an user, check gamification.goal.plan linked to @@ -25,6 +25,7 @@ class res_users_gamification_group(osv.Model): self.pool.get('gamification.goal').changed_users_avatar(cr, uid, ids, context) return write_res +#TODO: this method is not clear about what she returns (list of dict but the dict's keys may be different from one to another)... it's doubtfull conceptually. Don't we need some more doc? why is this method looking so overkill? def get_goals_todo_info(self, cr, uid, context=None): """Return the list of goals assigned to the user, grouped by plan""" all_goals_info = [] @@ -67,7 +68,7 @@ class res_users_gamification_group(osv.Model): continue vals['goals'].append({ - 'rank': goal[0]+1, + 'rank': goal[0] + 1, 'id': goal[1].id, 'user_id': goal[1].user_id.id, 'user_name': goal[1].user_id.name, @@ -110,10 +111,10 @@ class res_users_gamification_group(osv.Model): def get_challenge_suggestions(self, cr, uid, context=None): """Return the list of goal plans suggested to the user""" - if context is None: context = {} plan_info = [] - plan_ids = self.pool.get('gamification.goal.plan').search(cr, uid, [('proposed_user_ids', 'in', uid), ('state', '=', 'inprogress')], context=context) - for plan in self.pool.get('gamification.goal.plan').browse(cr, uid, plan_ids, context=context): + goal_plan_obj = self.pool.get('gamification.goal.plan') + plan_ids = goal_plan_obj.search(cr, uid, [('proposed_user_ids', 'in', uid), ('state', '=', 'inprogress')], context=context) + for plan in goal_plan_obj.browse(cr, uid, plan_ids, context=context): values = { 'id': plan.id, 'name': plan.name, diff --git a/addons/gamification/templates.py b/addons/gamification/templates.py index 21e6b8e0b4f..fb9c23c3e3d 100644 --- a/addons/gamification/templates.py +++ b/addons/gamification/templates.py @@ -8,18 +8,22 @@ from jinja2.sandbox import SandboxedEnvironment from jinja2 import FileSystemLoader - + from urllib import urlencode, quote as quote import os.path +#TODO: to check: new dependancies in openerp? fine or not? +#TODO: to check: if it's ok, i think it would be better directly in the server (tools) so that other modules that doesn't depend on gamification can use it +#TODO; someone else should check this code, i'm not the good one + class TemplateHelper(SandboxedEnvironment): GAMIFICATION_PATH = os.path.dirname(os.path.abspath(__file__)) def __init__(self): - super(TemplateHelper,self).__init__( - loader=FileSystemLoader(os.path.join(self.GAMIFICATION_PATH,'templates/')), + super(TemplateHelper, self).__init__( + loader=FileSystemLoader(os.path.join(self.GAMIFICATION_PATH, 'templates/')), block_start_string="<%", block_end_string="%>", variable_start_string="${", From f0151cd6e4c918dab3edfe5856371bae36d79b4e Mon Sep 17 00:00:00 2001 From: Mohammed Shekha Date: Wed, 5 Jun 2013 15:23:42 +0530 Subject: [PATCH 0379/1311] [FIX]Fixed the issue of search on tranlsated field, search on tranlsated field fails due to expression parsing which fetches ids from ir_translation as well as working table and UNION of this makes search fruitless, also search fails for in language other then english when you enter part of a string for the field to search. bzr revid: msh@openerp.com-20130605095342-314zfmttymx1k86a --- openerp/osv/expression.py | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index 58f35149ddd..b2184f73753 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -1020,37 +1020,32 @@ class expression(object): elif field.translate: need_wildcard = operator in ('like', 'ilike', 'not like', 'not ilike') sql_operator = {'=like': 'like', '=ilike': 'ilike'}.get(operator, operator) + instr = ' %s' if need_wildcard: right = '%%%s%%' % right - subselect = '( SELECT res_id' \ - ' FROM ir_translation' \ - ' WHERE name = %s' \ - ' AND lang = %s' \ - ' AND type = %s' - instr = ' %s' + select = 'WITH temp_'+working_model._table+' (id, name) as (' \ + 'SELECT wt.id, coalesce(it.value,wt.name) ' \ + 'FROM '+working_model._table+' wt ' \ + 'LEFT JOIN ir_translation it ON (it.name = %s and ' \ + 'it.lang = %s and ' \ + 'it.type = %s and ' \ + 'it.res_id = wt.id and ' \ + "it.value != '')" \ + ') ' #Covering in,not in operators with operands (%s,%s) ,etc. if sql_operator in ['in', 'not in']: instr = ','.join(['%s'] * len(right)) - subselect += ' AND value ' + sql_operator + ' ' + " (" + instr + ")" \ - ') UNION (' \ - ' SELECT id' \ - ' FROM "' + working_model._table + '"' \ - ' WHERE "' + left + '" ' + sql_operator + ' ' + " (" + instr + "))" + select += 'SELECT id FROM temp_'+working_model._table+' WHERE "name" '+sql_operator+' ('+instr+') order by name' else: - subselect += ' AND value ' + sql_operator + instr + \ - ') UNION (' \ - ' SELECT id' \ - ' FROM "' + working_model._table + '"' \ - ' WHERE "' + left + '" ' + sql_operator + instr + ")" + select += 'SELECT id FROM temp_'+working_model._table+' WHERE "name" '+sql_operator+' '+instr+' order by name' params = [working_model._name + ',' + left, context.get('lang', False) or 'en_US', 'model', right, - right, ] - push(create_substitution_leaf(leaf, ('id', 'inselect', (subselect, params)), working_model)) + push(create_substitution_leaf(leaf, ('id', 'inselect', (select, params)), working_model)) else: push_result(leaf) From bb17b02de508526a90172def7f299636a655eb24 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Thu, 6 Jun 2013 14:47:08 +0200 Subject: [PATCH 0380/1311] [IMP] gamification: more flexible domain, using email_templates bzr revid: mat@openerp.com-20130606124708-3occhsgwkaxwzyp3 --- addons/gamification/__openerp__.py | 2 +- addons/gamification/badge.py | 16 ++- addons/gamification/badge_data.xml | 18 +++ addons/gamification/goal.py | 60 +++++----- addons/gamification/goal_base_data.xml | 110 +++++++++++++++++- addons/gamification/plan.py | 23 ++-- addons/gamification/res_users.py | 24 +++- addons/gamification/templates.py | 1 - addons/gamification/templates/reminder.mako | 3 +- .../gamification_sale_crm/sale_crm_goals.xml | 22 ++-- 10 files changed, 222 insertions(+), 57 deletions(-) diff --git a/addons/gamification/__openerp__.py b/addons/gamification/__openerp__.py index 0143f266d35..aaa4565cf43 100644 --- a/addons/gamification/__openerp__.py +++ b/addons/gamification/__openerp__.py @@ -23,7 +23,7 @@ 'version': '1.0', 'author': 'OpenERP SA', 'category': 'Human Ressources', - 'depends': ['mail'], + 'depends': ['mail', 'email_template'], 'description': """ Gamification process ==================== diff --git a/addons/gamification/badge.py b/addons/gamification/badge.py index 53e7eb84b05..2c44fd2f182 100644 --- a/addons/gamification/badge.py +++ b/addons/gamification/badge.py @@ -23,7 +23,7 @@ from openerp.osv import fields, osv from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as DF from openerp.tools.translate import _ -from templates import TemplateHelper +# from templates import TemplateHelper from datetime import date import logging @@ -194,12 +194,20 @@ class gamification_badge(osv.Model): :param user_from: optional id of the res.users object that has sent the badge """ badge = self.browse(cr, uid, badge_id, context=context) - template_env = TemplateHelper() + # template_env = TemplateHelper() res = None + temp_obj = self.pool.get('email.template') + template_id = self.pool['ir.model.data'].get_object(cr, uid, 'gamification', 'email_template_badge_received', context) + ctx = context.copy() for badge_user in self.pool.get('gamification.badge.user').browse(cr, uid, badge_user_ids, context=context): - values = {'badge_user': badge_user, 'user_from': user_from} - body_html = template_env.get_template('badge_received.mako').render(values) + + ctx.update({'user_from': self.pool.get('res.users').browse(cr, uid, user_from).name}) + + body_html = temp_obj.render_template(cr, uid, template_id.body_html, 'gamification.badge.user', badge_user.id, context=ctx) + + # values = {'badge_user': badge_user, 'user_from': user_from} + # body_html = template_env.get_template('badge_received.mako').render(values) res = self.message_post(cr, uid, badge.id, body=body_html, type='comment', subtype='mt_comment', context=context) return res diff --git a/addons/gamification/badge_data.xml b/addons/gamification/badge_data.xml index 8bd60d72dec..bedd9916c16 100644 --- a/addons/gamification/badge_data.xml +++ b/addons/gamification/badge_data.xml @@ -884,4 +884,22 @@ AABJRU5ErkJggg==
+ + + + + + Received Badge + Congratulation, you have received the badge ${object.badge_id.name} ! + % if ctx["user_from"] + This badge was granted by ${ctx["user_from"]}. + % endif +

+ + % if object.comment +

${object.comment}

+ % endif]]>
+
+
diff --git a/addons/gamification/goal.py b/addons/gamification/goal.py index 896b476f158..32a6bd55512 100644 --- a/addons/gamification/goal.py +++ b/addons/gamification/goal.py @@ -19,12 +19,12 @@ # ############################################################################## +from openerp import SUPERUSER_ID from openerp.osv import fields, osv +from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as DF from openerp.tools.safe_eval import safe_eval from openerp.tools.translate import _ -from templates import TemplateHelper - from datetime import date, datetime, timedelta import logging @@ -88,8 +88,10 @@ class gamification_goal_type(osv.Model): 'field_date_id': fields.many2one('ir.model.fields', string='Date Field', help='The date to use for the time period evaluated'), + #TODO: actually it would be better to use 'user.id' in the domain definition, because it means user is a browse record and it's more flexible (i can do '[(country_id,=,user.partner_id.country_id.id)]) + 'domain': fields.char("Filter Domain", - help="Technical filters rules to apply", + help="Technical filters rules to apply. Use 'user.id' (without marks) to limit the search to the evaluated user.", required=True), 'compute_code': fields.char('Compute Code', help="The name of the python method that will be executed to compute the current value. See the file gamification/goal_type_data.py for examples."), @@ -146,8 +148,6 @@ class gamification_goal(osv.Model): _columns = { 'type_id': fields.many2one('gamification.goal.type', string='Goal Type', required=True, ondelete="cascade"), - #TODO: add some tooltip/grey label in view to explain that people can use 'user_id' in their domain - #TODO: actually it would be better to use 'user.id' in the domain definition, because it means user is a browse record and it's more flexible (i can do '[(country_id,=,user.partner_id.country_id.id)]) 'user_id': fields.many2one('res.users', string='User', required=True), 'planline_id': fields.many2one('gamification.goal.planline', string='Goal Planline', ondelete="cascade"), 'plan_id': fields.related('planline_id', 'plan_id', @@ -194,6 +194,25 @@ class gamification_goal(osv.Model): } _order = 'create_date desc, end_date desc, type_id, id' + def _check_remind_delay(self, goal, context=None): + """Verify if a goal has not been updated for some time and send a + reminder message of needed. + + :return: data to write on the goal object + """ + if goal.remind_update_delay and goal.last_update: + delta_max = timedelta(days=goal.remind_update_delay) + last_update = datetime.strptime(goal.last_update, DF).date() + if date.today() - last_update > delta_max and goal.state == 'inprogress': + # generate a remind report + temp_obj = self.pool.get('email.template') + template_id = self.pool['ir.model.data'].get_object(cr, uid, 'gamification', 'email_template_goal_reminder', context) + body_html = temp_obj.render_template(cr, uid, template_id.body_html, 'gamification.goal', goal.id, context=context) + + self.message_post(cr, uid, goal.id, body=body_html, partner_ids=[goal.user_id.partner_id.id], context=context, subtype='mail.mt_comment') + return {'state': 'inprogress_update'} + return {} + def update(self, cr, uid, ids, context=None): """Update the goals to recomputes values and change of states @@ -211,19 +230,7 @@ class gamification_goal(osv.Model): continue if goal.type_id.computation_mode == 'manually': - - #TODO: put this code section in a separated method - # check for remind to update - if goal.remind_update_delay and goal.last_update: - delta_max = timedelta(days=goal.remind_update_delay) - last_update = datetime.strptime(goal.last_update, '%Y-%m-%d').date() - if date.today() - last_update > delta_max and goal.state == 'inprogress': - towrite['state'] = 'inprogress_update' - - # generate a remind report - template_env = TemplateHelper() - body_html = template_env.get_template('reminder.mako').render({'object': goal}) - self.message_post(cr, uid, goal.id, body=body_html, partner_ids=[goal.user_id.partner_id.id], context=context, subtype='mail.mt_comment') + towrite.update(self._check_remind_delay(goal, context)) elif goal.type_id.computation_mode == 'python': # execute the chosen method @@ -231,7 +238,7 @@ class gamification_goal(osv.Model): result = safe_eval(goal.type_id.compute_code, values, {}) if type(result) in (float, int, long) and result != goal.current: - towrite = {'current': result} + towrite['current'] = result else: _logger.exception(_('Unvalid return content from the evaluation of %s' % str(goal.type_id.compute_code))) # raise osv.except_osv(_('Error!'), _('Unvalid return content from the evaluation of %s' % str(goal.type_id.compute_code))) @@ -241,7 +248,7 @@ class gamification_goal(osv.Model): field_date_name = goal.type_id.field_date_id.name # eval the domain with user_id replaced by goal user - domain = safe_eval(goal.type_id.domain, {'user_id': goal.user_id.id}) # TODO: {'user_id': goal.user_id} (see above comment) + domain = safe_eval(goal.type_id.domain, {'user': goal.user_id}) #add temporal clause(s) to the domain if fields are filled on the goal if goal.start_date and field_date_name: @@ -259,11 +266,11 @@ class gamification_goal(osv.Model): #avoid useless write if the new value is the same as the old one if new_value != goal.current: - towrite = {'current': new_value} + towrite['current'] = new_value # check goal target reached #TODO: reached condition is wrong because it should check time constraints. - if (goal.type_id.condition == 'higher' and towrite.get('current', goal.current) >= goal.target_goal) or (goal.type_id.condition == 'lower' and towrite.get('current', goal.curren) <= goal.target_goal): + if (goal.type_id.condition == 'higher' and towrite.get('current', goal.current) >= goal.target_goal) or (goal.type_id.condition == 'lower' and towrite.get('current', goal.current) <= goal.target_goal): towrite['state'] = 'reached' # check goal failure @@ -302,10 +309,9 @@ class gamification_goal(osv.Model): return self.write(cr, uid, ids, {'state': 'inprogress'}, context=context) def create(self, cr, uid, vals, context=None): - """Overwrite the create method to add a 'just_created' field to True""" - #TODO: rename just_created into something more explicit (related to the effect, not to the cause) like 'avoid_onchange_log', for example + """Overwrite the create method to add a 'no_remind_goal' field to True""" context = context or {} - context['just_created'] = True + context['no_remind_goal'] = True return super(gamification_goal, self).create(cr, uid, vals, context=context) def write(self, cr, uid, ids, vals, context=None): @@ -322,13 +328,13 @@ class gamification_goal(osv.Model): raise osv.except_osv(_('Error!'), _('Can not modify a started goal')) if 'current' in vals: - if 'just_created' in context: + if 'no_remind_goal' in context: # new goals should not be reported continue if goal.plan_id and goal.plan_id.report_message_frequency == 'onchange': plan_obj = self.pool.get('gamification.goal.plan') - plan_obj.report_progress(cr, uid, goal.plan_id, users=[goal.user_id], context=context) + plan_obj.report_progress(cr, SUPERUSER_ID, goal.plan_id, users=[goal.user_id], context=context) return result def get_action(self, cr, uid, goal_id, context=None): diff --git a/addons/gamification/goal_base_data.xml b/addons/gamification/goal_base_data.xml index 17886a6144c..b0e21b977cb 100644 --- a/addons/gamification/goal_base_data.xml +++ b/addons/gamification/goal_base_data.xml @@ -9,7 +9,7 @@ count checkbox - [('id','=',user_id),('partner_id.tz', '!=', False)] + [('id','=',user.id),('partner_id.tz', '!=', False)] id @@ -31,7 +31,7 @@ count checkbox - [('user_ids', 'in', user_id), ('name', '!=', 'Your Company')] + [('user_ids', 'in', user.id), ('name', '!=', 'Your Company')] company_id.id @@ -41,7 +41,7 @@ count checkbox - [('user_ids', 'in', user_id),('logo', '!=', False)] + [('user_ids', 'in', user.id),('logo', '!=', False)] company_id.id @@ -63,7 +63,7 @@ checkbox count - [('id', '!=', user_id)] + [('id', '!=', user.id)] @@ -124,7 +124,109 @@ 1 + + + + + + + Reminder for Goal Update + + Reminder ${object.name} + +

${object.report_header or ''}

+ +

You have not updated your progress for the goal ${object.type_id.name} (currently reached at ${object.completeness}%) for at least ${object.remind_update_delay} days. Do not forget to do it.

+ +

If you have not changed your score yet, you can use the button "The current value is up to date" to indicate so.

+ ]]>
+
+ + + Personal Goal Progress + + ${object.name} + +

${object.report_header or ''}

+ + + + + + + + + % for goal in ${ctx["goals"]}: + = 100: + style="font-weight:bold;" + % endif + > + + + + + + % endfor +
GoalTargetCurrentCompleteness
${goal.type_id.name}${goal.target_goal} + % if goal.type_suffix: + ${goal.type_suffix} + % endif + ${goal.current} + % if goal.type_suffix: + ${goal.type_suffix} + % endif + ${goal.completeness} %
]]>
+
+ + + Group Goal Progress + + ${object.name} + +

${object.report_header or ''}

+ + % for planline in ctx['planlines_boards']: + + + + + + + + + + + % for idx, goal in planline.board_goals: + % if idx < 3 or goal.user_id.id == user.id: + = 100: + style="font-weight:bold;" + % endif + > + + + + + + % endif + % endfor +
${planline.goal_type.name}
#PersonCompletenessCurrent
${idx+1}${goal.user_id.name}${goal.completeness}%${goal.current}/${goal.target_goal} + % if goal.type_suffix: + ${goal.type_suffix} + % endif +
+ +

+ + % endfor +]]>
+
diff --git a/addons/gamification/plan.py b/addons/gamification/plan.py index 2541d9e500b..4375474fd05 100644 --- a/addons/gamification/plan.py +++ b/addons/gamification/plan.py @@ -22,7 +22,7 @@ from openerp.osv import fields, osv from openerp.tools.translate import _ -from templates import TemplateHelper +# from templates import TemplateHelper from datetime import date, datetime, timedelta import calendar @@ -545,12 +545,17 @@ class gamification_goal_plan(osv.Model): context = context or {} goal_obj = self.pool.get('gamification.goal') - template_env = TemplateHelper() - + # template_env = TemplateHelper() + temp_obj = self.pool.get('email.template') + ctx = context.copy() if plan.visibility_mode == 'board': planlines_boards = self.get_board_goal_info(cr, uid, plan, subset_goal_ids, context) - body_html = template_env.get_template('group_progress.mako').render({'object': plan, 'planlines_boards': planlines_boards, 'uid': uid}) + ctx.update({'planlines_boards': planlines_boards}) + template_id = self.pool['ir.model.data'].get_object(cr, uid, 'gamification', 'email_template_goal_progress_group', context) + body_html = temp_obj.render_template(cr, uid, template_id.body_html, 'gamification.goal.plan', plan.id, context=context) + + # body_html = template_env.get_template('group_progress.mako').render({'object': plan, 'planlines_boards': planlines_boards, 'uid': uid}) # send to every follower of the plan self.message_post(cr, uid, plan.id, @@ -570,10 +575,14 @@ class gamification_goal_plan(osv.Model): if not values: continue - values['object'] = plan - values['user'] = user, + # values['object'] = plan + # values['user'] = user - body_html = template_env.get_template('personal_progress.mako').render(values) + ctx.update({'planlines_boards': planlines_boards}) + template_id = self.pool['ir.model.data'].get_object(cr, uid, 'gamification', 'email_template_goal_progress_perso', context) + body_html = temp_obj.render_template(cr, user.id, template_id.body_html, 'gamification.goal.plan', plan.id, context=context) + + # body_html = template_env.get_template('personal_progress.mako').render(values) # send message only to users self.message_post(cr, uid, 0, diff --git a/addons/gamification/res_users.py b/addons/gamification/res_users.py index c0458263e05..4962cb1b975 100644 --- a/addons/gamification/res_users.py +++ b/addons/gamification/res_users.py @@ -1,6 +1,27 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2010-Today OpenERP SA () +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see +# +############################################################################## + from openerp.osv import osv -#TODO add copyright and last line for vim users + class res_users_gamification_group(osv.Model): """ Update of res.users class - if adding groups to an user, check gamification.goal.plan linked to @@ -144,3 +165,4 @@ class res_groups_gamification_group(osv.Model): if plan_ids: goal_plan_obj.write(cr, uid, plan_ids, {'user_ids': [(4, user_id) for user_id in user_ids]}, context=context) return write_res +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/gamification/templates.py b/addons/gamification/templates.py index fb9c23c3e3d..b6d071c8f3a 100644 --- a/addons/gamification/templates.py +++ b/addons/gamification/templates.py @@ -40,4 +40,3 @@ class TemplateHelper(SandboxedEnvironment): 'quote': quote, 'urlencode': urlencode, }) - diff --git a/addons/gamification/templates/reminder.mako b/addons/gamification/templates/reminder.mako index f50d798a657..d3f8a584a03 100644 --- a/addons/gamification/templates/reminder.mako +++ b/addons/gamification/templates/reminder.mako @@ -13,4 +13,5 @@

You have not updated your progress for the goal ${object.type_id.name} (currently reached at ${object.completeness}%) for at least ${object.remind_update_delay} days. Do not forget to do it.

If you have not changed your score yet, you can use the button "The current value is up to date" to indicate so.

- \ No newline at end of file + + diff --git a/addons/gamification_sale_crm/sale_crm_goals.xml b/addons/gamification_sale_crm/sale_crm_goals.xml index c5f85ef3273..6336f0e0654 100644 --- a/addons/gamification_sale_crm/sale_crm_goals.xml +++ b/addons/gamification_sale_crm/sale_crm_goals.xml @@ -11,7 +11,7 @@ - [('state','!=','cancel'),('user_id','=',user_id),('type','=','out_invoice')] + [('state','!=','cancel'),('user_id','=',user.id),('type','=','out_invoice')] @@ -21,7 +21,7 @@ leads - [('user_id','=',user_id),('type', '=', 'lead')] + [('user_id','=',user.id),('type', '=', 'lead')] @@ -33,7 +33,7 @@ - [('user_id','=',user_id),('type', '=', 'lead')] + [('user_id','=',user.id),('type', '=', 'lead')] @@ -45,7 +45,7 @@ - [('user_id','=',user_id)] + [('user_id','=',user.id)] @@ -56,7 +56,7 @@ calls - [('user_id','=',user_id),('state','=','done')] + [('user_id','=',user.id),('state','=','done')] @@ -66,7 +66,7 @@ opportunities - [('user_id','=',user_id),('type','=','opportunity')] + [('user_id','=',user.id),('type','=','opportunity')] @@ -76,7 +76,7 @@ orders - [('user_id','=',user_id),('state','not in',('draft', 'sent', 'cancel'))] + [('user_id','=',user.id),('state','not in',('draft', 'sent', 'cancel'))] @@ -86,7 +86,7 @@ orders - [('state','=','paid'),('user_id','=',user_id),('type','=','out_invoice')] + [('state','=','paid'),('user_id','=',user.id),('type','=','out_invoice')] Total Paid Sales Orders @@ -96,7 +96,7 @@ - [('state','=','paid'),('user_id','=',user_id),('type','=','out_invoice')] + [('state','=','paid'),('user_id','=',user.id),('type','=','out_invoice')] @@ -108,7 +108,7 @@ invoices - [('state','!=','cancel'),('user_id','=',user_id),('type','=','out_refund')] + [('state','!=','cancel'),('user_id','=',user.id),('type','=','out_refund')] Total Customer Refunds @@ -119,7 +119,7 @@ - [('state','!=','cancel'),('user_id','=',user_id),('type','=','out_refund')] + [('state','!=','cancel'),('user_id','=',user.id),('type','=','out_refund')] From c46526a9dc2d376c016a51390779257ac45912c2 Mon Sep 17 00:00:00 2001 From: "Jignesh Rathod (OpenERP)" Date: Fri, 7 Jun 2013 18:46:46 +0530 Subject: [PATCH 0381/1311] Improve Code. bzr revid: jir@tinyerp.com-20130607131646-kkeo1nuue6klbikj --- addons/document/document_view.xml | 3 +++ addons/hr_recruitment/hr_recruitment.py | 18 ++++++++++++++++++ addons/hr_recruitment/hr_recruitment_view.xml | 10 ++++++---- addons/hr_recruitment/res_config.py | 4 ---- addons/hr_recruitment/res_config_view.xml | 6 ------ .../security/hr_recruitment_security.xml | 1 - 6 files changed, 27 insertions(+), 15 deletions(-) diff --git a/addons/document/document_view.xml b/addons/document/document_view.xml index c3bd5ad3f56..8c60e232dbf 100644 --- a/addons/document/document_view.xml +++ b/addons/document/document_view.xml @@ -199,6 +199,9 @@ + + + diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py index 161cf71d513..39064304300 100644 --- a/addons/hr_recruitment/hr_recruitment.py +++ b/addons/hr_recruitment/hr_recruitment.py @@ -185,6 +185,24 @@ class hr_applicant(base_stage, osv.Model): res[issue.id][field] = abs(float(duration)) return res + + def attachment_tree_view(self, cr, uid, ids, context): + domain = ['&', ('res_model', '=', 'hr.applicant'), ('res_id', 'in', ids)] + res_id = ids and ids[0] or False + return { + 'name': _('Attachments'), + 'domain': domain, + 'res_model': 'ir.attachment', + 'type': 'ir.actions.act_window', + 'view_id': False, + 'view_mode': 'tree,form', + 'view_type': 'form', + 'limit': 80, + 'context': "{'default_res_model': '%s','default_res_id': %d}" % (self._name, res_id) + } + + + def _compute_attachments(self, cr, uid, ids, fields, args, context=None): res = {} attachment_pool = self.pool.get('ir.attachment') diff --git a/addons/hr_recruitment/hr_recruitment_view.xml b/addons/hr_recruitment/hr_recruitment_view.xml index 542620ad14e..0cc6aff1496 100644 --- a/addons/hr_recruitment/hr_recruitment_view.xml +++ b/addons/hr_recruitment/hr_recruitment_view.xml @@ -89,6 +89,7 @@