2013-02-14 08:20:38 +00:00
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
2013-12-17 10:48:13 +00:00
# Copyright (C) 2013 OpenERP SA (<http://www.openerp.com>)
2013-02-14 08:20:38 +00:00
#
# This program is free software: you can redistribute it and/or modify
2013-02-26 11:34:03 +00:00
# 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.
2013-02-14 08:20:38 +00:00
#
# 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
2013-02-26 11:34:03 +00:00
# GNU General Public License for more details.
2013-02-14 08:20:38 +00:00
#
2013-02-26 11:34:03 +00:00
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
2013-02-14 08:20:38 +00:00
#
##############################################################################
2013-06-06 12:47:08 +00:00
from openerp import SUPERUSER_ID
2013-02-14 08:20:38 +00:00
from openerp . osv import fields , osv
2013-06-06 12:47:08 +00:00
from openerp . tools import DEFAULT_SERVER_DATE_FORMAT as DF
2013-02-20 12:33:28 +00:00
from openerp . tools . safe_eval import safe_eval
2013-04-23 13:03:51 +00:00
from openerp . tools . translate import _
2013-02-15 13:32:34 +00:00
2013-04-15 14:57:43 +00:00
import logging
2013-12-24 13:00:23 +00:00
import time
from datetime import date , datetime , timedelta
2013-04-15 14:57:43 +00:00
_logger = logging . getLogger ( __name__ )
2013-03-11 09:39:30 +00:00
2013-12-17 16:15:41 +00:00
class gamification_goal_definition ( osv . Model ) :
""" Goal definition
2013-02-14 17:07:36 +00:00
2013-12-17 16:15:41 +00:00
A goal definition contains the way to evaluate an objective
2013-02-14 17:07:36 +00:00
Each module wanting to be able to set goals to the users needs to create
2013-12-17 16:15:41 +00:00
a new gamification_goal_definition
2013-02-14 17:07:36 +00:00
"""
2013-12-17 16:15:41 +00:00
_name = ' gamification.goal.definition '
_description = ' Gamification goal definition '
2013-02-14 17:07:36 +00:00
2013-04-10 08:49:40 +00:00
def _get_suffix ( self , cr , uid , ids , field_name , arg , context = None ) :
2013-06-03 20:30:53 +00:00
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
2013-04-10 08:49:40 +00:00
elif goal . monetary :
# use the current user's company currency
user = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context )
2013-06-03 20:30:53 +00:00
if goal . suffix :
res [ goal . id ] = " %s %s " % ( user . company_id . currency_id . symbol , goal . suffix )
2013-04-10 08:49:40 +00:00
else :
res [ goal . id ] = user . company_id . currency_id . symbol
else :
res [ goal . id ] = " "
return res
2013-02-14 17:07:36 +00:00
_columns = {
2013-12-17 16:15:41 +00:00
' name ' : fields . char ( ' Goal Definition ' , required = True , translate = True ) ,
2013-04-08 13:17:29 +00:00
' description ' : fields . text ( ' Goal Description ' ) ,
' monetary ' : fields . boolean ( ' Monetary Value ' , help = " The target and current value are defined in the company currency. " ) ,
2013-06-03 20:30:53 +00:00
' 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 " ) ,
2013-02-20 12:33:28 +00:00
' computation_mode ' : fields . selection ( [
2013-04-08 13:17:29 +00:00
( ' manually ' , ' Recorded manually ' ) ,
( ' count ' , ' Automatic: number of records ' ) ,
( ' sum ' , ' Automatic: sum on a field ' ) ,
2013-04-15 14:57:43 +00:00
( ' python ' , ' Automatic: execute a specific Python code ' ) ,
2013-02-20 12:33:28 +00:00
] ,
2013-04-08 13:17:29 +00:00
string = " Computation Mode " ,
2013-06-06 08:39:28 +00:00
help = " Defined how will be computed the goals. The result of the operation will be stored in the field ' Current ' . " ,
required = True ) ,
2013-04-16 10:27:24 +00:00
' display_mode ' : fields . selection ( [
( ' progress ' , ' Progressive (using numerical values) ' ) ,
2013-12-20 14:45:21 +00:00
( ' boolean ' , ' Exclusive (done or not-done) ' ) ,
2013-04-16 10:27:24 +00:00
] ,
string = " Displayed as " , required = True ) ,
2013-02-18 15:16:07 +00:00
' model_id ' : fields . many2one ( ' ir.model ' ,
2013-02-19 13:23:06 +00:00
string = ' Model ' ,
2013-03-11 11:04:23 +00:00
help = ' The model object for the field to evaluate ' ) ,
2013-02-18 15:16:07 +00:00
' field_id ' : fields . many2one ( ' ir.model.fields ' ,
2013-04-08 13:17:29 +00:00
string = ' Field to Sum ' ,
2013-03-11 11:04:23 +00:00
help = ' The field containing the value to evaluate ' ) ,
2013-02-18 15:16:07 +00:00
' field_date_id ' : fields . many2one ( ' ir.model.fields ' ,
2013-04-08 13:17:29 +00:00
string = ' Date Field ' ,
2013-02-15 09:16:38 +00:00
help = ' The date to use for the time period evaluated ' ) ,
2013-04-08 13:17:29 +00:00
' domain ' : fields . char ( " Filter Domain " ,
2014-04-11 14:22:57 +00:00
help = " Domain for filtering records. General rule, not user depending, e.g. [( ' state ' , ' = ' , ' done ' )]. The expression can contain reference to ' user ' which is a browse record of the current user if not in batch mode. " ,
2013-04-10 08:49:40 +00:00
required = True ) ,
2014-04-11 14:22:57 +00:00
' batch_mode ' : fields . boolean ( ' Batch Mode ' ,
help = " Evaluate the expression in batch instead of once for each user " ) ,
' batch_distinctive_field ' : fields . many2one ( ' ir.model.fields ' ,
string = " Distinctive field for batch user " ,
help = " In batch mode, this indicates which field distinct one user form the other, e.g. user_id, partner_id... " ) ,
' batch_user_expression ' : fields . char ( " Evaluted expression for batch mode " ,
help = " The value to compare with the distinctive field. The expression can contain reference to ' user ' which is a browse record of the current user, e.g. user.id, user.partner_id.id... " ) ,
2013-12-24 13:00:23 +00:00
' compute_code ' : fields . text ( ' Python Code ' ,
help = " Python code to be executed for each user. ' result ' should contains the new current value. Evaluated user can be access through object.user_id. " ) ,
2013-03-11 11:04:23 +00:00
' condition ' : fields . selection ( [
2013-04-08 13:17:29 +00:00
( ' higher ' , ' The higher the better ' ) ,
( ' lower ' , ' The lower the better ' )
2013-02-20 12:33:28 +00:00
] ,
2013-04-08 13:17:29 +00:00
string = ' Goal Performance ' ,
2013-02-15 09:16:38 +00:00
help = ' A goal is considered as completed when the current value is compared to the value to reach ' ,
required = True ) ,
2013-06-03 20:30:53 +00:00
' action_id ' : fields . many2one ( ' ir.actions.act_window ' , string = " Action " ,
2013-04-08 13:17:29 +00:00
help = " The action that will be called to update the goal value. " ) ,
' res_id_field ' : fields . char ( " ID Field of user " ,
2014-04-11 14:22:57 +00:00
help = " The field name on the user profile (res.users) containing the value for res_id for action. " ) ,
2013-02-14 08:20:38 +00:00
}
2013-03-26 15:45:53 +00:00
2013-02-14 17:07:36 +00:00
_defaults = {
2013-03-06 14:45:44 +00:00
' condition ' : ' higher ' ,
2013-03-26 15:45:53 +00:00
' computation_mode ' : ' manually ' ,
' domain ' : " [] " ,
' monetary ' : False ,
2013-04-16 10:27:24 +00:00
' display_mode ' : ' progress ' ,
2013-02-14 08:20:38 +00:00
}
2013-12-24 13:00:23 +00:00
def number_following ( self , cr , uid , model_name = " mail.thread " , context = None ) :
""" Return the number of ' model_name ' objects the user is following
The model specified in ' model_name ' must inherit from mail . thread
"""
user = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context )
return self . pool . get ( ' mail.followers ' ) . search ( cr , uid , [ ( ' res_model ' , ' = ' , model_name ) , ( ' partner_id ' , ' = ' , user . partner_id . id ) ] , count = True , context = context )
2013-02-20 12:33:28 +00:00
2013-02-14 17:07:36 +00:00
class gamification_goal ( osv . Model ) :
""" Goal instance for a user
2013-02-21 13:50:00 +00:00
An individual goal for a user on a specified time period """
2013-02-14 17:07:36 +00:00
_name = ' gamification.goal '
_description = ' Gamification goal instance '
2013-12-23 16:32:14 +00:00
def _get_completion ( self , cr , uid , ids , field_name , arg , context = None ) :
2013-02-27 10:59:56 +00:00
""" Return the percentage of completeness of the goal, between 0 and 100 """
2013-06-03 20:30:53 +00:00
res = dict . fromkeys ( ids , 0.0 )
for goal in self . browse ( cr , uid , ids , context = context ) :
2013-12-17 16:15:41 +00:00
if goal . definition_condition == ' higher ' :
2013-12-16 13:27:48 +00:00
if goal . current > = goal . target_goal :
res [ goal . id ] = 100.0
else :
res [ goal . id ] = round ( 100.0 * goal . current / goal . target_goal , 2 )
2013-06-03 20:30:53 +00:00
elif goal . current < goal . target_goal :
2013-04-23 13:03:51 +00:00
# a goal 'lower than' has only two values possible: 0 or 100%
2013-06-03 20:30:53 +00:00
res [ goal . id ] = 100.0
2013-07-03 10:25:07 +00:00
else :
res [ goal . id ] = 0.0
2013-02-15 14:54:15 +00:00
return res
2013-02-20 16:17:55 +00:00
2013-12-17 16:15:41 +00:00
def on_change_definition_id ( self , cr , uid , ids , definition_id = False , context = None ) :
goal_definition = self . pool . get ( ' gamification.goal.definition ' )
if not definition_id :
return { ' value ' : { ' definition_id ' : False } }
goal_definition = goal_definition . browse ( cr , uid , definition_id , context = context )
return { ' value ' : { ' computation_mode ' : goal_definition . computation_mode , ' definition_condition ' : goal_definition . condition } }
2013-02-15 14:54:15 +00:00
2013-02-14 17:07:36 +00:00
_columns = {
2013-12-17 16:15:41 +00:00
' definition_id ' : fields . many2one ( ' gamification.goal.definition ' , string = ' Goal Definition ' , required = True , ondelete = " cascade " ) ,
2013-03-12 14:15:31 +00:00
' user_id ' : fields . many2one ( ' res.users ' , string = ' User ' , required = True ) ,
2014-04-11 14:22:57 +00:00
' line_id ' : fields . many2one ( ' gamification.challenge.line ' , string = ' Challenge Line ' , ondelete = " cascade " ) ,
2013-12-17 16:15:41 +00:00
' challenge_id ' : fields . related ( ' line_id ' , ' challenge_id ' ,
string = " Challenge " ,
2013-03-12 14:15:31 +00:00
type = ' many2one ' ,
2013-12-17 16:15:41 +00:00
relation = ' gamification.challenge ' ,
2014-02-07 10:27:05 +00:00
store = True , readonly = True ,
help = " Challenge that generated the goal, assign challenge to users to generate goals with a value in this field. " ) ,
2013-03-12 14:15:31 +00:00
' start_date ' : fields . date ( ' Start Date ' ) ,
2013-04-12 12:19:13 +00:00
' end_date ' : fields . date ( ' End Date ' ) , # no start and end = always active
2013-03-12 14:15:31 +00:00
' target_goal ' : fields . float ( ' To Reach ' ,
2013-02-14 17:07:36 +00:00
required = True ,
2013-04-12 12:19:13 +00:00
track_visibility = ' always ' ) , # no goal = global index
2013-06-04 18:37:39 +00:00
' current ' : fields . float ( ' Current Value ' , required = True , track_visibility = ' always ' ) ,
2013-12-23 16:32:14 +00:00
' completeness ' : fields . function ( _get_completion , type = ' float ' , string = ' Completeness ' ) ,
2013-02-20 12:33:28 +00:00
' state ' : fields . selection ( [
2013-02-22 13:40:24 +00:00
( ' draft ' , ' Draft ' ) ,
2013-02-20 12:33:28 +00:00
( ' inprogress ' , ' In progress ' ) ,
( ' reached ' , ' Reached ' ) ,
( ' failed ' , ' Failed ' ) ,
( ' canceled ' , ' Canceled ' ) ,
] ,
2013-02-15 14:16:51 +00:00
string = ' State ' ,
2013-02-14 17:07:36 +00:00
required = True ,
2013-03-11 11:04:23 +00:00
track_visibility = ' always ' ) ,
2014-04-11 14:22:57 +00:00
' to_update ' : fields . boolean ( ' To update ' ) ,
' closed ' : fields . boolean ( ' Closed goal ' , help = " These goals will not be recomputed. " ) ,
2013-02-20 16:05:17 +00:00
2013-12-17 16:15:41 +00:00
' computation_mode ' : fields . related ( ' definition_id ' , ' computation_mode ' , type = ' char ' , string = " Computation mode " ) ,
2013-03-12 14:15:31 +00:00
' remind_update_delay ' : fields . integer ( ' Remind delay ' ,
2013-02-20 16:05:17 +00:00
help = " The number of days after which the user assigned to a manual goal will be reminded. Never reminded if no value is specified. " ) ,
2013-04-12 12:19:13 +00:00
' last_update ' : fields . date ( ' Last Update ' ,
2013-12-17 16:15:41 +00:00
help = " In case of manual goal, reminders are sent if the goal as not been updated for a while (defined in challenge). Ignored in case of non-manual goal or goal not linked to a challenge. " ) ,
2013-03-11 11:04:23 +00:00
2013-12-17 16:15:41 +00:00
' definition_description ' : fields . related ( ' definition_id ' , ' description ' , type = ' char ' , string = ' Definition Description ' , readonly = True ) ,
' definition_condition ' : fields . related ( ' definition_id ' , ' condition ' , type = ' char ' , string = ' Definition Condition ' , readonly = True ) ,
' definition_suffix ' : fields . related ( ' definition_id ' , ' full_suffix ' , type = " char " , string = " Suffix " , readonly = True ) ,
' definition_display ' : fields . related ( ' definition_id ' , ' display_mode ' , type = " char " , string = " Display Mode " , readonly = True ) ,
2013-02-14 17:07:36 +00:00
}
2013-02-14 08:20:38 +00:00
2013-02-14 17:07:36 +00:00
_defaults = {
' current ' : 0 ,
2013-02-22 13:40:24 +00:00
' state ' : ' draft ' ,
2013-02-18 13:48:03 +00:00
' start_date ' : fields . date . today ,
2013-02-14 17:07:36 +00:00
}
2013-12-17 16:15:41 +00:00
_order = ' create_date desc, end_date desc, definition_id, id '
2013-02-14 08:20:38 +00:00
2013-12-16 14:23:52 +00:00
def _check_remind_delay ( self , cr , uid , goal , context = None ) :
2013-06-06 12:47:08 +00:00
""" 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 ( )
2014-04-11 14:22:57 +00:00
if date . today ( ) - last_update > delta_max :
2013-06-06 12:47:08 +00:00
# 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 )
2014-05-06 09:37:16 +00:00
self . pool [ ' mail.thread ' ] . message_post ( cr , uid , 0 , body = body_html , partner_ids = [ goal . user_id . partner_id . id ] , context = context , subtype = ' mail.mt_comment ' )
2014-04-11 14:22:57 +00:00
return { ' to_update ' : True }
2013-06-06 12:47:08 +00:00
return { }
2013-02-22 09:56:15 +00:00
def update ( self , cr , uid , ids , context = None ) :
2013-02-20 16:05:17 +00:00
""" Update the goals to recomputes values and change of states
2013-03-01 11:27:20 +00:00
If a manual goal is not updated for enough time , the user will be
reminded to do so ( done only once , in ' inprogress ' state ) .
If a goal reaches the target value , the status is set to reached
2013-02-20 16:05:17 +00:00
If the end date is passed ( at least + 1 day , time not considered ) without
2013-02-22 09:56:15 +00:00
the target value being reached , the goal is set as failed . """
2013-12-24 13:00:23 +00:00
if context is None :
context = { }
2014-05-06 11:38:31 +00:00
commit = context . get ( ' commit_gamification ' , False )
2013-03-12 14:15:31 +00:00
2014-04-11 14:22:57 +00:00
goals_by_definition = { }
all_goals = { }
2013-03-22 11:13:44 +00:00
for goal in self . browse ( cr , uid , ids , context = context ) :
2013-03-12 14:15:31 +00:00
if goal . state in ( ' draft ' , ' canceled ' ) :
2014-04-11 14:22:57 +00:00
# draft or canceled goals should not be recomputed
2013-02-22 09:47:45 +00:00
continue
2013-02-20 14:22:48 +00:00
2014-04-11 14:22:57 +00:00
goals_by_definition . setdefault ( goal . definition_id , [ ] ) . append ( goal )
all_goals [ goal . id ] = goal
for definition , goals in goals_by_definition . items ( ) :
2014-05-06 11:21:09 +00:00
goals_to_write = dict ( ( goal . id , { } ) for goal in goals )
2014-04-11 14:22:57 +00:00
if definition . computation_mode == ' manually ' :
for goal in goals :
goals_to_write [ goal . id ] . update ( self . _check_remind_delay ( cr , uid , goal , context ) )
elif definition . computation_mode == ' python ' :
# TODO batch execution
for goal in goals :
# execute the chosen method
cxt = {
' self ' : self . pool . get ( ' gamification.goal ' ) ,
' object ' : goal ,
' pool ' : self . pool ,
' cr ' : cr ,
' context ' : dict ( context ) , # copy context to prevent side-effects of eval
' uid ' : uid ,
' date ' : date , ' datetime ' : datetime , ' timedelta ' : timedelta , ' time ' : time
}
code = definition . compute_code . strip ( )
safe_eval ( code , cxt , mode = " exec " , nocopy = True )
# the result of the evaluated codeis put in the 'result' local variable, propagated to the context
2014-04-11 15:40:02 +00:00
result = cxt . get ( ' result ' )
if result is not None and type ( result ) in ( float , int , long ) :
2014-04-11 14:22:57 +00:00
if result != goal . current :
goals_to_write [ goal . id ] [ ' current ' ] = result
else :
_logger . exception ( _ ( ' Invalid return content from the evaluation of code for definition %s ' % definition . name ) )
2013-04-15 14:57:43 +00:00
2013-03-11 11:04:23 +00:00
else : # count or sum
2013-02-20 14:22:48 +00:00
2014-04-11 14:22:57 +00:00
obj = self . pool . get ( definition . model_id . model )
field_date_name = definition . field_date_id and definition . field_date_id . name or False
if definition . computation_mode == ' count ' and definition . batch_mode :
2014-04-14 13:51:15 +00:00
# batch mode, trying to do as much as possible in one request
2014-04-11 14:22:57 +00:00
general_domain = safe_eval ( definition . domain )
field_name = definition . batch_distinctive_field . name
subqueries = { }
for goal in goals :
start_date = field_date_name and goal . start_date or False
end_date = field_date_name and goal . end_date or False
subqueries . setdefault ( ( start_date , end_date ) , { } ) . update ( { goal . id : safe_eval ( definition . batch_user_expression , { ' user ' : goal . user_id } ) } )
2014-04-15 16:15:38 +00:00
# the global query should be split by time periods (especially for recurrent goals)
2014-04-11 14:22:57 +00:00
for ( start_date , end_date ) , query_goals in subqueries . items ( ) :
subquery_domain = list ( general_domain )
subquery_domain . append ( ( field_name , ' in ' , list ( set ( query_goals . values ( ) ) ) ) )
if start_date :
subquery_domain . append ( ( field_date_name , ' >= ' , start_date ) )
if end_date :
2014-04-15 16:15:38 +00:00
subquery_domain . append ( ( field_date_name , ' <= ' , end_date ) )
2014-04-11 14:22:57 +00:00
2014-04-15 10:18:42 +00:00
if field_name == ' id ' :
2014-04-15 16:15:38 +00:00
# grouping on id does not work and is similar to search anyway
2014-04-15 10:18:42 +00:00
user_ids = obj . search ( cr , uid , subquery_domain , context = context )
user_values = [ { ' id ' : user_id , ' id_count ' : 1 } for user_id in user_ids ]
else :
user_values = obj . read_group ( cr , uid , subquery_domain , fields = [ field_name ] , groupby = [ field_name ] , context = context )
2014-04-15 16:15:38 +00:00
# user_values has format of read_group: [{'partner_id': 42, 'partner_id_count': 3},...]
2014-04-11 14:22:57 +00:00
for goal in [ g for g in goals if g . id in query_goals . keys ( ) ] :
for user_value in user_values :
queried_value = field_name in user_value and user_value [ field_name ] or False
if isinstance ( queried_value , tuple ) and len ( queried_value ) == 2 and isinstance ( queried_value [ 0 ] , ( int , long ) ) :
queried_value = queried_value [ 0 ]
if queried_value == query_goals [ goal . id ] :
new_value = user_value . get ( field_name + ' _count ' , goal . current )
if new_value != goal . current :
goals_to_write [ goal . id ] [ ' current ' ] = new_value
2013-03-11 11:04:23 +00:00
2014-04-11 14:22:57 +00:00
else :
for goal in goals :
# eval the domain with user replaced by goal user object
domain = safe_eval ( definition . 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 :
domain . append ( ( field_date_name , ' >= ' , goal . start_date ) )
if goal . end_date and field_date_name :
domain . append ( ( field_date_name , ' <= ' , goal . end_date ) )
if definition . computation_mode == ' sum ' :
field_name = definition . field_id . name
res = obj . read_group ( cr , uid , domain , [ field_name ] , [ field_name ] , context = context )
new_value = res and res [ 0 ] [ field_name ] or 0.0
else : # computation mode = count
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 :
goals_to_write [ goal . id ] [ ' current ' ] = new_value
2014-05-06 11:21:09 +00:00
for goal_id , value in goals_to_write . items ( ) :
if not value :
continue
goal = all_goals [ goal_id ]
# check goal target reached
if ( goal . definition_condition == ' higher ' and value . get ( ' current ' , goal . current ) > = goal . target_goal ) \
or ( goal . definition_condition == ' lower ' and value . get ( ' current ' , goal . current ) < = goal . target_goal ) :
value [ ' state ' ] = ' reached '
# check goal failure
elif goal . end_date and fields . date . today ( ) > goal . end_date :
value [ ' state ' ] = ' failed '
value [ ' closed ' ] = True
if value :
self . write ( cr , uid , [ goal . id ] , value , context = context )
2014-05-06 11:38:31 +00:00
if commit :
cr . commit ( )
2013-02-20 14:22:48 +00:00
return True
2013-02-27 11:12:20 +00:00
def action_start ( self , cr , uid , ids , context = None ) :
2013-03-01 11:27:20 +00:00
""" Mark a goal as started.
This should only be used when creating goals manually ( in draft state ) """
2013-02-27 11:12:20 +00:00
self . write ( cr , uid , ids , { ' state ' : ' inprogress ' } , context = context )
return self . update ( cr , uid , ids , context = context )
2013-02-21 10:46:46 +00:00
2013-02-15 16:02:04 +00:00
def action_reach ( self , cr , uid , ids , context = None ) :
2013-03-01 11:27:20 +00:00
""" Mark a goal as reached.
If the target goal condition is not met , the state will be reset to In
Progress at the next goal update until the end date . """
2013-02-15 16:02:04 +00:00
return self . write ( cr , uid , ids , { ' state ' : ' reached ' } , context = context )
def action_fail ( self , cr , uid , ids , context = None ) :
2013-03-01 11:27:20 +00:00
""" Set the state of the goal to failed.
A failed goal will be ignored in future checks . """
2013-02-15 16:02:04 +00:00
return self . write ( cr , uid , ids , { ' state ' : ' failed ' } , context = context )
def action_cancel ( self , cr , uid , ids , context = None ) :
2013-03-01 11:27:20 +00:00
""" Reset the completion after setting a goal as reached or failed.
This is only the current state , if the date and / or target criterias
2013-03-12 14:15:31 +00:00
match the conditions for a change of state , this will be applied at the
2013-03-01 11:27:20 +00:00
next goal update . """
2013-02-15 16:02:04 +00:00
return self . write ( cr , uid , ids , { ' state ' : ' inprogress ' } , context = context )
2013-02-28 10:14:46 +00:00
def create ( self , cr , uid , vals , context = None ) :
2013-06-06 12:47:08 +00:00
""" Overwrite the create method to add a ' no_remind_goal ' field to True """
2013-12-18 11:12:43 +00:00
if context is None :
context = { }
2013-06-06 12:47:08 +00:00
context [ ' no_remind_goal ' ] = True
2013-02-28 10:14:46 +00:00
return super ( gamification_goal , self ) . create ( cr , uid , vals , context = context )
2013-03-12 14:15:31 +00:00
2013-02-26 15:23:25 +00:00
def write ( self , cr , uid , ids , vals , context = None ) :
2013-03-01 11:27:20 +00:00
""" Overwrite the write method to update the last_update field to today
2013-03-12 14:15:31 +00:00
If the current value is changed and the report frequency is set to On
2013-04-23 13:03:51 +00:00
change , a report is generated
"""
2013-12-18 11:12:43 +00:00
if context is None :
context = { }
2013-03-12 14:15:31 +00:00
vals [ ' last_update ' ] = fields . date . today ( )
2013-04-19 10:41:23 +00:00
result = super ( gamification_goal , self ) . write ( cr , uid , ids , vals , context = context )
2013-03-12 14:15:31 +00:00
for goal in self . browse ( cr , uid , ids , context = context ) :
2013-12-17 16:15:41 +00:00
if goal . state != " draft " and ( ' definition_id ' in vals or ' user_id ' in vals ) :
2013-04-19 10:41:23 +00:00
# avoid drag&drop in kanban view
2013-06-21 08:24:28 +00:00
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( ' Can not modify the configuration of a started goal ' ) )
2013-02-28 10:14:46 +00:00
2013-06-21 08:24:28 +00:00
if vals . get ( ' current ' ) :
2013-06-06 12:47:08 +00:00
if ' no_remind_goal ' in context :
2013-02-28 10:14:46 +00:00
# new goals should not be reported
continue
2013-12-17 16:15:41 +00:00
if goal . challenge_id and goal . challenge_id . report_message_frequency == ' onchange ' :
self . pool . get ( ' gamification.challenge ' ) . report_progress ( cr , SUPERUSER_ID , goal . challenge_id , users = [ goal . user_id ] , context = context )
2013-04-19 10:41:23 +00:00
return result
2013-03-24 13:46:58 +00:00
def get_action ( self , cr , uid , goal_id , context = None ) :
""" Get the ir.action related to update the goal
In case of a manual goal , should return a wizard to update the value
2013-03-24 18:05:57 +00:00
: return : action description in a dictionnary
2013-03-24 13:46:58 +00:00
"""
goal = self . browse ( cr , uid , goal_id , context = context )
2013-12-16 13:32:36 +00:00
2013-12-17 16:15:41 +00:00
if goal . definition_id . action_id :
2013-12-23 16:32:14 +00:00
# open a the action linked to the goal
2013-12-17 16:15:41 +00:00
action = goal . definition_id . action_id . read ( ) [ 0 ]
2013-04-08 13:17:29 +00:00
2013-12-17 16:15:41 +00:00
if goal . definition_id . res_id_field :
2013-06-04 18:37:39 +00:00
current_user = self . pool . get ( ' res.users ' ) . browse ( cr , uid , uid , context = context )
2013-12-17 16:15:41 +00:00
action [ ' res_id ' ] = safe_eval ( goal . definition_id . res_id_field , { ' user ' : current_user } )
2013-04-16 13:48:47 +00:00
# if one element to display, should see it in form mode if possible
2013-12-23 16:32:14 +00:00
action [ ' views ' ] = [ ( view_id , mode ) for ( view_id , mode ) in action [ ' views ' ] if mode == ' form ' ] or action [ ' views ' ]
2013-04-08 13:17:29 +00:00
return action
2013-03-27 10:50:40 +00:00
2013-12-16 14:20:27 +00:00
if goal . computation_mode == ' manually ' :
2013-12-23 16:32:14 +00:00
# open a wizard window to update the value manually
2013-12-16 14:20:27 +00:00
action = {
2013-12-17 16:15:41 +00:00
' name ' : _ ( " Update %s " ) % goal . definition_id . name ,
2013-12-16 14:20:27 +00:00
' id ' : goal_id ,
' type ' : ' ir.actions.act_window ' ,
' views ' : [ [ False , ' form ' ] ] ,
' target ' : ' new ' ,
2013-12-23 16:32:14 +00:00
' context ' : { ' default_goal_id ' : goal_id , ' default_current ' : goal . current } ,
' res_model ' : ' gamification.goal.wizard '
2013-12-16 14:20:27 +00:00
}
return action
2013-03-27 10:50:40 +00:00
return False