2013-02-14 08:20:38 +00:00
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
2013-02-26 11:34:03 +00:00
# Copyright (C) 2010-Today 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
#
##############################################################################
from openerp . osv import fields , osv
2013-02-20 12:33:28 +00:00
from openerp . tools . safe_eval import safe_eval
2013-02-15 13:32:34 +00:00
2013-02-26 11:34:03 +00:00
from mako . template import Template as MakoTemplate
2013-02-27 10:59:56 +00:00
from datetime import date , datetime , timedelta
2013-02-21 10:46:46 +00:00
import calendar
2013-02-22 11:44:21 +00:00
import itertools
2013-02-15 09:16:38 +00:00
2013-02-26 14:54:59 +00:00
def start_end_date_for_period ( period ) :
""" Return the start and end date for a goal period based on today
: return ( start_date , end_date ) , datetime . date objects , False if the period is
not defined or unknown """
today = date . today ( )
if period == ' daily ' :
start_date = today
end_date = start_date # ? + timedelta(days=1)
elif period == ' weekly ' :
delta = timedelta ( days = today . weekday ( ) )
start_date = today - delta
end_date = start_date + timedelta ( days = 7 )
elif period == ' monthly ' :
month_range = calendar . monthrange ( today . year , today . month )
start_date = today . replace ( day = month_range [ 0 ] )
end_date = today . replace ( day = month_range [ 1 ] )
elif period == ' yearly ' :
start_date = today . replace ( month = 1 , day = 1 )
end_date = today . replace ( month = 12 , day = 31 )
else : # period == 'once':
start_date = False # for manual goal, start each time
end_date = False
return ( start_date , end_date )
2013-02-14 17:07:36 +00:00
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 '
2013-02-18 12:44:03 +00:00
_order = " sequence "
2013-02-14 17:07:36 +00:00
_columns = {
2013-02-18 13:06:56 +00:00
' name ' : fields . char ( ' Type Name ' , required = True ) ,
2013-02-15 09:16:38 +00:00
' description ' : fields . text ( ' Description ' ) ,
2013-02-20 12:33:28 +00:00
' computation_mode ' : fields . selection ( [
( ' sum ' , ' Sum ' ) ,
( ' count ' , ' Count ' ) ,
( ' manually ' , ' Manually ' )
] ,
2013-02-18 13:06:56 +00:00
string = " Mode of Computation " ,
2013-02-15 09:16:38 +00:00
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
2013-02-14 17:07:36 +00:00
- ' Manually ' for user defined values """ ,
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-02-18 15:16:07 +00:00
help = ' The model object for the field to evaluate ' ) ,
' field_id ' : fields . many2one ( ' ir.model.fields ' ,
2013-02-18 13:06:56 +00:00
string = ' Evaluated Field ' ,
2013-02-15 09:16:38 +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-02-18 13:06:56 +00:00
string = ' Evaluated Date Field ' ,
2013-02-15 09:16:38 +00:00
help = ' The date to use for the time period evaluated ' ) ,
2013-02-18 09:56:44 +00:00
' domain ' : fields . char ( " Domain " ,
help = " Technical filters rules to apply " ,
required = True ) , # how to apply it ?
2013-02-20 12:33:28 +00:00
' condition ' : fields . selection ( [
( ' minus ' , ' <= ' ) ,
( ' plus ' , ' >= ' )
] ,
2013-02-18 13:06:56 +00:00
string = ' Validation Condition ' ,
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-02-14 17:07:36 +00:00
' sequence ' : fields . integer ( ' Sequence ' ,
2013-02-15 09:16:38 +00:00
help = ' Sequence number for ordering ' ,
2013-02-14 17:07:36 +00:00
required = True ) ,
2013-02-14 08:20:38 +00:00
}
2013-02-14 17:07:36 +00:00
2013-02-15 14:54:15 +00:00
_order = ' sequence '
2013-02-14 17:07:36 +00:00
_defaults = {
2013-02-18 12:44:03 +00:00
' sequence ' : 1 ,
2013-02-14 17:07:36 +00:00
' condition ' : ' plus ' ,
2013-02-15 09:16:38 +00:00
' computation_mode ' : ' manually ' ,
2013-02-18 09:56:44 +00:00
' domain ' : " [] " ,
2013-02-14 08:20:38 +00:00
}
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 '
_inherit = ' mail.thread '
2013-02-15 14:54:15 +00:00
def _get_completeness ( 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-02-15 14:54:15 +00:00
res = { }
for goal in self . browse ( cr , uid , ids , context ) :
2013-02-27 10:59:56 +00:00
if goal . current > 0 :
res [ goal . id ] = min ( 100 , round ( 100.0 * goal . current / goal . target_goal , 2 ) )
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-02-20 16:05:17 +00:00
def on_change_type_id ( self , cr , uid , ids , type_id = False , context = None ) :
goal_type = self . pool . get ( ' gamification.goal.type ' )
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 } }
return ret
2013-02-15 14:54:15 +00:00
2013-02-14 17:07:36 +00:00
_columns = {
' type_id ' : fields . many2one ( ' gamification.goal.type ' ,
2013-02-18 13:06:56 +00:00
string = ' Goal Type ' ,
2013-02-19 14:37:22 +00:00
required = True ,
ondelete = " cascade " ) ,
2013-02-14 17:07:36 +00:00
' user_id ' : fields . many2one ( ' res.users ' , string = ' User ' , required = True ) ,
2013-02-20 12:33:28 +00:00
' planline_id ' : fields . many2one ( ' gamification.goal.planline ' ,
2013-02-21 12:57:59 +00:00
string = ' Goal Planline ' ,
2013-02-19 14:37:22 +00:00
ondelete = " cascade " ) ,
2013-02-18 13:06:56 +00:00
' start_date ' : fields . date ( ' Start Date ' ) ,
' end_date ' : fields . date ( ' End Date ' ) , # no start and end = always active
' target_goal ' : fields . float ( ' To Reach ' ,
2013-02-14 17:07:36 +00:00
required = True ,
track_visibility = ' always ' ) , # no goal = global index
' current ' : fields . float ( ' Current ' ,
required = True ,
track_visibility = ' always ' ) ,
2013-02-15 14:54:15 +00:00
' completeness ' : fields . function ( _get_completeness ,
type = ' float ' ,
2013-02-20 12:33:28 +00:00
string = ' Completeness ' ) ,
' state ' : fields . selection ( [
2013-02-22 13:40:24 +00:00
( ' draft ' , ' Draft ' ) ,
2013-02-20 12:33:28 +00:00
( ' inprogress ' , ' In progress ' ) ,
( ' inprogress_update ' , ' In progress (to update) ' ) ,
( ' 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 ,
track_visibility = ' always ' ) ,
2013-02-20 16:05:17 +00:00
' 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. " ) ,
2013-02-19 16:52:15 +00:00
' 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. " ) , #
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-20 16:05:17 +00:00
' last_update ' : fields . date . today ,
2013-02-14 17:07:36 +00:00
}
2013-02-14 08:20:38 +00:00
2013-02-21 13:50:00 +00:00
def _update_all ( self , cr , uid , ids = False , context = None ) :
""" Update every goal in progress """
if not ids :
2013-02-22 09:47:45 +00:00
ids = self . search ( cr , uid , [ ( ' state ' , ' in ' , ( ' inprogress ' , ' inprogress_update ' , ' reached ' ) ) ] )
2013-02-22 11:44:21 +00:00
2013-02-21 13:50:00 +00:00
return self . update ( cr , uid , ids , context = context )
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
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
2013-02-22 09:56:15 +00:00
the target value being reached , the goal is set as failed . """
2013-02-27 10:59:56 +00:00
2013-02-20 14:22:48 +00:00
for goal in self . browse ( cr , uid , ids , context = context or { } ) :
2013-02-22 09:56:15 +00:00
if goal . state not in ( ' inprogress ' , ' inprogress_update ' , ' reached ' ) :
2013-02-22 09:47:45 +00:00
# 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)
2013-02-20 14:22:48 +00:00
continue
2013-02-20 16:17:55 +00:00
if goal . type_id . computation_mode == ' manually ' :
2013-02-22 09:47:45 +00:00
towrite = { ' current ' : goal . current }
2013-02-20 16:17:55 +00:00
# check for remind to update
if goal . remind_update_delay and goal . last_update :
delta_max = timedelta ( days = goal . remind_update_delay )
2013-02-27 10:59:56 +00:00
last_update = datetime . strptime ( goal . last_update , ' % Y- % m- %d ' ) . date ( )
if date . today ( ) - last_update > delta_max :
2013-02-20 16:17:55 +00:00
towrite [ ' state ' ] = ' inprogress_update '
2013-02-20 14:22:48 +00:00
2013-02-27 10:59:56 +00:00
# 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 )
2013-02-20 16:17:55 +00:00
else : # count or sum
2013-02-20 14:22:48 +00:00
obj = self . pool . get ( goal . type_id . model_id . model )
field_date_name = goal . type_id . field_date_id . name
2013-02-22 09:47:45 +00:00
domain = safe_eval ( goal . type_id . domain ,
{ ' user_id ' : goal . user_id . id } )
2013-02-20 14:22:48 +00:00
if goal . start_date :
domain . append ( ( field_date_name , ' >= ' , goal . start_date ) )
if goal . end_date :
domain . append ( ( field_date_name , ' <= ' , goal . end_date ) )
2013-02-20 16:17:55 +00:00
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 )
towrite = { ' current ' : res [ 0 ] [ field_name ] }
else : # computation mode = count
res = obj . search ( cr , uid , domain , context = context )
towrite = { ' current ' : len ( res ) }
# check goal target reached
if ( goal . type_id . condition == ' plus ' \
and towrite [ ' current ' ] > = goal . target_goal ) \
or ( goal . type_id . condition == ' minus ' \
and towrite [ ' current ' ] < = goal . target_goal ) :
2013-02-20 14:22:48 +00:00
towrite [ ' state ' ] = ' reached '
2013-02-20 16:17:55 +00:00
# check goal failure
2013-02-20 16:05:17 +00:00
elif goal . end_date and fields . date . today ( ) > goal . end_date :
2013-02-20 14:22:48 +00:00
towrite [ ' state ' ] = ' failed '
2013-02-20 16:05:17 +00:00
2013-02-20 14:22:48 +00:00
self . write ( cr , uid , [ goal . id ] , towrite , context = context )
return True
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 ) :
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 )
2013-02-14 08:20:38 +00:00
2013-02-26 15:23:25 +00:00
def write ( self , cr , uid , ids , vals , context = None ) :
2013-02-26 15:32:47 +00:00
""" Overwrite the write method to update the last_update field to today """
2013-02-26 15:23:25 +00:00
for goal in self . browse ( cr , uid , ids , vals ) :
2013-02-26 15:32:47 +00:00
vals [ ' last_update ' ] = fields . date . today ( )
2013-02-26 15:23:25 +00:00
write_res = super ( gamification_goal , self ) . write ( cr , uid , ids , vals , context = context )
return write_res
2013-02-14 17:07:36 +00:00
class gamification_goal_plan ( osv . Model ) :
2013-02-20 16:05:17 +00:00
""" Gamification goal plan
2013-02-14 17:07:36 +00:00
Set of predifined goals to be able to automate goal settings or
quickly apply several goals manually to a group of users
If ' user_ids ' is defined and ' period ' is different than ' one ' , the set will
be assigned to the users for each period ( eg : every 1 st of each month if
' monthly ' is selected )
"""
_name = ' gamification.goal.plan '
_description = ' Gamification goal plan '
_columns = {
2013-02-18 13:06:56 +00:00
' name ' : fields . char ( ' Plan Name ' , required = True ) ,
2013-02-14 17:07:36 +00:00
' user_ids ' : fields . many2many ( ' res.users ' ,
2013-02-15 13:09:21 +00:00
string = ' Users ' ,
2013-02-15 09:16:38 +00:00
help = " list of users to which the goal will be set " ) ,
2013-02-15 13:09:21 +00:00
' planline_ids ' : fields . one2many ( ' gamification.goal.planline ' ,
' plan_id ' ,
string = ' Planline ' ,
2013-02-18 15:49:15 +00:00
help = " list of goals that will be set " ,
required = True ) ,
2013-02-15 13:32:34 +00:00
' autojoin_group_id ' : fields . many2one ( ' res.groups ' ,
2013-02-27 08:46:08 +00:00
string = ' Auto-join Group ' ,
2013-02-15 13:32:34 +00:00
help = ' Group of users whose members will automatically be added to the users ' ) ,
2013-02-20 12:33:28 +00:00
' period ' : fields . selection ( [
2013-02-22 09:47:45 +00:00
( ' once ' , ' No Periodicity ' ) ,
2013-02-20 12:33:28 +00:00
( ' daily ' , ' Daily ' ) ,
( ' weekly ' , ' Weekly ' ) ,
( ' monthly ' , ' Monthly ' ) ,
( ' yearly ' , ' Yearly ' )
] ,
string = ' Periodicity ' ,
2013-02-15 09:16:38 +00:00
help = ' Period of automatic goal assigment, will be done manually if none is selected ' ,
2013-02-14 17:07:36 +00:00
required = True ) ,
2013-02-20 12:33:28 +00:00
' state ' : fields . selection ( [
( ' draft ' , ' Draft ' ) ,
( ' inprogress ' , ' In progress ' ) ,
( ' done ' , ' Done ' ) ,
] ,
2013-02-15 14:16:51 +00:00
string = ' State ' ,
2013-02-14 17:07:36 +00:00
required = True ) ,
2013-02-20 12:33:28 +00:00
' visibility_mode ' : fields . selection ( [
( ' board ' , ' Leader board ' ) ,
( ' progressbar ' , ' Personal progressbar ' )
] ,
string = " Visibility " ,
help = ' How are displayed the results, shared or in a single progressbar ' ,
2013-02-15 13:32:34 +00:00
required = True ) ,
2013-02-20 12:33:28 +00:00
' report_message_frequency ' : fields . selection ( [
( ' never ' , ' Never ' ) ,
( ' onchange ' , ' On change ' ) ,
( ' daily ' , ' Daily ' ) ,
( ' weekly ' , ' Weekly ' ) ,
( ' monthly ' , ' Monthly ' ) ,
( ' yearly ' , ' Yearly ' )
] ,
2013-02-15 13:32:34 +00:00
string = " Frequency " ,
required = True ) ,
' report_message_group_id ' : fields . many2one ( ' mail.group ' ,
2013-02-27 08:46:08 +00:00
string = ' Send a copy to ' ,
help = ' Group that will receive a copy of the report in addition to the user ' ) ,
2013-02-18 13:06:56 +00:00
' report_header ' : fields . text ( ' Report Header ' ) ,
2013-02-20 16:05:17 +00:00
' remind_update_delay ' : fields . integer ( ' Remind delay ' ,
2013-02-19 16:52:15 +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-02-14 17:07:36 +00:00
}
_defaults = {
' period ' : ' once ' ,
2013-02-15 16:02:04 +00:00
' state ' : ' draft ' ,
2013-02-20 12:33:28 +00:00
' visibility_mode ' : ' progressbar ' ,
2013-02-15 13:32:34 +00:00
' report_message_frequency ' : ' onchange ' ,
2013-02-14 17:07:36 +00:00
}
2013-02-14 08:20:38 +00:00
2013-02-18 15:59:33 +00:00
def _check_nonzero_planline ( self , cr , uid , ids , context = None ) :
2013-02-21 12:57:59 +00:00
""" checks that there is at least one planline set """
2013-02-18 15:59:33 +00:00
for plan in self . browse ( cr , uid , ids , context ) :
if len ( plan . planline_ids ) < 1 :
return False
return True
def _check_nonzero_users ( self , cr , uid , ids , context = None ) :
2013-02-21 12:57:59 +00:00
""" checks that there is at least one user set """
2013-02-18 15:59:33 +00:00
for plan in self . browse ( cr , uid , ids , context ) :
2013-02-19 14:37:22 +00:00
if len ( plan . user_ids ) < 1 and plan . state != ' draft ' :
2013-02-18 15:59:33 +00:00
return False
return True
_constraints = [
( _check_nonzero_planline , " At least one planline is required to create a goal plan " , [ ' planline_ids ' ] ) ,
2013-02-19 14:37:22 +00:00
( _check_nonzero_users , " At least one user is required to create a non-draft goal plan " , [ ' user_ids ' ] ) ,
2013-02-18 15:59:33 +00:00
]
2013-02-27 08:46:08 +00:00
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
2013-02-21 13:50:00 +00:00
def _update_all ( self , cr , uid , ids = False , context = None ) :
""" Update every plan in progress """
if not ids :
2013-02-22 09:47:45 +00:00
ids = self . search ( cr , uid , [ ( ' state ' , ' = ' , ' inprogress ' ) ,
( ' period ' , ' != ' , ' once ' ) ] )
2013-02-22 11:44:21 +00:00
2013-02-21 13:50:00 +00:00
return self . generate_goals_from_plan ( cr , uid , ids , context = context )
2013-02-15 16:02:04 +00:00
def action_start ( self , cr , uid , ids , context = None ) :
2013-02-19 13:23:06 +00:00
""" Start a draft goal plan
2013-02-21 10:46:46 +00:00
Change the state of the plan to in progress """
self . generate_goals_from_plan ( cr , uid , ids , context = context )
2013-02-15 16:02:04 +00:00
return self . write ( cr , uid , ids , { ' state ' : ' inprogress ' } , context = context )
2013-02-21 12:57:59 +00:00
def action_check ( self , cr , uid , ids , context = None ) :
""" Check a goal plan in progress
Create goals that haven ' t been created yet (eg: if added users of planlines)
Recompute the current value for each goal related """
self . generate_goals_from_plan ( cr , uid , ids , context = context )
for plan in self . browse ( cr , uid , ids , context ) :
2013-02-21 13:50:00 +00:00
if plan . state != ' improgress ' :
continue
2013-02-21 12:57:59 +00:00
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 )
2013-02-22 09:56:15 +00:00
goal_obj . update ( cr , uid , goal_ids , context = context )
2013-02-21 12:57:59 +00:00
return True
2013-02-15 16:02:04 +00:00
def action_close ( self , cr , uid , ids , context = None ) :
2013-02-19 13:23:06 +00:00
""" Close a plan in progress
Change the state of the plan to in done
2013-02-21 11:15:55 +00:00
Does NOT close the related goals , this is handled by the goal itself """
2013-02-15 16:02:04 +00:00
return self . write ( cr , uid , ids , { ' state ' : ' done ' } , context = context )
2013-02-21 12:57:59 +00:00
def action_reset ( self , cr , uid , ids , context = None ) :
""" Reset a closed goal plan
Change the state of the plan to in progress
2013-02-25 08:45:40 +00:00
Closing a plan does not affect the goals so neither does reset """
2013-02-21 12:57:59 +00:00
return self . write ( cr , uid , ids , { ' state ' : ' inprogress ' } , context = context )
2013-02-15 16:02:04 +00:00
def action_cancel ( self , cr , uid , ids , context = None ) :
2013-02-19 13:23:06 +00:00
""" Cancel a plan in progress
Change the state of the plan to draft
2013-02-21 11:15:55 +00:00
Cancel the related goals """
2013-02-21 10:46:46 +00:00
self . write ( cr , uid , ids , { ' state ' : ' draft ' } , context = context )
for plan in self . browse ( cr , uid , ids , context ) :
for planline in plan . planline_ids :
2013-02-21 11:15:55 +00:00
goal_obj = self . pool . get ( ' gamification.goal ' )
2013-02-22 14:10:16 +00:00
goal_ids = goal_obj . search ( cr , uid , [ ( ' planline_id ' , ' = ' , planline . id ) ] , context = context )
goal_obj . write ( cr , uid , goal_ids , { ' state ' : ' canceled ' } , context = context )
2013-02-21 10:46:46 +00:00
return True
2013-02-15 16:02:04 +00:00
2013-02-27 09:42:13 +00:00
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
2013-02-20 12:33:28 +00:00
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 ) :
2013-02-26 14:54:59 +00:00
( start_date , end_date ) = start_end_date_for_period ( plan . period )
2013-02-21 10:46:46 +00:00
2013-02-20 12:33:28 +00:00
for planline in plan . planline_ids :
for user in plan . user_ids :
2013-02-22 14:10:16 +00:00
#self.create_goal_from_plan(cr, uid, ids, planline.id, user.id, start_date, context=context)
2013-02-20 12:33:28 +00:00
goal_obj = self . pool . get ( ' gamification.goal ' )
2013-02-22 14:10:16 +00:00
domain = [ ( ' planline_id ' , ' = ' , planline . id ) ,
( ' user_id ' , ' = ' , user . id ) ]
if start_date :
2013-02-26 14:54:59 +00:00
domain . append ( ( ' start_date ' , ' = ' , start_date ) )
2013-02-22 14:10:16 +00:00
2013-02-22 14:22:12 +00:00
# goal existing for this planline ?
2013-02-22 14:10:16 +00:00
if len ( goal_obj . search ( cr , uid , domain , context = context ) ) > 0 :
2013-02-22 14:22:12 +00:00
# resume canceled goals
domain . append ( ( ' state ' , ' = ' , ' canceled ' ) )
canceled_goal_ids = goal_obj . search ( cr , uid , domain , context = context )
goal_obj . write ( cr , uid , canceled_goal_ids , { ' state ' : ' inprogress ' } , context = context )
goal_obj . update ( cr , uid , canceled_goal_ids , context = context )
# skip to next user
2013-02-22 14:10:16 +00:00
continue
values = {
' type_id ' : planline . type_id . id ,
' planline_id ' : planline . id ,
' user_id ' : user . id ,
' target_goal ' : planline . target_goal ,
' state ' : ' inprogress ' ,
}
if start_date :
values [ ' start_date ' ] = start_date . isoformat ( )
2013-02-26 14:54:59 +00:00
if end_date :
values [ ' end_date ' ] = end_date . isoformat ( )
2013-02-22 14:10:16 +00:00
if planline . plan_id . remind_update_delay :
values [ ' remind_update_delay ' ] = planline . plan_id . remind_update_delay
new_goal_id = goal_obj . create ( cr , uid , values , context )
2013-02-22 14:22:12 +00:00
goal_obj . update ( cr , uid , [ new_goal_id ] , context = context )
2013-02-20 12:33:28 +00:00
2013-02-21 11:15:55 +00:00
return True
2013-02-20 12:33:28 +00:00
2013-02-22 11:44:21 +00:00
2013-02-27 08:46:08 +00:00
def plan_subscribe_users ( self , cr , uid , ids , new_user_ids , context = None ) :
2013-02-22 11:44:21 +00:00
""" Add the following users to plans
: param ids : ids of plans to which the users will be added
: param user_ids : ids of the users to add """
for plan in self . browse ( cr , uid , ids , context ) :
subscription = [ user . id for user in plan . user_ids ]
2013-02-27 08:46:08 +00:00
subscription . extend ( new_user_ids )
2013-02-22 11:44:21 +00:00
unified_subscription = list ( set ( subscription ) )
self . write ( cr , uid , ids , { ' user_ids ' : [ ( 4 , uid ) for uid in unified_subscription ] } , context = context )
return True
2013-02-25 08:45:40 +00:00
def report_progress ( self , cr , uid , ids , context = None ) :
""" Post report about the progress of the goals """
goal_obj = self . pool . get ( ' gamification.goal ' )
2013-02-26 11:34:03 +00:00
2013-02-25 08:45:40 +00:00
for plan in self . browse ( cr , uid , ids , context = context ) :
2013-02-26 14:54:59 +00:00
if not plan . report_message_group_id :
# no report group, skipping
continue
# copy of context to access more variables in templates
template_context = dict ( context )
2013-02-25 08:45:40 +00:00
if plan . visibility_mode == ' board ' :
# generate a shared report
2013-02-26 14:54:59 +00:00
template_id = self . pool . get ( ' ir.model.data ' ) . get_object_reference ( cr , uid , ' gamification ' , ' email_template_gamification_leaderboard ' ) [ 1 ]
# unsorted list of planline
template_context [ ' planlines ' ] = [ ]
for planline in plan . planline_ids :
( start_date , end_date ) = start_end_date_for_period ( plan . period )
domain = [
( ' planline_id ' , ' = ' , planline . id ) ,
( ' state ' , ' in ' , ( ' inprogress ' , ' inprogress_update ' ,
' reached ' , ' failed ' ) ) ,
]
if start_date :
domain . append ( ( ' start_date ' , ' = ' , start_date . isoformat ( ) ) )
goal_ids = goal_obj . search ( cr , uid , domain , context = context )
planlines_stats = [ ]
for goal in goal_obj . browse ( cr , uid , goal_ids , context = context ) :
planlines_stats . append ( {
' user ' : goal . user_id ,
' current ' : goal . current ,
' target_goal ' : goal . target_goal ,
' completeness ' : goal . completeness ,
} )
# 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 } )
2013-02-26 15:23:25 +00:00
2013-02-26 14:54:59 +00:00
self . pool . get ( ' email.template ' ) . send_mail ( cr , uid , template_id , plan . id , context = template_context )
2013-02-25 08:45:40 +00:00
else :
# generate individual reports
2013-02-26 11:34:03 +00:00
template_id = self . pool . get ( ' ir.model.data ' ) . get_object_reference ( cr , uid , ' gamification ' , ' email_template_gamification_individual ' ) [ 1 ]
2013-02-25 08:45:40 +00:00
for user in plan . user_ids :
2013-02-26 11:34:03 +00:00
2013-02-25 08:45:40 +00:00
goal_ids = self . get_current_related_user_goals ( cr , uid , plan . id , user . id , context )
2013-02-26 11:34:03 +00:00
if len ( goal_ids ) == 0 :
continue
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 )
2013-02-26 14:54:59 +00:00
return True
2013-02-25 08:45:40 +00:00
2013-02-27 09:42:13 +00:00
2013-02-26 14:54:59 +00:00
def get_current_related_goals ( self , cr , uid , plan_id , user_id , context = None ) :
2013-02-25 08:45:40 +00:00
""" Get the ids of goals linked to a plan for the current instance
If several goals are linked to the same planline and user , only the
2013-02-26 14:54:59 +00:00
latest instance of the plan is checked ( eg : if the plan is monthly ,
2013-02-25 08:45:40 +00:00
return the goals started the 1 st of this month ) .
"""
2013-02-26 11:34:03 +00:00
plan = self . browse ( cr , uid , plan_id , context = context )
2013-02-26 14:54:59 +00:00
( start_date , end_date ) = start_end_date_for_period ( plan . period )
2013-02-25 08:45:40 +00:00
goal_obj = self . pool . get ( ' gamification.goal ' )
related_goal_ids = [ ]
for planline in plan . planline_ids :
domain = [ ( ' planline_id ' , ' = ' , planline . id ) ,
( ' user_id ' , ' = ' , user_id ) ]
if start_date :
domain . append ( ( ' start_date ' , ' = ' , start_date . isoformat ( ) ) )
goal_ids = goal_obj . search ( cr , uid , domain , context = context )
2013-02-27 09:42:13 +00:00
related_goal_ids . append ( goal_ids [ 0 ] )
if len ( goal_ids ) == 0 :
2013-02-25 08:45:40 +00:00
# this goal has been deleted
2013-02-26 11:34:03 +00:00
raise osv . except_osv ( ' Warning! ' , ' Planline {0} has no goal present for user {1} at date {2} ' . format ( planline . id , user . id , start_date ) )
2013-02-25 08:45:40 +00:00
else : # more than one goal ?
2013-02-26 11:34:03 +00:00
raise osv . except_osv ( ' Warning! ' , ' Duplicate goals for planline {0} , user {1} , date {2} ' . format ( planline . id , user . id , start_date ) )
2013-02-25 08:45:40 +00:00
related_goal_ids . extend ( goal_ids )
2013-02-26 11:34:03 +00:00
return related_goal_ids
2013-02-14 17:07:36 +00:00
class gamification_goal_planline ( osv . Model ) :
""" Gamification goal planline
2013-02-14 08:20:38 +00:00
2013-02-14 17:07:36 +00:00
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
"""
2013-02-14 08:20:38 +00:00
2013-02-14 17:07:36 +00:00
_name = ' gamification.goal.planline '
_description = ' Gamification generic goal for plan '
2013-02-18 12:44:03 +00:00
_order = " sequence_type "
def _get_planline_types ( self , cr , uid , ids , context = None ) :
""" Return the ids of planline items related to the gamification.goal.type
objects in ' ids (used to update the value of ' sequence_type ' ) ' """
result = { }
for goal_type in self . pool . get ( ' gamification.goal.type ' ) . browse ( cr , uid , ids , context = context ) :
domain = [ ( ' type_id ' , ' = ' , goal_type . id ) ]
planline_ids = self . pool . get ( ' gamification.goal.planline ' ) . search ( cr , uid , domain , context = context )
for p_id in planline_ids :
result [ p_id ] = True
return result . keys ( )
2013-02-14 08:20:38 +00:00
2013-02-14 17:07:36 +00:00
_columns = {
' plan_id ' : fields . many2one ( ' gamification.goal.plan ' ,
2013-02-19 14:37:22 +00:00
string = ' Plan ' ,
ondelete = " cascade " ) ,
2013-02-14 17:07:36 +00:00
' type_id ' : fields . many2one ( ' gamification.goal.type ' ,
2013-02-18 13:06:56 +00:00
string = ' Goal Type ' ,
2013-02-19 14:37:22 +00:00
required = True ,
ondelete = " cascade " ) ,
2013-02-18 13:06:56 +00:00
' target_goal ' : fields . float ( ' Target Value to Reach ' ,
required = True ) ,
2013-02-18 12:44:03 +00:00
' sequence_type ' : fields . related ( ' type_id ' , ' sequence ' ,
type = ' integer ' ,
string = ' Sequence ' ,
readonly = True ,
store = {
' gamification.goal.type ' : ( _get_planline_types , [ ' sequence ' ] , 10 ) ,
} ) ,
2013-02-26 11:34:03 +00:00
}