[REF] gamification: pretty much changing half of the code to make tde happy...

bzr revid: mat@openerp.com-20131217161541-oxsgy7gmko2x6qui
This commit is contained in:
Martin Trigaux 2013-12-17 17:15:41 +01:00
parent 3e028490f6
commit 122c15c48d
31 changed files with 644 additions and 685 deletions

View File

@ -19,8 +19,5 @@
#
##############################################################################
import goal
import goal_type_data
import plan
import res_users
import badge
import models
import data

View File

@ -30,7 +30,7 @@ Gamification process
The Gamification module provides ways to evaluate and motivate the users of OpenERP.
The users can be evaluated using goals and numerical objectives to reach.
**Goals** are assigned through **plans** to evaluate and compare members of a team with each others and through time.
**Goals** are assigned through **challenges** to evaluate and compare members of a team with each others and through time.
For non-numerical achievements, **badges** can be granted to users. From a simple "thank you" to an exceptional achievement, a badge is an easy way to exprimate gratitude to a user for their good work.
@ -38,24 +38,23 @@ Both goals and badges are flexibles and can be adapted to a large range of modul
""",
'data': [
'plan_view.xml',
'badge_view.xml',
'goal_view.xml',
'cron.xml',
'views/challenge.xml',
'views/badge.xml',
'views/goal.xml',
'data/cron.xml',
'security/gamification_security.xml',
'security/ir.model.access.csv',
'goal_base_data.xml',
'badge_data.xml',
'data/goal_base.xml',
'data/badge.xml',
],
'test': [
'test/goal_demo.yml'
],
'installable': True,
'application': True,
'auto_install': True,
'auto_install': False,
'css': ['static/src/css/gamification.css'],
'js': [
'static/src/js/gamification.js',
],
'js': ['static/src/js/gamification.js',],
'qweb': ['static/src/xml/gamification.xml'],
}

View File

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2013 OpenERP SA (<http://openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import goal_definition

View File

@ -1,14 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<data>
<record forcecreate="True" id="ir_cron_check_plan"
<record forcecreate="True" id="ir_cron_check_challenge"
model="ir.cron">
<field name="name">Run Goal Plan Checker</field>
<field name="name">Run Goal Challenge Checker</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall" />
<field name="model">gamification.goal.plan</field>
<field name="model">gamification.challenge</field>
<field name="function">_cron_update</field>
<field name="args">()</field>
</record>

View File

@ -2,8 +2,8 @@
<openerp>
<data>
<!-- goal types -->
<record model="gamification.goal.type" id="type_base_timezone">
<!-- goal definitions -->
<record model="gamification.goal.definition" id="definition_base_timezone">
<field name="name">Set your Timezone</field>
<field name="description">Configure your profile and specify your timezone</field>
<field name="computation_mode">count</field>
@ -14,7 +14,7 @@
<field name="res_id_field">user.id</field>
</record>
<record model="gamification.goal.type" id="type_base_avatar">
<record model="gamification.goal.definition" id="definition_base_avatar">
<field name="name">Set your Avatar</field>
<field name="description">In your user preference</field>
<field name="computation_mode">manually</field>
@ -25,7 +25,7 @@
</record>
<record model="gamification.goal.type" id="type_base_company_data">
<record model="gamification.goal.definition" id="definition_base_company_data">
<field name="name">Set your Company Data</field>
<field name="description">Write some information about your company (specify at least a name)</field>
<field name="computation_mode">count</field>
@ -37,7 +37,7 @@
<field name="res_id_field">user.company_id.id</field>
</record>
<record model="gamification.goal.type" id="type_base_company_logo">
<record model="gamification.goal.definition" id="definition_base_company_logo">
<field name="name">Set your Company Logo</field>
<field name="computation_mode">count</field>
<field name="display_mode">checkbox</field>
@ -58,7 +58,7 @@
<field name="help">Create and manage users that will connect to the system. Users can be deactivated should there be a period of time during which they will/should not connect to the system. You can assign them groups in order to give them specific access to the applications they need to use in the system.</field>
</record>
<record model="gamification.goal.type" id="type_base_invite">
<record model="gamification.goal.definition" id="definition_base_invite">
<field name="name">Invite new Users</field>
<field name="description">Create at least another user</field>
<field name="display_mode">checkbox</field>
@ -68,7 +68,7 @@
<field name="action_id" eval="ref('action_new_simplified_res_users')" />
</record>
<record model="gamification.goal.type" id="type_nbr_following">
<record model="gamification.goal.definition" id="definition_nbr_following">
<field name="name">Mail Group Following</field>
<field name="description">Follow mail groups to receive news</field>
<field name="computation_mode">python</field>
@ -77,8 +77,8 @@
</record>
<!-- plans -->
<record model="gamification.goal.plan" id="plan_base_discover">
<!-- challenges -->
<record model="gamification.challenge" id="challenge_base_discover">
<field name="name">Complete your Profile</field>
<field name="period">once</field>
<field name="visibility_mode">progressbar</field>
@ -88,7 +88,7 @@
<field name="category">other</field>
</record>
<record model="gamification.goal.plan" id="plan_base_configure">
<record model="gamification.challenge" id="challenge_base_configure">
<field name="name">Setup your Company</field>
<field name="period">once</field>
<field name="visibility_mode">progressbar</field>
@ -98,32 +98,32 @@
<field name="category">other</field>
</record>
<!-- planlines -->
<record model="gamification.goal.planline" id="planline_base_discover1">
<field name="type_id" eval="ref('type_base_timezone')" />
<!-- lines -->
<record model="gamification.challenge.line" id="line_base_discover1">
<field name="definition_id" eval="ref('definition_base_timezone')" />
<field name="target_goal">1</field>
<field name="plan_id" eval="ref('plan_base_discover')" />
<field name="challenge_id" eval="ref('challenge_base_discover')" />
</record>
<record model="gamification.goal.planline" id="planline_base_discover2">
<field name="type_id" eval="ref('type_base_avatar')" />
<record model="gamification.challenge.line" id="line_base_discover2">
<field name="definition_id" eval="ref('definition_base_avatar')" />
<field name="target_goal">1</field>
<field name="plan_id" eval="ref('plan_base_discover')" />
<field name="challenge_id" eval="ref('challenge_base_discover')" />
</record>
<record model="gamification.goal.planline" id="planline_base_admin2">
<field name="type_id" eval="ref('type_base_company_logo')" />
<record model="gamification.challenge.line" id="line_base_admin2">
<field name="definition_id" eval="ref('definition_base_company_logo')" />
<field name="target_goal">1</field>
<field name="plan_id" eval="ref('plan_base_configure')" />
<field name="challenge_id" eval="ref('challenge_base_configure')" />
</record>
<record model="gamification.goal.planline" id="planline_base_admin1">
<field name="type_id" eval="ref('type_base_company_data')" />
<record model="gamification.challenge.line" id="line_base_admin1">
<field name="definition_id" eval="ref('definition_base_company_data')" />
<field name="target_goal">0</field>
<field name="plan_id" eval="ref('plan_base_configure')" />
<field name="challenge_id" eval="ref('challenge_base_configure')" />
</record>
<record model="gamification.goal.planline" id="planline_base_admin3">
<field name="type_id" eval="ref('type_base_invite')" />
<record model="gamification.challenge.line" id="line_base_admin3">
<field name="definition_id" eval="ref('definition_base_invite')" />
<field name="target_goal">1</field>
<field name="plan_id" eval="ref('plan_base_configure')" />
<field name="challenge_id" eval="ref('challenge_base_configure')" />
</record>
</data>
@ -141,7 +141,7 @@
<p class="oe_grey">${object.report_header or ''}</p>
<p>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.</p>
<p>You have not updated your progress for the goal ${object.definition_id.name} (currently reached at ${object.completeness}%) for at least ${object.remind_update_delay} days. Do not forget to do it.</p>
<p>If you have not changed your score yet, you can use the button "The current value is up to date" to indicate so.</p>
]]></field>
@ -168,15 +168,15 @@
style="font-weight:bold;"
% endif
>
<td>${goal.type_id.name}</td>
<td>${goal.definition_id.name}</td>
<td>${goal.target_goal}
% if goal.type_suffix:
${goal.type_suffix}
% if goal.definition_suffix:
${goal.definition_suffix}
% endif
</td>
<td>${goal.current}
% if goal.type_suffix:
${goal.type_suffix}
% if goal.definition_suffix:
${goal.definition_suffix}
% endif
</td>
<td>${goal.completeness} %</td>
@ -193,10 +193,10 @@
</header>
<p class="oe_grey">${object.report_header or ''}</p>
% for planline in ctx['planlines_boards']:
% for line in ctx['lines_boards']:
<table width="100%" border="1">
<tr>
<th colspan="4">${planline.goal_type.name}</th>
<th colspan="4">${line.goal_definition.name}</th>
</tr>
<tr>
<th>#</th>
@ -204,7 +204,7 @@
<th>Completeness</th>
<th>Current</th>
</tr>
% for idx, goal in planline.board_goals:
% for idx, goal in line.board_goals:
% if idx < 3 or goal.user_id.id == user.id:
<tr
% if goal.completeness >= 100:
@ -215,8 +215,8 @@
<td>${goal.user_id.name}</td>
<td>${goal.completeness}%</td>
<td>${goal.current}/${goal.target_goal}
% if goal.type_suffix:
${goal.type_suffix}
% if goal.definition_suffix:
${goal.definition_suffix}
% endif
</td>
</tr>

View File

@ -22,14 +22,14 @@
from openerp.osv import osv
class gamification_goal_type_data(osv.Model):
"""Goal type data
class gamification_goal_definition_data(osv.Model):
"""Goal definition data
Methods for more complex goals not possible with the 'sum' and 'count' mode.
Each method should return the value that will be set in the 'current' field
of a user's goal. The return type must be a float or integer.
of a user's goal. The return definition must be a float or integer.
"""
_inherit = 'gamification.goal.type'
_inherit = 'gamification.goal.definition'
def number_following(self, cr, uid, xml_id="mail.thread", context=None):
"""Return the number of 'xml_id' objects the user is following

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2013 OpenERP SA (<http://openerp.com>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import goal
import challenge
import res_users
import badge

View File

@ -141,11 +141,11 @@ class gamification_badge(osv.Model):
'remaining_sending': fields.function(_remaining_sending_calc, type='integer',
string='Remaining Sending Allowed', help="If a maxium is set"),
'plan_ids': fields.one2many('gamification.goal.plan', 'reward_id',
'challenge_ids': fields.one2many('gamification.challenge', 'reward_id',
string="Reward of Challenges"),
'goal_type_ids': fields.many2many('gamification.goal.type',
string='Goals Linked',
'goal_definition_ids': fields.many2many('gamification.goal.definition', 'badge_unlocked_definition_rel',
string='Rewarded by',
help="The users that have succeeded theses goals will receive automatically the badge."),
'owner_ids': fields.one2many('gamification.badge.user', 'badge_id',

View File

@ -61,19 +61,19 @@ def start_end_date_for_period(period, default_start_date=False, default_end_date
return (start_date, end_date)
class gamification_goal_plan(osv.Model):
"""Gamification goal plan
class gamification_challenge(osv.Model):
"""Gamification challenge
Set of predifined goals to be able to automate goal settings or
quickly apply several goals manually to a group of users
Set of predifined objectives assigned to people with rules for recurrence and
rewards
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 = 'gamification.goal.plan'
_description = 'Gamification goal plan'
_name = 'gamification.challenge'
_description = 'Gamification challenge'
_inherit = 'mail.thread'
def _get_next_report_date(self, cr, uid, ids, field_name, arg, context=None):
@ -82,32 +82,27 @@ class gamification_goal_plan(osv.Model):
:return: a string in DEFAULT_SERVER_DATE_FORMAT representing the date"""
res = {}
for plan in self.browse(cr, uid, ids, context):
last = datetime.strptime(plan.last_report_date, DF).date()
if plan.report_message_frequency == 'daily':
for challenge in self.browse(cr, uid, ids, context):
last = datetime.strptime(challenge.last_report_date, DF).date()
if challenge.report_message_frequency == 'daily':
next = last + timedelta(days=1)
res[plan.id] = next.strftime(DF)
elif plan.report_message_frequency == 'weekly':
res[challenge.id] = next.strftime(DF)
elif challenge.report_message_frequency == 'weekly':
next = last + timedelta(days=7)
res[plan.id] = next.strftime(DF)
elif plan.report_message_frequency == 'monthly':
res[challenge.id] = next.strftime(DF)
elif challenge.report_message_frequency == 'monthly':
month_range = calendar.monthrange(last.year, last.month)
next = last.replace(day=month_range[1]) + timedelta(days=1)
res[plan.id] = next.strftime(DF)
elif plan.report_message_frequency == 'yearly':
res[plan.id] = last.replace(year=last.year + 1).strftime(DF)
res[challenge.id] = next.strftime(DF)
elif challenge.report_message_frequency == 'yearly':
res[challenge.id] = last.replace(year=last.year + 1).strftime(DF)
# frequency == 'once', reported when closed only
else:
res[plan.id] = False
res[challenge.id] = False
return res
def _planline_count(self, cr, uid, ids, field_name, arg, context=None):
res = dict.fromkeys(ids, 0)
for plan in self.browse(cr, uid, ids, context):
res[plan.id] = len(plan.planline_ids)
return res
_sort = 'end_date, start_date, name, id'
_columns = {
'name': fields.char('Challenge Name', required=True, translate=True),
'description': fields.text('Description', translate=True),
@ -123,10 +118,10 @@ class gamification_goal_plan(osv.Model):
'user_ids': fields.many2many('res.users', 'user_ids',
string='Users',
help="List of users to which the goal will be set"),
help="List of users participating to the challenge"),
'autojoin_group_id': fields.many2one('res.groups',
string='Auto-subscription Group',
help='Group of users whose members will automatically be added to the users'),
help='Group of users whose members will be automatically added to user_ids once the challenge is started'),
'period': fields.selection([
('once', 'Non recurring'),
@ -143,14 +138,13 @@ class gamification_goal_plan(osv.Model):
'end_date': fields.date('End Date',
help="The day a new challenge will be automatically closed. If no periodicity is set, will use this date as the goal end date."),
'proposed_user_ids': fields.many2many('res.users', 'proposed_user_ids',
'invited_user_ids': fields.many2many('res.users', 'invited_user_ids',
string="Suggest to users"),
'planline_ids': fields.one2many('gamification.goal.planline', 'plan_id',
string='Planline',
'line_ids': fields.one2many('gamification.challenge.line', 'challenge_id',
string='Lines',
help="List of goals that will be set",
required=True),
'planline_count': fields.function(_planline_count, type='integer', string="Planlines"),
'reward_id': fields.many2one('gamification.badge', string="For Every Succeding User"),
'reward_first_id': fields.many2one('gamification.badge', string="For 1st user"),
@ -202,70 +196,53 @@ class gamification_goal_plan(osv.Model):
'reward_failure': False,
}
_sort = 'end_date, start_date, name'
def write(self, cr, uid, ids, vals, context=None):
"""Overwrite the write method to add the user of groups"""
context = context or {}
if not ids:
return True
# unsubscribe removed users from the plan
# users are not able to manually unsubscribe to challenges so should
# do it for them when not concerned anymore
if vals.get('user_ids'):
for action_tuple in vals['user_ids']:
if action_tuple[0] == 3:
# form (3, ID), remove one
self.message_unsubscribe_users(cr, uid, ids, [action_tuple[1]], context=context)
if action_tuple[0] == 5:
# form (5,), remove all
for plan in self.browse(cr, uid, ids, context=context):
self.message_unsubscribe_users(cr, uid, [plan.id], [user.id for user in plan.user_ids], context=context)
if action_tuple[0] == 6:
# form (6, False, [IDS]), replace by IDS
for plan in self.browse(cr, uid, ids, context=context):
removed_users = set([user.id for user in plan.user_ids]) - set(action_tuple[2])
self.message_unsubscribe_users(cr, uid, [plan.id], list(removed_users), context=context)
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:
if vals.get('autojoin_group_id'):
new_group = self.pool.get('res.groups').browse(cr, uid, vals['autojoin_group_id'], context=context)
group_user_ids = [user.id for user in new_group.users]
for plan in self.browse(cr, uid, ids, context=context):
self.write(cr, uid, [plan.id], {'user_ids': [(4, user) for user in group_user_ids]}, context=context)
# subscribe new users to the plan
if 'user_ids' in vals:
for plan in self.browse(cr, uid, ids, context=context):
self.message_subscribe_users(cr, uid, ids, [user.id for user in plan.user_ids], context=context)
if 'user_ids' not in vals:
vals['user_ids'] = []
vals['user_ids'] += [(4, user.id) for user in new_group.users]
write_res = super(gamification_challenge, self).write(cr, uid, ids, vals, context=context)
# subscribe new users to the challenge
if vals.get('user_ids'):
# done with browse after super if changes in groups
for challenge in self.browse(cr, uid, ids, context=context):
self.message_subscribe_users(cr, uid, [challenge.id], [user.id for user in challenge.user_ids], context=context)
return write_res
##### Update #####
def _cron_update(self, cr, uid, context=None, ids=False):
"""Daily cron check.
Start planned plans (in draft and with start_date = today)
Create the goals for planlines not linked to goals (eg: modified the
plan to add planlines)
Update every plan running
- Start planned challenges (in draft and with start_date = today)
- Create the missing goals (eg: modified the challenge to add lines)
- Update every running challenge
"""
if not context: context = {}
# start planned plans
planned_plan_ids = self.search(cr, uid, [
# start planned challenges
planned_challenge_ids = self.search(cr, uid, [
('state', '=', 'draft'),
('start_date', '<=', fields.date.today())])
self.action_start(cr, uid, planned_plan_ids, context=context)
self.action_start(cr, uid, planned_challenge_ids, context=context)
# close planned plans
planned_plan_ids = self.search(cr, uid, [
# close planned challenges
planned_challenge_ids = self.search(cr, uid, [
('state', '=', 'inprogress'),
('end_date', '>=', fields.date.today())])
self.action_close(cr, uid, planned_plan_ids, context=context)
self.action_close(cr, uid, planned_challenge_ids, context=context)
if not ids:
ids = self.search(cr, uid, [('state', '=', 'inprogress')], context=context)
@ -273,17 +250,17 @@ class gamification_goal_plan(osv.Model):
return self._update_all(cr, uid, ids, context=context)
def _update_all(self, cr, uid, ids, context=None):
"""Update the plans and related goals
"""Update the challenges and related goals
:param list(int) ids: the ids of the plans to update, if False will
update only plans in progress."""
:param list(int) ids: the ids of the challenges to update, if False will
update only challenges in progress."""
if not context: context = {}
goal_obj = self.pool.get('gamification.goal')
# we use yesterday to update the goals that just ended
yesterday = date.today() - timedelta(days=1)
goal_ids = goal_obj.search(cr, uid, [
('plan_id', 'in', ids),
('challenge_id', 'in', ids),
'|',
('state', 'in', ('inprogress', 'inprogress_update')),
'&',
@ -292,121 +269,123 @@ class gamification_goal_plan(osv.Model):
('end_date', '>=', yesterday.strftime(DF)),
('end_date', '=', False)
], context=context)
# update every running goal already generated linked to selected plans
# update every running goal already generated linked to selected challenges
goal_obj.update(cr, uid, goal_ids, context=context)
for plan in self.browse(cr, uid, ids, context=context):
if plan.autojoin_group_id:
# check in case of new users in plan, this happens if manager removed users in plan manually
self.write(cr, uid, [plan.id], {'user_ids': [(4, user.id) for user in plan.autojoin_group_id.users]}, context=context)
self.generate_goals_from_plan(cr, uid, [plan.id], context=context)
for challenge in self.browse(cr, uid, ids, context=context):
if challenge.autojoin_group_id:
# check in case of new users in challenge, this happens if manager removed users in challenge manually
self.write(cr, uid, [challenge.id], {'user_ids': [(4, user.id) for user in challenge.autojoin_group_id.users]}, context=context)
self.generate_goals_from_challenge(cr, uid, [challenge.id], context=context)
# goals closed but still opened at the last report date
closed_goals_to_report = goal_obj.search(cr, uid, [
('plan_id', '=', plan.id),
('start_date', '>=', plan.last_report_date),
('end_date', '<=', plan.last_report_date)
('challenge_id', '=', challenge.id),
('start_date', '>=', challenge.last_report_date),
('end_date', '<=', challenge.last_report_date)
])
if len(closed_goals_to_report) > 0:
# some goals need a final report
self.report_progress(cr, uid, plan, subset_goal_ids=closed_goals_to_report, context=context)
self.report_progress(cr, uid, challenge, subset_goal_ids=closed_goals_to_report, context=context)
if fields.date.today() == plan.next_report_date:
self.report_progress(cr, uid, plan, context=context)
if fields.date.today() == challenge.next_report_date:
self.report_progress(cr, uid, challenge, context=context)
self.check_challenge_reward(cr, uid, ids, context=context)
return True
def quick_update(self, cr, uid, plan_id, context=None):
"""Update all the goals of a plan, no generation of new goals"""
def quick_update(self, cr, uid, challenge_id, context=None):
"""Update all the goals of a challenge, no generation of new goals"""
if not context: context = {}
goal_ids = self.pool.get('gamification.goal').search(cr, uid, [('plan_id', '=', plan_id)], context=context)
goal_ids = self.pool.get('gamification.goal').search(cr, uid, [('challenge_id', '=', challenge_id)], context=context)
self.pool.get('gamification.goal').update(cr, uid, goal_ids, context=context)
return True
##### User actions #####
def action_start(self, cr, uid, ids, context=None):
"""Start a draft goal plan
"""Start a draft challenge
Change the state of the plan to in progress and generate related goals
Change the state of the challenge to in progress and generate related goals
"""
# subscribe users if autojoin group
for plan in self.browse(cr, uid, ids, context=context):
if plan.autojoin_group_id:
self.write(cr, uid, [plan.id], {'user_ids': [(4, user.id) for user in plan.autojoin_group_id.users]}, context=context)
for challenge in self.browse(cr, uid, ids, context=context):
if challenge.autojoin_group_id:
self.write(cr, uid, [challenge.id], {'user_ids': [(4, user.id) for user in challenge.autojoin_group_id.users]}, context=context)
self.write(cr, uid, plan.id, {'state': 'inprogress'}, context=context)
self.message_post(cr, uid, plan.id, body="New challenge started.", context=context)
return self.generate_goals_from_plan(cr, uid, ids, context=context)
self.write(cr, uid, challenge.id, {'state': 'inprogress'}, context=context)
self.message_post(cr, uid, challenge.id, body="New challenge started.", context=context)
return self.generate_goals_from_challenge(cr, uid, ids, context=context)
def action_check(self, cr, uid, ids, context=None):
"""Check a goal plan
"""Check a challenge
Create goals that haven't been created yet (eg: if added users of planlines)
Create goals that haven't been created yet (eg: if added users)
Recompute the current value for each goal related"""
return self._update_all(cr, uid, ids=ids, context=context)
def action_close(self, cr, uid, ids, context=None):
"""Close a plan in progress
"""Close a challenge in progress
Change the state of the plan to in done
Change the state of the challenge to in done
Does NOT close the related goals, this is handled by the goal itself"""
self.check_challenge_reward(cr, uid, ids, force=True, context=context)
return self.write(cr, uid, ids, {'state': 'done'}, context=context)
def action_reset(self, cr, uid, ids, context=None):
"""Reset a closed goal plan
"""Reset a closed challenge
Change the state of the plan to in progress
Closing a plan does not affect the goals so neither does reset"""
Change the state of the challenge to in progress
Closing a challenge does not affect the goals so neither does reset"""
return self.write(cr, uid, ids, {'state': 'inprogress'}, context=context)
def action_cancel(self, cr, uid, ids, context=None):
"""Cancel a plan in progress
"""Cancel a challenge in progress
Change the state of the plan to draft
Change the state of the challenge to draft
Cancel the related goals"""
self.write(cr, uid, ids, {'state': 'draft'}, context=context)
goal_ids = self.pool.get('gamification.goal').search(cr, uid, [('plan_id', 'in', ids)], context=context)
goal_ids = self.pool.get('gamification.goal').search(cr, uid, [('challenge_id', 'in', ids)], context=context)
self.pool.get('gamification.goal').write(cr, uid, goal_ids, {'state': 'canceled'}, context=context)
return True
def action_report_progress(self, cr, uid, ids, context=None):
"""Manual report of a goal, does not influence automatic report frequency"""
for plan in self.browse(cr, uid, ids, context):
self.report_progress(cr, uid, plan, context=context)
for challenge in self.browse(cr, uid, ids, context):
self.report_progress(cr, uid, challenge, context=context)
return True
##### Automatic actions #####
def generate_goals_from_plan(self, cr, uid, ids, context=None):
"""Generate the list of goals linked to a plan.
def generate_goals_from_challenge(self, cr, uid, ids, context=None):
"""Generate the goals for each line and user.
If goals already exist for this planline, the planline is skipped. This
can be called after each change in the user or planline list.
:param list(int) ids: the list of plan concerned"""
If goals already exist for this line and user, the line is skipped. This
can be called after each change in the list of users or lines.
:param list(int) ids: the list of challenge concerned"""
for plan in self.browse(cr, uid, ids, context):
(start_date, end_date) = start_end_date_for_period(plan.period)
for challenge in self.browse(cr, uid, ids, context):
(start_date, end_date) = start_end_date_for_period(challenge.period)
# if no periodicity, use plan dates
if not start_date and plan.start_date:
start_date = plan.start_date
if not end_date and plan.end_date:
end_date = plan.end_date
# if no periodicity, use challenge dates
if not start_date and challenge.start_date:
start_date = challenge.start_date
if not end_date and challenge.end_date:
end_date = challenge.end_date
for planline in plan.planline_ids:
for user in plan.user_ids:
for line in challenge.line_ids:
for user in challenge.user_ids:
goal_obj = self.pool.get('gamification.goal')
domain = [('planline_id', '=', planline.id), ('user_id', '=', user.id)]
domain = [('line_id', '=', line.id), ('user_id', '=', user.id)]
if start_date:
domain.append(('start_date', '=', start_date))
# goal already existing for this planline ?
# goal already existing for this line ?
if len(goal_obj.search(cr, uid, domain, context=context)) > 0:
# resume canceled goals
@ -419,10 +398,10 @@ class gamification_goal_plan(osv.Model):
continue
values = {
'type_id': planline.type_id.id,
'planline_id': planline.id,
'definition_id': line.definition_id.id,
'line_id': line.id,
'user_id': user.id,
'target_goal': planline.target_goal,
'target_goal': line.target_goal,
'state': 'inprogress',
}
@ -431,8 +410,8 @@ class gamification_goal_plan(osv.Model):
if end_date:
values['end_date'] = end_date
if planline.plan_id.remind_update_delay:
values['remind_update_delay'] = planline.plan_id.remind_update_delay
if challenge.remind_update_delay:
values['remind_update_delay'] = challenge.remind_update_delay
new_goal_id = goal_obj.create(cr, uid, values, context)
@ -442,17 +421,17 @@ class gamification_goal_plan(osv.Model):
##### JS utilities #####
def get_board_goal_info(self, cr, uid, plan, subset_goal_ids=False, context=None):
"""Get the list of latest goals for a plan, sorted by user ranking for each planline"""
def get_board_goal_info(self, cr, uid, challenge, subset_goal_ids=False, context=None):
"""Get the list of latest goals for a challenge, sorted by user ranking for each line"""
goal_obj = self.pool.get('gamification.goal')
planlines_boards = []
(start_date, end_date) = start_end_date_for_period(plan.period)
lines_boards = []
(start_date, end_date) = start_end_date_for_period(challenge.period)
for planline in plan.planline_ids:
for line in challenge.line_ids:
domain = [
('planline_id', '=', planline.id),
('line_id', '=', line.id),
('state', 'in', ('inprogress', 'inprogress_update',
'reached', 'failed')),
]
@ -471,24 +450,24 @@ class gamification_goal_plan(osv.Model):
board_goals = [goal for goal in goal_obj.browse(cr, uid, common_goal_ids, context=context)]
if len(board_goals) == 0:
# planline has no generated goals
# line has no generated goals
continue
# most complete first, current if same percentage (eg: if several 100%)
sorted_board = enumerate(sorted(board_goals, key=lambda k: (k.completeness, k.current), reverse=True))
planlines_boards.append({'goal_type': planline.type_id, 'board_goals': sorted_board, 'target_goal': planline.target_goal})
return planlines_boards
lines_boards.append({'goal_definition': line.definition_id, 'board_goals': sorted_board, 'target_goal': line.target_goal})
return lines_boards
def get_indivual_goal_info(self, cr, uid, user_id, plan, subset_goal_ids=False, context=None):
"""Get the list of latest goals of a user for a plan"""
def get_indivual_goal_info(self, cr, uid, user_id, challenge, subset_goal_ids=False, context=None):
"""Get the list of latest goals of a user for a challenge"""
domain = [
('plan_id', '=', plan.id),
('challenge_id', '=', challenge.id),
('user_id', '=', user_id),
('state', 'in', ('inprogress', 'inprogress_update',
'reached', 'failed')),
]
goal_obj = self.pool.get('gamification.goal')
(start_date, end_date) = start_end_date_for_period(plan.period)
(start_date, end_date) = start_end_date_for_period(challenge.period)
if subset_goal_ids:
# use the domain for safety, don't want irrelevant report if wrong argument
@ -510,7 +489,7 @@ class gamification_goal_plan(osv.Model):
for goal in goal_obj.browse(cr, uid, related_goal_ids, context=context):
if goal.end_date:
if goal.end_date < fields.date.today():
# do not include goals of previous plan run
# do not include goals of previous challenge run
continue
else:
all_done = False
@ -521,24 +500,24 @@ class gamification_goal_plan(osv.Model):
goals.append(goal)
if all_done:
# skip plans where all goal are done or failed
# skip challenges where all goal are done or failed
return False
else:
return goals
##### Reporting #####
def report_progress(self, cr, uid, plan, context=None, users=False, subset_goal_ids=False):
def report_progress(self, cr, uid, challenge, context=None, users=False, subset_goal_ids=False):
"""Post report about the progress of the goals
:param plan: the plan object that need to be reported
:param challenge: the challenge object that need to be reported
:param users: the list(res.users) of users that are concerned by
the report. If False, will send the report to every user concerned
(goal users and group that receive a copy). Only used for plan with
(goal users and group that receive a copy). Only used for challenge with
a visibility mode set to 'personal'.
:param goal_ids: the list(int) of goal ids linked to the plan for
the report. If not specified, use the goals for the current plan
period. This parameter can be used to produce report for previous plan
:param goal_ids: the list(int) of goal ids linked to the challenge for
the report. If not specified, use the goals for the current challenge
period. This parameter can be used to produce report for previous challenge
periods.
:param subset_goal_ids: a list(int) of goal ids to restrict the report
"""
@ -547,81 +526,81 @@ class gamification_goal_plan(osv.Model):
# 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)
if challenge.visibility_mode == 'board':
lines_boards = self.get_board_goal_info(cr, uid, challenge, subset_goal_ids, context)
ctx.update({'planlines_boards': planlines_boards})
ctx.update({'lines_boards': lines_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 = temp_obj.render_template(cr, uid, template_id.body_html, 'gamification.challenge', challenge.id, context=context)
# body_html = template_env.get_template('group_progress.mako').render({'object': plan, 'planlines_boards': planlines_boards, 'uid': uid})
# body_html = template_env.get_template('group_progress.mako').render({'object': challenge, 'lines_boards': lines_boards, 'uid': uid})
# send to every follower of the plan
self.message_post(cr, uid, plan.id,
# send to every follower of the challenge
self.message_post(cr, uid, challenge.id,
body=body_html,
context=context,
subtype='mail.mt_comment')
if plan.report_message_group_id:
self.pool.get('mail.group').message_post(cr, uid, plan.report_message_group_id.id,
if challenge.report_message_group_id:
self.pool.get('mail.group').message_post(cr, uid, challenge.report_message_group_id.id,
body=body_html,
context=context,
subtype='mail.mt_comment')
else:
# generate individual reports
for user in users or plan.user_ids:
goals = self.get_indivual_goal_info(cr, uid, user.id, plan, subset_goal_ids, context=context)
for user in users or challenge.user_ids:
goals = self.get_indivual_goal_info(cr, uid, user.id, challenge, subset_goal_ids, context=context)
if not goals:
continue
ctx.update({'goals': goals})
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 = temp_obj.render_template(cr, user.id, template_id.body_html, 'gamification.challenge', challenge.id, context=context)
# send message only to users
self.message_post(cr, uid, 0,
body=body_html,
partner_ids=[(4, user.partner_id.id)],
context=context,
subtype='mail.mt_comment')
if plan.report_message_group_id:
self.pool.get('mail.group').message_post(cr, uid, plan.report_message_group_id.id,
if challenge.report_message_group_id:
self.pool.get('mail.group').message_post(cr, uid, challenge.report_message_group_id.id,
body=body_html,
context=context,
subtype='mail.mt_comment')
return self.write(cr, uid, plan.id, {'last_report_date': fields.date.today()}, context=context)
return self.write(cr, uid, challenge.id, {'last_report_date': fields.date.today()}, context=context)
##### Challenges #####
def accept_challenge(self, cr, uid, plan_ids, context=None, user_id=None):
def accept_challenge(self, cr, uid, challenge_ids, context=None, user_id=None):
"""The user accept the suggested challenge"""
context = context or {}
user_id = user_id or uid
user = self.pool.get('res.users').browse(cr, uid, user_id, context=context)
message = "%s has joined the challenge" % user.name
self.message_post(cr, uid, plan_ids, body=message, context=context)
self.write(cr, uid, plan_ids, {'proposed_user_ids': [(3, user_id)], 'user_ids': [(4, user_id)]}, context=context)
return self.generate_goals_from_plan(cr, uid, plan_ids, context=context)
self.message_post(cr, uid, challenge_ids, body=message, context=context)
self.write(cr, uid, challenge_ids, {'invited_user_ids': [(3, user_id)], 'user_ids': [(4, user_id)]}, context=context)
return self.generate_goals_from_challenge(cr, uid, challenge_ids, context=context)
def discard_challenge(self, cr, uid, plan_ids, context=None, user_id=None):
def discard_challenge(self, cr, uid, challenge_ids, context=None, user_id=None):
"""The user discard the suggested challenge"""
context = context or {}
user_id = user_id or uid
user = self.pool.get('res.users').browse(cr, uid, user_id, context=context)
message = "%s has refused the challenge" % user.name
self.message_post(cr, uid, plan_ids, body=message, context=context)
return self.write(cr, uid, plan_ids, {'proposed_user_ids': (3, user_id)}, context=context)
self.message_post(cr, uid, challenge_ids, body=message, context=context)
return self.write(cr, uid, challenge_ids, {'invited_user_ids': (3, user_id)}, context=context)
def reply_challenge_wizard(self, cr, uid, plan_id, context=None):
def reply_challenge_wizard(self, cr, uid, challenge_id, context=None):
context = context or {}
mod_obj = self.pool.get('ir.model.data')
act_obj = self.pool.get('ir.actions.act_window')
result = mod_obj.get_object_reference(cr, uid, 'gamification', 'challenge_wizard')
id = result and result[1] or False
result = act_obj.read(cr, uid, [id], context=context)[0]
result['res_id'] = plan_id
result['res_id'] = challenge_id
return result
def check_challenge_reward(self, cr, uid, plan_ids, force=False, context=None):
def check_challenge_reward(self, cr, uid, challenge_ids, force=False, context=None):
"""Actions for the end of a challenge
If a reward was selected, grant it to the correct users.
@ -632,55 +611,55 @@ class gamification_goal_plan(osv.Model):
(if no end date, a running challenge is never rewarded)
"""
context = context or {}
for plan in self.browse(cr, uid, plan_ids, context=context):
(start_date, end_date) = start_end_date_for_period(plan.period, plan.start_date, plan.end_date)
for challenge in self.browse(cr, uid, challenge_ids, context=context):
(start_date, end_date) = start_end_date_for_period(challenge.period, challenge.start_date, challenge.end_date)
yesterday = date.today() - timedelta(days=1)
if end_date == yesterday.strftime(DF) or force:
# open chatter message
message_body = _("The challenge %s is finished." % plan.name)
message_body = _("The challenge %s is finished." % challenge.name)
# reward for everybody succeeding
rewarded_users = []
if plan.reward_id:
for user in plan.user_ids:
if challenge.reward_id:
for user in challenge.user_ids:
reached_goal_ids = self.pool.get('gamification.goal').search(cr, uid, [
('plan_id', '=', plan.id),
('challenge_id', '=', challenge.id),
('user_id', '=', user.id),
('start_date', '=', start_date),
('end_date', '=', end_date),
('state', '=', 'reached')
], context=context)
if len(reached_goal_ids) == len(plan.planline_ids):
self.reward_user(cr, uid, user.id, plan.reward_id.id, context)
if len(reached_goal_ids) == len(challenge.line_ids):
self.reward_user(cr, uid, user.id, challenge.reward_id.id, context)
rewarded_users.append(user)
if rewarded_users:
message_body += _("<br/>Reward (badge %s) for every succeeding user was sent to %s." % (plan.reward_id.name, ", ".join([user.name for user in rewarded_users])))
message_body += _("<br/>Reward (badge %s) for every succeeding user was sent to %s." % (challenge.reward_id.name, ", ".join([user.name for user in rewarded_users])))
else:
message_body += _("<br/>Nobody has succeeded to reach every goal, no badge is rewared for this challenge.")
# reward bests
if plan.reward_first_id:
(first_user, second_user, third_user) = self.get_top3_users(cr, uid, plan, context)
if challenge.reward_first_id:
(first_user, second_user, third_user) = self.get_top3_users(cr, uid, challenge, context)
if first_user:
self.reward_user(cr, uid, first_user.id, plan.reward_first_id.id, context)
self.reward_user(cr, uid, first_user.id, challenge.reward_first_id.id, context)
message_body += _("<br/>Special rewards were sent to the top competing users. The ranking for this challenge is :")
message_body += "<br/> 1. %s - %s" % (first_user.name, plan.reward_first_id.name)
message_body += "<br/> 1. %s - %s" % (first_user.name, challenge.reward_first_id.name)
else:
message_body += _("Nobody reached the required conditions to receive special badges.")
if second_user and plan.reward_second_id:
self.reward_user(cr, uid, second_user.id, plan.reward_second_id.id, context)
message_body += "<br/> 2. %s - %s" % (second_user.name, plan.reward_second_id.name)
if third_user and plan.reward_third_id:
self.reward_user(cr, uid, third_user.id, plan.reward_second_id.id, context)
message_body += "<br/> 3. %s - %s" % (third_user.name, plan.reward_third_id.name)
if second_user and challenge.reward_second_id:
self.reward_user(cr, uid, second_user.id, challenge.reward_second_id.id, context)
message_body += "<br/> 2. %s - %s" % (second_user.name, challenge.reward_second_id.name)
if third_user and challenge.reward_third_id:
self.reward_user(cr, uid, third_user.id, challenge.reward_second_id.id, context)
message_body += "<br/> 3. %s - %s" % (third_user.name, challenge.reward_third_id.name)
self.message_post(cr, uid, plan.id, body=message_body, context=context)
self.message_post(cr, uid, challenge.id, body=message_body, context=context)
return True
def get_top3_users(self, cr, uid, plan, context=None):
"""Get the top 3 users for a defined plan
def get_top3_users(self, cr, uid, challenge, context=None):
"""Get the top 3 users for a defined challenge
Ranking criterias:
1. succeed every goal of the challenge
@ -694,14 +673,14 @@ class gamification_goal_plan(osv.Model):
higher one (eg: if 'second' == False, 'third' will be False)
"""
goal_obj = self.pool.get('gamification.goal')
(start_date, end_date) = start_end_date_for_period(plan.period, plan.start_date, plan.end_date)
(start_date, end_date) = start_end_date_for_period(challenge.period, challenge.start_date, challenge.end_date)
challengers = []
for user in plan.user_ids:
for user in challenge.user_ids:
all_reached = True
total_completness = 0
# every goal of the user for the running period
goal_ids = goal_obj.search(cr, uid, [
('plan_id', '=', plan.id),
('challenge_id', '=', challenge.id),
('user_id', '=', user.id),
('start_date', '=', start_date),
('end_date', '=', end_date)
@ -709,7 +688,7 @@ class gamification_goal_plan(osv.Model):
for goal in goal_obj.browse(cr, uid, goal_ids, context=context):
if goal.state != 'reached':
all_reached = False
if goal.type_condition == 'higher':
if goal.definition_condition == 'higher':
# can be over 100
total_completness += 100.0 * goal.current / goal.target_goal
elif goal.state == 'reached':
@ -719,13 +698,13 @@ class gamification_goal_plan(osv.Model):
challengers.append({'user': user, 'all_reached': all_reached, 'total_completness': total_completness})
sorted_challengers = sorted(challengers, key=lambda k: (k['all_reached'], k['total_completness']), reverse=True)
if len(sorted_challengers) == 0 or (not plan.reward_failure and not sorted_challengers[0]['all_reached']):
if len(sorted_challengers) == 0 or (not challenge.reward_failure and not sorted_challengers[0]['all_reached']):
# nobody succeeded
return (False, False, False)
if len(sorted_challengers) == 1 or (not plan.reward_failure and not sorted_challengers[1]['all_reached']):
if len(sorted_challengers) == 1 or (not challenge.reward_failure and not sorted_challengers[1]['all_reached']):
# only one user succeeded
return (sorted_challengers[0]['user'], False, False)
if len(sorted_challengers) == 2 or (not plan.reward_failure and not sorted_challengers[2]['all_reached']):
if len(sorted_challengers) == 2 or (not challenge.reward_failure and not sorted_challengers[2]['all_reached']):
# only one user succeeded
return (sorted_challengers[0]['user'], sorted_challengers[1]['user'], False)
return (sorted_challengers[0]['user'], sorted_challengers[1]['user'], sorted_challengers[2]['user'])
@ -736,66 +715,50 @@ class gamification_goal_plan(osv.Model):
return self.pool.get('gamification.badge').send_badge(cr, uid, badge_id, [user_badge_id], user_from=None, context=context)
class gamification_goal_planline(osv.Model):
"""Gamification goal planline
class gamification_challenge_line(osv.Model):
"""Gamification challenge line
Predifined goal for 'gamification_goal_plan'
Predifined goal for 'gamification_challenge'
These are generic list of goals with only the target goal defined
Should only be created for the gamification_goal_plan object
Should only be created for the gamification_challenge object
"""
_name = 'gamification.goal.planline'
_description = 'Gamification generic goal for plan'
_order = "sequence, sequence_type, id"
_name = 'gamification.challenge.line'
_description = 'Gamification generic goal for challenge'
_order = "sequence, id"
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()
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': {
'type_condition': goal_type.condition,
'type_full_suffix': goal_type.full_suffix}}
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)
ret = {
'value': {
'definition_condition': goal_definition.condition,
'definition_full_suffix': goal_definition.full_suffix
}
}
return ret
_columns = {
'name': fields.related('type_id', 'name', string="Name"),
'plan_id': fields.many2one('gamification.goal.plan',
string='Plan',
'name': fields.related('definition_id', 'name', string="Name"),
'challenge_id': fields.many2one('gamification.challenge',
string='Challenge',
required=True,
ondelete="cascade"),
'type_id': fields.many2one('gamification.goal.type',
string='Goal Type',
'definition_id': fields.many2one('gamification.goal.definition',
string='Goal Definition',
required=True,
ondelete="cascade"),
'target_goal': fields.float('Target Value to Reach',
required=True),
'sequence': fields.integer('Sequence',
help='Sequence number for ordering'),
'sequence_type': fields.related('type_id', 'sequence',
type='integer',
string='Sequence',
readonly=True,
store={
'gamification.goal.type': (_get_planline_types, ['sequence'], 10),
}),
'type_condition': fields.related('type_id', 'condition', type="selection",
'definition_condition': fields.related('definition_id', 'condition', type="selection",
readonly=True, string="Condition", selection=[('lower', '<='), ('higher', '>=')]),
'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"),
'definition_suffix': fields.related('definition_id', 'suffix', type="char", readonly=True, string="Unit"),
'definition_monetary': fields.related('definition_id', 'monetary', type="boolean", readonly=True, string="Monetary"),
'definition_full_suffix': fields.related('definition_id', 'full_suffix', type="char", readonly=True, string="Suffix"),
}
_default = {

View File

@ -32,16 +32,15 @@ import logging
_logger = logging.getLogger(__name__)
class gamification_goal_type(osv.Model):
"""Goal type definition
class gamification_goal_definition(osv.Model):
"""Goal definition
A goal type defining a way to set an objective and evaluate it
A goal definition contains the way to evaluate an objective
Each module wanting to be able to set goals to the users needs to create
a new gamification_goal_type
a new gamification_goal_definition
"""
_name = 'gamification.goal.type'
_description = 'Gamification goal type'
_order = 'sequence'
_name = 'gamification.goal.definition'
_description = 'Gamification goal definition'
def _get_suffix(self, cr, uid, ids, field_name, arg, context=None):
res = dict.fromkeys(ids, '')
@ -60,7 +59,7 @@ class gamification_goal_type(osv.Model):
return res
_columns = {
'name': fields.char('Goal Type', required=True, translate=True),
'name': fields.char('Goal Definition', 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."),
'suffix': fields.char('Suffix', help="The unit of the target and current values", translate=True),
@ -92,7 +91,7 @@ class gamification_goal_type(osv.Model):
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."),
help="The name of the python method that will be executed to compute the current value. See the file gamification/goal_definition_data.py for examples."),
'condition': fields.selection([
('higher', 'The higher the better'),
('lower', 'The lower the better')
@ -100,7 +99,6 @@ 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",
help="The action that will be called to update the goal value."),
'res_id_field': fields.char("ID Field of user",
@ -108,7 +106,6 @@ class gamification_goal_type(osv.Model):
}
_defaults = {
'sequence': 1,
'condition': 'higher',
'computation_mode': 'manually',
'domain': "[]",
@ -130,7 +127,7 @@ class gamification_goal(osv.Model):
"""Return the percentage of completeness of the goal, between 0 and 100"""
res = dict.fromkeys(ids, 0.0)
for goal in self.browse(cr, uid, ids, context=context):
if goal.type_condition == 'higher':
if goal.definition_condition == 'higher':
if goal.current >= goal.target_goal:
res[goal.id] = 100.0
else:
@ -142,21 +139,21 @@ class gamification_goal(osv.Model):
res[goal.id] = 0.0
return res
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)
return {'value': {'computation_mode': goal_type.computation_mode, 'type_condition': goal_type.condition}}
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}}
_columns = {
'type_id': fields.many2one('gamification.goal.type', string='Goal Type', required=True, ondelete="cascade"),
'definition_id': fields.many2one('gamification.goal.definition', string='Goal Definition', required=True, ondelete="cascade"),
'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',
string="Plan",
'line_id': fields.many2one('gamification.challenge.line', string='Goal Line', ondelete="cascade"),
'challenge_id': fields.related('line_id', 'challenge_id',
string="Challenge",
type='many2one',
relation='gamification.goal.plan',
relation='gamification.challenge',
store=True),
'start_date': fields.date('Start Date'),
'end_date': fields.date('End Date'), # no start and end = always active
@ -177,16 +174,16 @@ 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('definition_id', 'computation_mode', type='char', string="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."),
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."),
'type_description': fields.related('type_id', 'description', 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),
'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),
}
_defaults = {
@ -194,7 +191,7 @@ class gamification_goal(osv.Model):
'state': 'draft',
'start_date': fields.date.today,
}
_order = 'create_date desc, end_date desc, type_id, id'
_order = 'create_date desc, end_date desc, definition_id, id'
def _check_remind_delay(self, cr, uid, goal, context=None):
"""Verify if a goal has not been updated for some time and send a
@ -230,26 +227,26 @@ class gamification_goal(osv.Model):
# skip if goal draft or canceled
continue
if goal.type_id.computation_mode == 'manually':
if goal.definition_id.computation_mode == 'manually':
towrite.update(self._check_remind_delay(cr, uid, goal, context))
elif goal.type_id.computation_mode == 'python':
elif goal.definition_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, {})
values = {'cr': cr, 'uid': goal.user_id.id, 'context': context, 'self': self.pool.get('gamification.goal.definition')}
result = safe_eval(goal.definition_id.compute_code, values, {})
if type(result) in (float, int, long) and result != goal.current:
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)))
_logger.exception(_('Unvalid return content from the evaluation of %s' % str(goal.definition_id.compute_code)))
# raise osv.except_osv(_('Error!'), _('Unvalid return content from the evaluation of %s' % str(goal.definition_id.compute_code)))
else: # count or sum
obj = self.pool.get(goal.type_id.model_id.model)
field_date_name = goal.type_id.field_date_id.name
obj = self.pool.get(goal.definition_id.model_id.model)
field_date_name = goal.definition_id.field_date_id.name
# eval the domain with user replaced by goal user object
domain = safe_eval(goal.type_id.domain, {'user': goal.user_id})
domain = safe_eval(goal.definition_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:
@ -257,8 +254,8 @@ class gamification_goal(osv.Model):
if goal.end_date and field_date_name:
domain.append((field_date_name, '<=', goal.end_date))
if goal.type_id.computation_mode == 'sum':
field_name = goal.type_id.field_id.name
if goal.definition_id.computation_mode == 'sum':
field_name = goal.definition_id.field_id.name
res = obj.read_group(cr, uid, domain, [field_name], [''], context=context)
new_value = res and res[0][field_name] or 0.0
@ -270,7 +267,7 @@ class gamification_goal(osv.Model):
towrite['current'] = new_value
# check goal target reached
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):
if (goal.definition_id.condition == 'higher' and towrite.get('current', goal.current) >= goal.target_goal) or (goal.definition_id.condition == 'lower' and towrite.get('current', goal.current) <= goal.target_goal):
towrite['state'] = 'reached'
# check goal failure
@ -323,7 +320,7 @@ class gamification_goal(osv.Model):
vals['last_update'] = fields.date.today()
result = super(gamification_goal, self).write(cr, uid, ids, vals, context=context)
for goal in self.browse(cr, uid, ids, context=context):
if goal.state != "draft" and ('type_id' in vals or 'user_id' in vals):
if goal.state != "draft" and ('definition_id' in vals or 'user_id' in vals):
# avoid drag&drop in kanban view
raise osv.except_osv(_('Error!'), _('Can not modify the configuration of a started goal'))
@ -332,8 +329,8 @@ class gamification_goal(osv.Model):
# new goals should not be reported
continue
if goal.plan_id and goal.plan_id.report_message_frequency == 'onchange':
self.pool.get('gamification.goal.plan').report_progress(cr, SUPERUSER_ID, goal.plan_id, users=[goal.user_id], context=context)
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)
return result
def get_action(self, cr, uid, goal_id, context=None):
@ -344,13 +341,13 @@ class gamification_goal(osv.Model):
"""
goal = self.browse(cr, uid, goal_id, context=context)
if goal.type_id.action_id:
if goal.definition_id.action_id:
#open a the action linked on the goal
action = goal.type_id.action_id.read()[0]
action = goal.definition_id.action_id.read()[0]
if goal.type_id.res_id_field:
if goal.definition_id.res_id_field:
current_user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
action['res_id'] = safe_eval(goal.type_id.res_id_field, {'user': current_user})
action['res_id'] = safe_eval(goal.definition_id.res_id_field, {'user': current_user})
# if one element to display, should see it in form mode if possible
views = action['views']
@ -364,7 +361,7 @@ class gamification_goal(osv.Model):
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.definition_id.name,
'id': goal_id,
'type': 'ir.actions.act_window',
'views': [[False, 'form']],
@ -378,7 +375,7 @@ class gamification_goal(osv.Model):
class goal_manual_wizard(osv.TransientModel):
"""Wizard type to update a manual goal"""
"""Wizard to update a manual goal"""
_name = 'gamification.goal.wizard'
_columns = {
'goal_id': fields.many2one("gamification.goal", string='Goal', required=True),

View File

@ -24,7 +24,7 @@ from openerp.osv import osv
class res_users_gamification_group(osv.Model):
""" Update of res.users class
- if adding groups to an user, check gamification.goal.plan linked to
- if adding groups to an user, check gamification.challenge linked to
this group, and the user. This is done by overriding the write method.
"""
_name = 'res.users'
@ -37,62 +37,62 @@ class res_users_gamification_group(osv.Model):
user_group_ids = [command[1] for command in vals['groups_id'] if command[0] == 4]
user_group_ids += [id for command in vals['groups_id'] if command[0] == 6 for id in command[2]]
goal_plan_obj = self.pool.get('gamification.goal.plan')
plan_ids = goal_plan_obj.search(cr, uid, [('autojoin_group_id', 'in', user_group_ids)], context=context)
if plan_ids:
goal_plan_obj.write(cr, uid, plan_ids, {'user_ids': [(4, user_id) for user_id in ids]}, context=context)
challenge_obj = self.pool.get('gamification.challenge')
challenge_ids = challenge_obj.search(cr, uid, [('autojoin_group_id', 'in', user_group_ids)], context=context)
if challenge_ids:
challenge_obj.write(cr, uid, challenge_ids, {'user_ids': [(4, user_id) for user_id in ids]}, context=context)
if vals.get('image'):
goal_type_id = self.pool.get('ir.model.data').get_object(cr, uid, 'gamification', 'type_base_avatar', context)
goal_ids = self.pool.get('gamification.goal').search(cr, uid, [('type_id', '=', goal_type_id.id), ('user_id', 'in', ids)], context=context)
goal_definition_id = self.pool.get('ir.model.data').get_object(cr, uid, 'gamification', 'definition_base_avatar', context)
goal_ids = self.pool.get('gamification.goal').search(cr, uid, [('definition_id', '=', goal_definition_id.id), ('user_id', 'in', ids)], context=context)
values = {'state': 'reached', 'current': 1}
self.pool.get('gamification.goal').write(cr, uid, goal_ids, values, context=context)
return write_res
def get_goals_todo_info(self, cr, uid, context=None):
"""Return the list of goals assigned to the user, grouped by plan
"""Return the list of goals assigned to the user, grouped by challenge
This method intends to return processable data in javascript in the
goal_list_to_do template. The output format is not constant as the
required information is different between individual and board goal
types
definitions
:return: list of dictionnaries for each goal to display
"""
all_goals_info = []
plan_obj = self.pool.get('gamification.goal.plan')
challenge_obj = self.pool.get('gamification.challenge')
plan_ids = plan_obj.search(cr, uid, [('user_ids', 'in', uid), ('state', '=', 'inprogress')], context=context)
for plan in plan_obj.browse(cr, uid, plan_ids, context=context):
challenge_ids = challenge_obj.search(cr, uid, [('user_ids', 'in', uid), ('state', '=', 'inprogress')], context=context)
for challenge in challenge_obj.browse(cr, uid, challenge_ids, context=context):
# serialize goals info to be able to use it in javascript
serialized_goals_info = {
'id': plan.id,
'name': plan.name,
'visibility_mode': plan.visibility_mode,
'id': challenge.id,
'name': challenge.name,
'visibility_mode': challenge.visibility_mode,
}
user = self.browse(cr, uid, uid, context=context)
serialized_goals_info['currency'] = user.company_id.currency_id.id
if plan.visibility_mode == 'board':
# board report should be grouped by planline for all users
goals_info = plan_obj.get_board_goal_info(cr, uid, plan, subset_goal_ids=False, context=context)
if challenge.visibility_mode == 'board':
# board report should be grouped by line for all users
goals_info = challenge_obj.get_board_goal_info(cr, uid, challenge, subset_goal_ids=False, context=context)
if len(goals_info) == 0:
# plan with no valid planlines
# challenge with no valid lines
continue
serialized_goals_info['planlines'] = []
for planline_board in goals_info:
vals = {'type_name': planline_board['goal_type'].name,
'type_description': planline_board['goal_type'].description,
'type_condition': planline_board['goal_type'].condition,
'computation_mode': planline_board['goal_type'].computation_mode,
'type_monetary': planline_board['goal_type'].monetary,
'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'],
serialized_goals_info['lines'] = []
for line_board in goals_info:
vals = {'definition_name': line_board['goal_definition'].name,
'definition_description': line_board['goal_definition'].description,
'definition_condition': line_board['goal_definition'].condition,
'computation_mode': line_board['goal_definition'].computation_mode,
'definition_monetary': line_board['goal_definition'].monetary,
'definition_suffix': line_board['goal_definition'].suffix,
'definition_action': True if line_board['goal_definition'].action_id else False,
'definition_display': line_board['goal_definition'].display_mode,
'target_goal': line_board['target_goal'],
'goals': []}
for goal in planline_board['board_goals']:
for goal in line_board['board_goals']:
# Keep only the Top 3 and the current user
if goal[0] > 2 and goal[1].user_id.id != uid:
continue
@ -109,11 +109,11 @@ class res_users_gamification_group(osv.Model):
})
if uid == goal[1].user_id.id:
vals['own_goal_id'] = goal[1].id
serialized_goals_info['planlines'].append(vals)
serialized_goals_info['lines'].append(vals)
else:
# individual report are simply a list of goal
goals_info = plan_obj.get_indivual_goal_info(cr, uid, uid, plan, subset_goal_ids=False, context=context)
goals_info = challenge_obj.get_indivual_goal_info(cr, uid, uid, challenge, subset_goal_ids=False, context=context)
if not goals_info:
continue
@ -122,13 +122,13 @@ class res_users_gamification_group(osv.Model):
for goal in goals_info:
serialized_goals_info['goals'].append({
'id': goal.id,
'type_name': goal.type_id.name,
'type_description': goal.type_description,
'type_condition': goal.type_id.condition,
'type_monetary': goal.type_id.monetary,
'type_suffix': goal.type_id.suffix,
'type_action': True if goal.type_id.action_id else False,
'type_display': goal.type_id.display_mode,
'definition_name': goal.definition_id.name,
'definition_description': goal.definition_description,
'definition_condition': goal.definition_id.condition,
'definition_monetary': goal.definition_id.monetary,
'definition_suffix': goal.definition_id.suffix,
'definition_action': True if goal.definition_id.action_id else False,
'definition_display': goal.definition_id.display_mode,
'state': goal.state,
'completeness': goal.completeness,
'computation_mode': goal.computation_mode,
@ -140,23 +140,23 @@ class res_users_gamification_group(osv.Model):
return all_goals_info
def get_challenge_suggestions(self, cr, uid, context=None):
"""Return the list of goal plans suggested to the user"""
plan_info = []
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):
"""Return the list of challenges suggested to the user"""
challenge_info = []
challenge_obj = self.pool.get('gamification.challenge')
challenge_ids = challenge_obj.search(cr, uid, [('invited_user_ids', 'in', uid), ('state', '=', 'inprogress')], context=context)
for challenge in challenge_obj.browse(cr, uid, challenge_ids, context=context):
values = {
'id': plan.id,
'name': plan.name,
'description': plan.description,
'id': challenge.id,
'name': challenge.name,
'description': challenge.description,
}
plan_info.append(values)
return plan_info
challenge_info.append(values)
return challenge_info
class res_groups_gamification_group(osv.Model):
""" Update of res.groups class
- if adding users from a group, check gamification.goal.plan linked to
- if adding users from a group, check gamification.challenge linked to
this group, and the user. This is done by overriding the write method.
"""
_name = 'res.groups'
@ -169,9 +169,9 @@ class res_groups_gamification_group(osv.Model):
user_ids = [command[1] for command in vals['users'] if command[0] == 4]
user_ids += [id for command in vals['users'] if command[0] == 6 for id in command[2]]
goal_plan_obj = self.pool.get('gamification.goal.plan')
plan_ids = goal_plan_obj.search(cr, uid, [('autojoin_group_id', 'in', ids)], context=context)
if plan_ids:
goal_plan_obj.write(cr, uid, plan_ids, {'user_ids': [(4, user_id) for user_id in user_ids]}, context=context)
challenge_obj = self.pool.get('gamification.challenge')
challenge_ids = challenge_obj.search(cr, uid, [('autojoin_group_id', 'in', ids)], context=context)
if challenge_ids:
challenge_obj.write(cr, uid, challenge_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:

View File

@ -13,7 +13,7 @@
</record>
<record id="goal_user_visibility" model="ir.rule">
<field name="name">User can only see his/her goals or goal from the same plan in board visibility</field>
<field name="name">User can only see his/her goals or goal from the same challenge in board visibility</field>
<field name="model_id" ref="model_gamification_goal"/>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
<field name="perm_read" eval="True"/>
@ -24,8 +24,8 @@
'|',
('user_id','=',user.id),
'&amp;',
('plan_id.user_ids','in',user.id),
('plan_id.visibility_mode','=','board')]</field>
('challenge_id.user_ids','in',user.id),
('challenge_id.visibility_mode','=','board')]</field>
</record>
<record id="goal_gamification_manager_visibility" model="ir.rule">

View File

@ -3,14 +3,14 @@ id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink
goal_employee,"Goal Employee",model_gamification_goal,base.group_user,1,1,0,0
goal_manager,"Goal Manager",model_gamification_goal,group_goal_manager,1,1,1,1
goal_type_employee,"Goal Type Employee",model_gamification_goal_type,base.group_user,1,0,0,0
goal_type_manager,"Goal Type Manager",model_gamification_goal_type,group_goal_manager,1,1,1,1
goal_definition_employee,"Goal Definition Employee",model_gamification_goal_definition,base.group_user,1,0,0,0
goal_definition_manager,"Goal Definition Manager",model_gamification_goal_definition,group_goal_manager,1,1,1,1
plan_employee,"Goal Plan Employee",model_gamification_goal_plan,base.group_user,1,0,0,0
plan_manager,"Goal Plan Manager",model_gamification_goal_plan,group_goal_manager,1,1,1,1
challenge_employee,"Goal Challenge Employee",model_gamification_challenge,base.group_user,1,0,0,0
challenge_manager,"Goal Challenge Manager",model_gamification_challenge,group_goal_manager,1,1,1,1
planline_employee,"Goal Planline Employee",model_gamification_goal_planline,base.group_user,1,0,0,0
planline_manager,"Goal Planline Manager",model_gamification_goal_planline,group_goal_manager,1,1,1,1
challenge_line_employee,"Challenge Line Employee",model_gamification_challenge_line,base.group_user,1,0,0,0
challenge_line_manager,"Challenge Line Manager",model_gamification_challenge_line,group_goal_manager,1,1,1,1
badge_employee,"Badge Employee",model_gamification_badge,base.group_user,1,0,0,0
badge_manager,"Badge Manager",model_gamification_badge,group_goal_manager,1,1,1,1

1 id name model_id/id group_id/id perm_read perm_write perm_create perm_unlink
3 goal_manager Goal Manager model_gamification_goal group_goal_manager 1 1 1 1
4 goal_type_employee goal_definition_employee Goal Type Employee Goal Definition Employee model_gamification_goal_type model_gamification_goal_definition base.group_user 1 0 0 0
5 goal_type_manager goal_definition_manager Goal Type Manager Goal Definition Manager model_gamification_goal_type model_gamification_goal_definition group_goal_manager 1 1 1 1
6 plan_employee challenge_employee Goal Plan Employee Goal Challenge Employee model_gamification_goal_plan model_gamification_challenge base.group_user 1 0 0 0
7 plan_manager challenge_manager Goal Plan Manager Goal Challenge Manager model_gamification_goal_plan model_gamification_challenge group_goal_manager 1 1 1 1
8 planline_employee challenge_line_employee Goal Planline Employee Challenge Line Employee model_gamification_goal_planline model_gamification_challenge_line base.group_user 1 0 0 0
9 planline_manager challenge_line_manager Goal Planline Manager Challenge Line Manager model_gamification_goal_planline model_gamification_challenge_line group_goal_manager 1 1 1 1
10 badge_employee Badge Employee model_gamification_badge base.group_user 1 0 0 0
11 badge_manager Badge Manager model_gamification_badge group_goal_manager 1 1 1 1
12 badge_user_employee Badge-user Employee model_gamification_badge_user base.group_user 1 1 1 0
13 badge_user_manager Badge-user Manager model_gamification_badge_user group_goal_manager 1 1 1 1
14
15
16

View File

@ -12,11 +12,11 @@ openerp.gamification = function(instance) {
this.challenge_suggestions = {};
},
events: {
// update a plan and related goals
'click a.oe_update_plan': function(event) {
// update a challenge and related goals
'click a.oe_update_challenge': function(event) {
var self = this;
var plan_id = parseInt(event.currentTarget.id, 10);
var goals_updated = new instance.web.Model('gamification.goal.plan').call('quick_update', [plan_id]);
var challenge_id = parseInt(event.currentTarget.id, 10);
var goals_updated = new instance.web.Model('gamification.challenge').call('quick_update', [challenge_id]);
$.when(goals_updated).done(function() {
self.get_goal_todo_info();
});
@ -40,12 +40,12 @@ openerp.gamification = function(instance) {
// get more info about a challenge request
'click a.oe_challenge_reply': function(event) {
var self = this;
var plan_id = parseInt(event.currentTarget.id, 10);
var plan_action = new instance.web.Model('gamification.goal.plan').call('reply_challenge_wizard', [plan_id]).then(function(res) {
plan_action['action'] = res;
var challenge_id = parseInt(event.currentTarget.id, 10);
var challenge_action = new instance.web.Model('gamification.challenge').call('reply_challenge_wizard', [challenge_id]).then(function(res) {
challenge_action['action'] = res;
});
$.when(plan_action).done(function() {
self.do_action(plan_action.action).done(function () {
$.when(challenge_action).done(function() {
self.do_action(challenge_action.action).done(function () {
self.get_goal_todo_info();
});
});
@ -132,7 +132,7 @@ openerp.gamification = function(instance) {
instance.web_kanban.KanbanRecord.include({
// open related goals when clicking on challenge kanban view
on_card_clicked: function() {
if (this.view.dataset.model === 'gamification.goal.plan') {
if (this.view.dataset.model === 'gamification.challenge') {
this.$('.oe_kanban_project_list a').first().click();
} else {
this._super.apply(this, arguments);

View File

@ -4,37 +4,37 @@
<div class="oe_gamification_goal"><em>Your goal list is empty</em></div>
</t>
<t t-name="gamification.goal_list_to_do">
<div t-foreach="widget.goals_info.info" t-as="plan" class="oe_goal">
<div t-foreach="widget.goals_info.info" t-as="challenge" class="oe_goal">
<div>
<a class="oe_update_plan oe_e" rol="button" t-attf-id="{plan.id}">e</a>
<h4><t t-esc="plan.name" /></h4>
<a class="oe_update_challenge oe_e" rol="button" t-attf-id="{challenge.id}">e</a>
<h4><t t-esc="challenge.name" /></h4>
</div>
<t t-if="plan.visibility_mode == 'progressbar'">
<t t-if="challenge.visibility_mode == 'progressbar'">
<div class="oe_table oe_goals_list">
<div t-foreach="plan.goals" t-as="goal" t-attf-class="oe_row oe_goal_outer_box #{goal.state == 'reached' ? 'oe_goal_reached' : ''} #{goal.type_display != 'progress' ? 'oe_no_progress' : ''}">
<t t-if="goal.type_display == 'progress'">
<div t-foreach="challenge.goals" t-as="goal" t-attf-class="oe_row oe_goal_outer_box #{goal.state == 'reached' ? 'oe_goal_reached' : ''} #{goal.definition_display != 'progress' ? 'oe_no_progress' : ''}">
<t t-if="goal.definition_display == 'progress'">
<div class="oe_goal_progress_background"></div>
<div class="oe_goal_progress" t-attf-style="#{goal.type_display == 'progress' ? 'width: '+goal.completeness+'%;' : 'width:0;'}"></div>
<div class="oe_goal_progress" t-attf-style="#{goal.definition_display == 'progress' ? 'width: '+goal.completeness+'%;' : 'width:0;'}"></div>
<div class="oe_cell oe_goal_current"><t t-esc="goal.current" /></div>
</t>
<div class="oe_cell">
<t t-if="goal.computation_mode != 'manually' and !goal.type_action">
<span t-att-title="goal.type_description"><t t-esc="goal.type_name" /></span>
<t t-if="goal.computation_mode != 'manually' and !goal.definition_action">
<span t-att-title="goal.definition_description"><t t-esc="goal.definition_name" /></span>
</t>
<t t-if="goal.type_action or goal.computation_mode == 'manually'">
<span t-att-title="goal.type_description"><a class="oe_goal_action" t-att-id="goal.id"><t t-esc="goal.type_name" /></a></span>
<t t-if="goal.definition_action or goal.computation_mode == 'manually'">
<span t-att-title="goal.definition_description"><a class="oe_goal_action" t-att-id="goal.id"><t t-esc="goal.definition_name" /></a></span>
</t>
<t t-if="goal.type_display == 'progress'"><br/>
<t t-if="goal.definition_display == 'progress'"><br/>
<div class="oe_grey">
<t t-if="goal.type_condition == 'higher'">
<t t-if="goal.definition_condition == 'higher'">
Target:
</t>
<t t-if="goal.type_condition == 'lower'">
<t t-if="goal.definition_condition == 'lower'">
Target: &lt;=
</t>
<span t-attf-class="#{goal.type_monetary ? 'oe_goal_field_monetary' : ''}"><t t-esc="goal.target_goal"/></span>
<t t-if="goal.type_suffix"><t t-esc="goal.type_suffix"/></t>
<span t-attf-class="#{goal.definition_monetary ? 'oe_goal_field_monetary' : ''}"><t t-esc="goal.target_goal"/></span>
<t t-if="goal.definition_suffix"><t t-esc="goal.definition_suffix"/></t>
</div>
</t>
</div>
@ -42,52 +42,52 @@
</div>
</t>
<t t-if="plan.visibility_mode == 'board'">
<div t-foreach="plan.planlines" t-as="planline" class="oe_goals_list oe_table">
<t t-if="challenge.visibility_mode == 'board'">
<div t-foreach="challenge.lines" t-as="line" class="oe_goals_list oe_table">
<div class="oe_row">
<div class="oe_cell oe_thead" colspan="3" t-attf-title="#{planline.type_description ? planline.type_description : ''}">
<strong><t t-esc="planline.type_name"/></strong>
<div class="oe_cell oe_thead" colspan="3" t-attf-title="#{line.definition_description ? line.definition_description : ''}">
<strong><t t-esc="line.definition_name"/></strong>
<br/>
<div class="oe_grey">
<t t-if="planline.type_condition == 'higher'">
<t t-if="line.definition_condition == 'higher'">
Target:
</t>
<t t-if="planline.type_condition == 'lower'">
<t t-if="line.definition_condition == 'lower'">
Target: &lt;=
</t>
<span t-attf-class="#{planline.type_monetary ? 'oe_goal_field_monetary' : ''}"><t t-esc="planline.target_goal" /></span><t t-if="planline.type_suffix"> <t t-esc="planline.type_suffix"/></t>
<span t-attf-class="#{line.definition_monetary ? 'oe_goal_field_monetary' : ''}"><t t-esc="line.target_goal" /></span><t t-if="line.definition_suffix"> <t t-esc="line.definition_suffix"/></t>
</div>
</div>
</div>
<div t-foreach="planline.goals" t-as="goal" t-attf-class="#{goal.id == planline.own_goal_id ? 'oe_bold' : ''}">
<div t-attf-class="oe_row oe_goal_outer_box #{goal.state == 'reached' ? 'oe_goal_reached' : ''} #{planline.type_display != 'progress' ? 'oe_no_progress' : ''}">
<t t-if="planline.type_display == 'progress'">
<div t-foreach="line.goals" t-as="goal" t-attf-class="#{goal.id == line.own_goal_id ? 'oe_bold' : ''}">
<div t-attf-class="oe_row oe_goal_outer_box #{goal.state == 'reached' ? 'oe_goal_reached' : ''} #{line.definition_display != 'progress' ? 'oe_no_progress' : ''}">
<t t-if="line.definition_display == 'progress'">
<div class="oe_goal_progress_background"></div>
<div class="oe_goal_progress" t-attf-style="#{planline.type_display == 'progress' ? 'width: '+goal.completeness+'%;' : 'width:0;'}"></div>
<div class="oe_goal_progress" t-attf-style="#{line.definition_display == 'progress' ? 'width: '+goal.completeness+'%;' : 'width:0;'}"></div>
</t>
<div class="oe_cell col0"><t t-esc="goal.rank" /></div>
<div class="oe_cell col1"><img class="oe_user_avatar" t-attf-alt="#{goal.user_name}" t-attf-data-id="#{goal.user_id}"/></div>
<div class="oe_cell col2">
<t t-if="planline.type_display == 'progress'">
<t t-if="line.definition_display == 'progress'">
<!-- progress, action on current value -->
<t t-esc="goal.user_name"/><br/>
<t t-if="goal.id != planline.own_goal_id or (planline.computation_mode != 'manually' and !planline.type_action)">
<span t-attf-class="#{planline.type_monetary ? 'oe_goal_field_monetary' : ''}"><t t-esc="goal.current" /></span><t t-if="planline.type_suffix"> <t t-esc="planline.type_suffix"/></t>
<t t-if="goal.id != line.own_goal_id or (line.computation_mode != 'manually' and !line.definition_action)">
<span t-attf-class="#{line.definition_monetary ? 'oe_goal_field_monetary' : ''}"><t t-esc="goal.current" /></span><t t-if="line.definition_suffix"> <t t-esc="line.definition_suffix"/></t>
</t>
<t t-if="goal.id == planline.own_goal_id and (planline.type_action or planline.computation_mode == 'manually')">
<a class="oe_goal_action" t-att-id="planline.own_goal_id">
<span t-attf-class="#{planline.type_monetary ? 'oe_goal_field_monetary' : ''}"><t t-esc="goal.current" /></span><t t-if="planline.type_suffix"> <t t-esc="planline.type_suffix"/></t>
<t t-if="goal.id == line.own_goal_id and (line.definition_action or line.computation_mode == 'manually')">
<a class="oe_goal_action" t-att-id="line.own_goal_id">
<span t-attf-class="#{line.definition_monetary ? 'oe_goal_field_monetary' : ''}"><t t-esc="goal.current" /></span><t t-if="line.definition_suffix"> <t t-esc="line.definition_suffix"/></t>
</a>
</t>
</t>
<t t-if="planline.type_display != 'progress'">
<t t-if="line.definition_display != 'progress'">
<!-- not progress, action on user name -->
<t t-if="goal.id != planline.own_goal_id or (planline.computation_mode != 'manually' and !planline.type_action)">
<t t-if="goal.id != line.own_goal_id or (line.computation_mode != 'manually' and !line.definition_action)">
<t t-esc="goal.user_name"/>
</t>
<t t-if="goal.id == planline.own_goal_id and (planline.type_action or planline.computation_mode == 'manually')">
<a class="oe_goal_action" t-att-id="planline.own_goal_id"><t t-esc="goal.user_name"/></a>
<t t-if="goal.id == line.own_goal_id and (line.definition_action or line.computation_mode == 'manually')">
<a class="oe_goal_action" t-att-id="line.own_goal_id"><t t-esc="goal.user_name"/></a>
</t>
</t>
</div>
@ -100,7 +100,7 @@
<t t-name="gamification.challenge_suggestions">
<div class="oe_goal">
<h4>Proposed Challenges</h4>
<h4>Invited Challenges</h4>
<ul t-foreach="widget.challenge_suggestions.info" t-as="challenge" class="oe_goals_list">
<li>
<strong><a class="oe_challenge_reply" t-attf-title="#{challenge.description.value ? challenge.description.value : ''}" t-attf-id="{challenge.id}" title="more details..."><t t-esc="challenge.name"/></a></strong>

View File

@ -1,42 +0,0 @@
# We use a jinja2 sandboxed environment to render mako templates.
# Note that the rendering does not cover all the mako syntax, in particular
# arbitrary Python statements are not accepted, and not all expressions are
# allowed: only "public" attributes (not starting with '_') of objects may
# be accessed.
# This is done on purpose: it prevents incidental or malicious execution of
# Python code that may break the security of the server.
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/')),
block_start_string="<%",
block_end_string="%>",
variable_start_string="${",
variable_end_string="}",
comment_start_string="<%doc>",
comment_end_string="</%doc>",
line_statement_prefix="%",
line_comment_prefix="##",
trim_blocks=True, # do not output newline after blocks
autoescape=True, # XML/HTML automatic escaping
)
self.globals.update({
'str': str,
'quote': quote,
'urlencode': urlencode,
})

View File

@ -1,10 +1,10 @@
<% extends 'base.mako' %>
<% block body %>
% for planline in planlines_boards:
% for line in lines_boards:
<table width="100%" border="1">
<tr>
<th colspan="4">${planline.goal_type.name}</th>
<th colspan="4">${line.goal_definition.name}</th>
</tr>
<tr>
<th>#</th>
@ -12,7 +12,7 @@
<th>Completeness</th>
<th>Current</th>
</tr>
% for idx, goal in planline.board_goals:
% for idx, goal in line.board_goals:
% if idx < 3 or goal.user_id.id == uid:
<tr
% if goal.completeness >= 100:
@ -23,8 +23,8 @@
<td>${goal.user_id.name}</td>
<td>${goal.completeness}%</td>
<td>${goal.current}/${goal.target_goal}
% if goal.type_suffix:
${goal.type_suffix}
% if goal.definition_suffix:
${goal.definition_suffix}
% endif
</td>
</tr>

View File

@ -13,15 +13,15 @@
style="font-weight:bold;"
% endif
>
<td>${goal.type_id.name}</td>
<td>${goal.definition_id.name}</td>
<td>${goal.target_goal}
% if goal.type_suffix:
${goal.type_suffix}
% if goal.definition_suffix:
${goal.definition_suffix}
% endif
</td>
<td>${goal.current}
% if goal.type_suffix:
${goal.type_suffix}
% if goal.definition_suffix:
${goal.definition_suffix}
% endif
</td>
<td>${goal.completeness} %</td>

View File

@ -10,7 +10,7 @@
<p class="oe_grey">${object.report_header or ''}</p>
<p>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.</p>
<p>You have not updated your progress for the goal ${object.definition_id.name} (currently reached at ${object.completeness}%) for at least ${object.remind_update_delay} days. Do not forget to do it.</p>
<p>If you have not changed your score yet, you can use the button "The current value is up to date" to indicate so.</p>
</body>

View File

@ -1,29 +1,29 @@
-
In order to test process of the Goals, I assign the discovery goal plan to all users
In order to test process of the Goals, I assign the discovery challenge to all users
-
!record {model: gamification.goal.plan, id: plan_base_discover}:
!record {model: gamification.challenge, id: challenge_base_discover}:
autojoin_group_id: base.group_user
-
I verify that the users of the group are added (at least admin and demo)
-
!assert {model: gamification.goal.plan, id: plan_base_discover, string: The autojoin function was not successful}:
!assert {model: gamification.challenge, id: challenge_base_discover, string: The autojoin function was not successful}:
- len(user_ids) >= 2
-
I start the goal plan and verify the change of state
I start the challenge and verify the change of state
-
!python {model: gamification.goal.plan}: |
plan = self.browse(cr, uid, ref('plan_base_discover'))
self.action_start(cr, uid, [plan.id], context=context)
assert plan.state == 'inprogress', "Plan failed the change of state"
!python {model: gamification.challenge}: |
challenge = self.browse(cr, uid, ref('challenge_base_discover'))
self.action_start(cr, uid, [challenge.id], context=context)
assert challenge.state == 'inprogress', "Challenge failed the change of state"
-
I verify the goals are generated correctly
-
!python {model: gamification.goal}: |
goal_ids = self.search(cr, uid, [('plan_id', '=', ref('plan_base_discover'))], context=context)
goal_ids = self.search(cr, uid, [('challenge_id', '=', ref('challenge_base_discover'))], context=context)
assert len(goal_ids) >= 4, "Not enough goals have been generated"
for goal in self.browse(cr, uid, goal_ids, context=context):
assert goal.state != 'draft', "Draft goal have been generated"
assert goal.planline_id.plan_id.id == ref('plan_base_discover'), "Linked planline incorrect"
assert goal.line_id.challenge_id.id == ref('challenge_base_discover'), "Linked line incorrect"
-
I change timezone for demo user
-
@ -33,21 +33,21 @@
I check the goal for demo user is successful
-
!python {model: gamification.goal}: |
goal_ids = self.search(cr, uid, [('user_id', '=', ref('base.user_demo')),('type_id','=',ref('type_base_timezone'))])
goal_ids = self.search(cr, uid, [('user_id', '=', ref('base.user_demo')),('definition_id','=',ref('definition_base_timezone'))])
self.update(cr, uid, goal_ids, context=context)
for goal in self.browse(cr, uid, goal_ids, context=context):
assert goal.state == "reached", "Goal not successful %s" % goal.state
-
I add a reward to the challenge
-
!record {model: gamification.goal.plan, id: plan_base_discover}:
!record {model: gamification.challenge, id: challenge_base_discover}:
reward_first_id: gamification.badge_good_job
reward_failure: True
-
I check the demo user received the badge
-
!python {model: gamification.goal.plan}: |
plan = self.browse(cr, uid, ref('plan_base_discover'), context=context)
self.action_close(cr, uid, [plan.id])
!python {model: gamification.challenge}: |
challenge = self.browse(cr, uid, ref('challenge_base_discover'), context=context)
self.action_close(cr, uid, [challenge.id])
badge_ids = self.pool.get('gamification.badge.user').search(cr, uid, [('badge_id', '=', ref('badge_good_job')), ('user_id', '=', ref('base.user_demo'))])
assert badge_ids, "Demo users didn't received the badge"

View File

@ -101,7 +101,7 @@
</div>
</group>
<group string="Rewards for challenges">
<field name="plan_ids" widget="many2many_kanban" nolabel="1" />
<field name="challenge_ids" widget="many2many_kanban" nolabel="1" />
</group>
<group string="Statistics">
<group>

View File

@ -2,11 +2,11 @@
<openerp>
<data>
<record id="goal_plan_list_view" model="ir.ui.view">
<record id="challenge_list_view" model="ir.ui.view">
<field name="name">Challenges List</field>
<field name="model">gamification.goal.plan</field>
<field name="model">gamification.challenge</field>
<field name="arch" type="xml">
<tree string="Goal types" colors="blue:state == 'draft';grey:state == 'done'">
<tree string="Goal definitions" colors="blue:state == 'draft';grey:state == 'done'">
<field name="name"/>
<field name="period"/>
<field name="manager_id"/>
@ -15,11 +15,11 @@
</field>
</record>
<record id="goals_from_plan_act" model="ir.actions.act_window">
<record id="goals_from_challenge_act" model="ir.actions.act_window">
<field name="res_model">gamification.goal</field>
<field name="name">Related Goals</field>
<field name="view_mode">kanban,tree</field>
<field name="context">{'search_default_group_by_type': True, 'search_default_inprogress': True, 'search_default_plan_id': active_id, 'default_plan_id': active_id}</field>
<field name="context">{'search_default_group_by_definition': True, 'search_default_inprogress': True, 'search_default_challenge_id': active_id, 'default_challenge_id': active_id}</field>
<field name="help" type="html">
<p>
There is no goals associated to this challenge matching your search.
@ -28,11 +28,11 @@
</field>
</record>
<record id="goal_plan_form_view" model="ir.ui.view">
<record id="challenge_form_view" model="ir.ui.view">
<field name="name">Challenge Form</field>
<field name="model">gamification.goal.plan</field>
<field name="model">gamification.challenge</field>
<field name="arch" type="xml">
<form string="Goal types" version="7.0">
<form string="Goal definitions" version="7.0">
<header>
<button string="Start Now" type="object" name="action_start" states="draft" class="oe_highlight"/>
<button string="Refresh Challenge" type="object" name="action_check" states="inprogress"/>
@ -57,7 +57,7 @@
<!-- action buttons -->
<div class="oe_right oe_button_box">
<button type="action" name="%(goals_from_plan_act)d" string="Related Goals" attrs="{'invisible': [('state','=','draft')]}" />
<button type="action" name="%(goals_from_challenge_act)d" string="Related Goals" attrs="{'invisible': [('state','=','draft')]}" />
</div>
<group>
<group>
@ -72,13 +72,13 @@
</group>
<notebook>
<page string="Goals">
<field name="planline_ids" nolabel="1" colspan="4">
<tree string="Planline List" version="7.0" editable="bottom" >
<field name="line_ids" nolabel="1" colspan="4">
<tree string="Line List" version="7.0" editable="bottom" >
<field name="sequence" widget="handle"/>
<field name="type_id" on_change="on_change_type_id(type_id)" />
<field name="type_condition"/>
<field name="definition_id" on_change="on_change_definition_id(definition_id)" />
<field name="definition_condition"/>
<field name="target_goal"/>
<field name="type_full_suffix"/>
<field name="definition_full_suffix"/>
</tree>
</field>
<field name="description" placeholder="Describe the challenge: what is does, who it targets, why it matters..."/>
@ -98,7 +98,7 @@
<page string="Advanced Options">
<group string="Subscriptions">
<field name="autojoin_group_id" />
<field name="proposed_user_ids" widget="many2many_tags" />
<field name="invited_user_ids" widget="many2many_tags" />
</group>
<group string="Notification Messages">
<field name="report_message_frequency" />
@ -126,13 +126,12 @@
</field>
</record>
<record model="ir.ui.view" id="view_goal_plan_kanban">
<record model="ir.ui.view" id="view_challenge_kanban">
<field name="name">Challenge Kanban</field>
<field name="model">gamification.goal.plan</field>
<field name="model">gamification.challenge</field>
<field name="arch" type="xml">
<kanban version="7.0" class="oe_background_grey">
<field name="planline_ids"/>
<field name="planline_count"/>
<kanban string="Challenges" class="oe_background_grey" version="7.0">
<field name="line_ids"/>
<field name="user_ids"/>
<templates>
<t t-name="kanban-box">
@ -147,9 +146,9 @@
<h4><field name="name"/></h4>
<div class="oe_kanban_project_list">
<a type="action" name="%(goals_from_plan_act)d" style="margin-right: 10px">
<span t-if="record.planline_count.raw_value gt 1"><field name="planline_count"/> Goals</span>
<span t-if="record.planline_count.raw_value lt 2"><field name="planline_count"/> Goal</span>
<a type="action" name="%(goals_from_challenge_act)d" style="margin-right: 10px">
<span t-if="record.line_ids.length gt 1"><t t-esc="record.line_ids.length"/> Goals</span>
<span t-if="record.line_ids.length lt 2"><t t-esc="record.line_ids.length"/> Goal</span>
</a>
</div>
<div class="oe_kanban_badge_avatars">
@ -165,9 +164,9 @@
</field>
</record>
<record id="goal_plan_list_action" model="ir.actions.act_window">
<record id="challenge_list_action" model="ir.actions.act_window">
<field name="name">Challenges</field>
<field name="res_model">gamification.goal.plan</field>
<field name="res_model">gamification.challenge</field>
<field name="view_mode">kanban,tree,form</field>
<field name="context">{'search_default_inprogress':True, 'default_inprogress':True}</field>
<field name="help" type="html">
@ -182,40 +181,40 @@
</field>
</record>
<!-- Specify form view ID to avoid selecting view_challenge_wizard -->
<record id="goal_plan_list_action_view1" model="ir.actions.act_window.view">
<record id="challenge_list_action_view1" model="ir.actions.act_window.view">
<field eval="1" name="sequence"/>
<field name="view_mode">kanban</field>
<field name="act_window_id" ref="goal_plan_list_action"/>
<field name="view_id" ref="view_goal_plan_kanban"/>
<field name="act_window_id" ref="challenge_list_action"/>
<field name="view_id" ref="view_challenge_kanban"/>
</record>
<record id="goal_plan_list_action_view2" model="ir.actions.act_window.view">
<record id="challenge_list_action_view2" model="ir.actions.act_window.view">
<field eval="10" name="sequence"/>
<field name="view_mode">form</field>
<field name="act_window_id" ref="goal_plan_list_action"/>
<field name="view_id" ref="goal_plan_form_view"/>
<field name="act_window_id" ref="challenge_list_action"/>
<field name="view_id" ref="challenge_form_view"/>
</record>
<!-- Planline -->
<record id="goal_planline_list_view" model="ir.ui.view">
<field name="name">Goal planline list</field>
<field name="model">gamification.goal.planline</field>
<!-- Line -->
<record id="challenge_line_list_view" model="ir.ui.view">
<field name="name">Challenge line list</field>
<field name="model">gamification.challenge.line</field>
<field name="arch" type="xml">
<tree string="planline list" >
<field name="type_id"/>
<tree string="Challenge Lines" >
<field name="definition_id"/>
<field name="target_goal"/>
</tree>
</field>
</record>
<record id="goal_plan_search_view" model="ir.ui.view">
<record id="challenge_search_view" model="ir.ui.view">
<field name="name">Challenge Search</field>
<field name="model">gamification.goal.plan</field>
<field name="model">gamification.challenge</field>
<field name="arch" type="xml">
<search string="Search Challenges">
<filter name="inprogress" string="Running Challenges"
domain="[('state', '=', 'inprogress')]"/>
<filter name="hr_plans" string="HR Challenges"
<filter name="hr_challenges" string="HR Challenges"
domain="[('category', '=', 'hr')]"/>
<field name="name"/>
<group expand="0" string="Group By...">
@ -229,7 +228,7 @@
<record id="view_challenge_wizard" model="ir.ui.view">
<field name="name">Challenge Wizard</field>
<field name="model">gamification.goal.plan</field>
<field name="model">gamification.challenge</field>
<field name="arch" type="xml">
<form string="Challenge" version="7.0">
<field name="reward_failure" invisible="1"/>
@ -241,16 +240,16 @@
<field name="start_date" readonly="1" />
<field name="end_date" readonly="1" />
<field name="user_ids" string="Participating" readonly="1" widget="many2many_tags" />
<field name="proposed_user_ids" string="Invited" readonly="1" widget="many2many_tags" />
<field name="invited_user_ids" string="Invited" readonly="1" widget="many2many_tags" />
</group>
<group string="Goals">
<field name="planline_ids" nolabel="1" readonly="1" colspan="4">
<tree string="Planline List" version="7.0" editable="bottom" >
<field name="line_ids" nolabel="1" readonly="1" colspan="4">
<tree string="Challenge Lines" version="7.0" editable="bottom" >
<field name="sequence" widget="handle"/>
<field name="type_id"/>
<field name="type_condition"/>
<field name="definition_id"/>
<field name="definition_condition"/>
<field name="target_goal"/>
<field name="type_full_suffix"/>
<field name="definition_full_suffix"/>
</tree>
</field>
</group>
@ -281,7 +280,7 @@
<record id="challenge_wizard" model="ir.actions.act_window">
<field name="name">Challenge Description</field>
<field name="res_model">gamification.goal.plan</field>
<field name="res_model">gamification.challenge</field>
<field name="view_type">form</field>
<field name="view_id" ref="view_challenge_wizard"/>
<field name="target">new</field>

View File

@ -7,14 +7,14 @@
<field name="name">Goals</field>
<field name="res_model">gamification.goal</field>
<field name="view_mode">tree,form,kanban</field>
<field name="context">{'search_default_group_by_user': True, 'search_default_group_by_type': True}</field>
<field name="context">{'search_default_group_by_user': True, 'search_default_group_by_definition': True}</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create a goal.
</p>
<p>
A goal is defined by a user and a goal type.
Goals can be created automatically by using goal plans.
A goal is defined by a user and a goal definition.
Goals can be created automatically by using challenges.
</p>
</field>
</record>
@ -24,7 +24,7 @@
<field name="model">gamification.goal</field>
<field name="arch" type="xml">
<tree string="Goal List" colors="red:state == 'failed';green:state == 'reached';grey:state == 'canceled'">
<field name="type_id" invisible="1" />
<field name="definition_id" invisible="1" />
<field name="user_id" invisible="1" />
<field name="start_date"/>
<field name="end_date"/>
@ -32,7 +32,7 @@
<field name="target_goal"/>
<field name="completeness" widget="progressbar"/>
<field name="state" invisible="1"/>
<field name="planline_id" invisible="1"/>
<field name="line_id" invisible="1"/>
</tree>
</field>
</record>
@ -53,9 +53,9 @@
<sheet>
<group>
<group string="Reference">
<field name="type_id" on_change="on_change_type_id(type_id)" attrs="{'readonly':[('state','!=','draft')]}"/>
<field name="definition_id" on_change="on_change_definition_id(definition_id)" attrs="{'readonly':[('state','!=','draft')]}"/>
<field name="user_id" attrs="{'readonly':[('state','!=','draft')]}"/>
<field name="plan_id" attrs="{'readonly':[('state','!=','draft')]}"/>
<field name="challenge_id" attrs="{'readonly':[('state','!=','draft')]}"/>
</group>
<group string="Schedule">
<field name="start_date" attrs="{'readonly':[('state','!=','draft')]}"/>
@ -73,14 +73,14 @@
<label for="target_goal" />
<div>
<field name="target_goal" attrs="{'readonly':[('state','!=','draft')]}" class="oe_inline"/>
<field name="type_suffix" class="oe_inline"/>
<field name="definition_suffix" class="oe_inline"/>
</div>
<label for="current" />
<div>
<field name="current" class="oe_inline"/>
<button string="refresh" type="object" name="update" class="oe_link" attrs="{'invisible':['|',('computation_mode', '=', 'manually'),('state', '=', 'draft')]}" />
<div class="oe_grey" attrs="{'invisible':[('type_id', '=', False)]}">
Reached when current value is <strong><field name="type_condition" class="oe_inline"/></strong> than the target.
<div class="oe_grey" attrs="{'invisible':[('definition_id', '=', False)]}">
Reached when current value is <strong><field name="definition_condition" class="oe_inline"/></strong> than the target.
</div>
</div>
</group>
@ -112,11 +112,11 @@
<separator/>
<field name="user_id"/>
<field name="type_id"/>
<field name="plan_id"/>
<field name="definition_id"/>
<field name="challenge_id"/>
<group expand="0" string="Group By...">
<filter name="group_by_user" string="User" domain="[]" context="{'group_by':'user_id'}"/>
<filter name="group_by_type" string="Goal Type" domain="[]" context="{'group_by':'type_id'}"/>
<filter name="group_by_definition" string="Goal Definition" domain="[]" context="{'group_by':'definition_id'}"/>
<filter string="State" domain="[]" context="{'group_by':'state'}"/>
<filter string="End Date" domain="[]" context="{'group_by':'end_date'}"/>
</group>
@ -129,43 +129,43 @@
<field name="model">gamification.goal</field>
<field name="arch" type="xml">
<kanban version="7.0" class="oe_background_grey">
<field name="type_id"/>
<field name="definition_id"/>
<field name="user_id"/>
<field name="current"/>
<field name="completeness"/>
<field name="state"/>
<field name="target_goal"/>
<field name="type_condition"/>
<field name="type_suffix"/>
<field name="type_display"/>
<field name="definition_condition"/>
<field name="definition_suffix"/>
<field name="definition_display"/>
<field name="start_date"/>
<field name="end_date"/>
<field name="last_update"/>
<templates>
<t t-name="kanban-tooltip">
<field name="type_description"/>
<field name="definition_description"/>
</t>
<t t-name="kanban-box">
<div t-attf-class="oe_kanban_card oe_gamification_goal oe_kanban_goal #{record.end_date.raw_value &lt; record.last_update.raw_value &amp; record.state.raw_value == 'failed' ? 'oe_kanban_color_2' : ''} #{record.end_date.raw_value &lt; record.last_update.raw_value &amp; record.state.raw_value == 'reached' ? 'oe_kanban_color_5' : ''}">
<div class="oe_kanban_content">
<p><h4 class="oe_goal_name" tooltip="kanban-tooltip"><field name="type_id" /></h4></p>
<p><h4 class="oe_goal_name" tooltip="kanban-tooltip"><field name="definition_id" /></h4></p>
<div class="oe_kanban_left">
<img t-att-src="kanban_image('res.users', 'image_small', record.user_id.raw_value)" t-att-title="record.user_id.value" width="24" height="24" />
</div>
<field name="user_id" />
<div class="oe_goal_state_block">
<t t-if="record.type_display.raw_value == 'checkbox'">
<t t-if="record.definition_display.raw_value == 'checkbox'">
<div class="oe_goal_state oe_e">
<t t-if="record.state.raw_value=='reached'"><span class="oe_green" title="Goal Reached">W</span></t>
<t t-if="record.state.raw_value=='inprogress' || record.state.raw_value=='inprogress_update'"><span title="Goal in Progress">N</span></t>
<t t-if="record.state.raw_value=='failed'"><span class="oe_red" title="Goal Failed">X</span></t>
</div>
</t>
<t t-if="record.type_display.raw_value == 'progress'">
<t t-if="record.type_condition.raw_value =='higher'">
<field name="current" widget="gauge" style="width:160px; height: 120px;" options="{'max_field': 'target_goal', 'label_field': 'type_suffix'}" />
<t t-if="record.definition_display.raw_value == 'progress'">
<t t-if="record.definition_condition.raw_value =='higher'">
<field name="current" widget="gauge" style="width:160px; height: 120px;" options="{'max_field': 'target_goal', 'label_field': 'definition_suffix'}" />
</t>
<t t-if="record.type_condition.raw_value != 'higher'">
<t t-if="record.definition_condition.raw_value != 'higher'">
<div t-attf-class="oe_goal_state #{record.current.raw_value == record.target_goal.raw_value+1 ? 'oe_orange' : record.current.raw_value &gt; record.target_goal.raw_value ? 'oe_red' : 'oe_green'}">
<t t-esc="record.current.raw_value" />
</div>
@ -191,29 +191,28 @@
</record>
<!-- Goal types view -->
<!-- Goal definitions view -->
<record id="goal_type_list_action" model="ir.actions.act_window">
<field name="name">Goal Types</field>
<field name="res_model">gamification.goal.type</field>
<record id="goal_definition_list_action" model="ir.actions.act_window">
<field name="name">Goal Definitions</field>
<field name="res_model">gamification.goal.definition</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create a goal type.
Click to create a goal definition.
</p>
<p>
A goal type is a technical model of goal defining a condition to reach.
A goal definition is a technical model of goal defining a condition to reach.
The dates, values to reach or users are defined in goal instance.
</p>
</field>
</record>
<record id="goal_type_list_view" model="ir.ui.view">
<field name="name">Goal Types List</field>
<field name="model">gamification.goal.type</field>
<record id="goal_definition_list_view" model="ir.ui.view">
<field name="name">Goal Definitions List</field>
<field name="model">gamification.goal.definition</field>
<field name="arch" type="xml">
<tree string="Goal types">
<field name="sequence" widget="handle"/>
<tree string="Goal Definitions">
<field name="name"/>
<field name="computation_mode"/>
</tree>
@ -221,11 +220,11 @@
</record>
<record id="goal_type_form_view" model="ir.ui.view">
<field name="name">Goal Types Form</field>
<field name="model">gamification.goal.type</field>
<record id="goal_definition_form_view" model="ir.ui.view">
<field name="name">Goal Definitions Form</field>
<field name="model">gamification.goal.definition</field>
<field name="arch" type="xml">
<form string="Goal types" version="7.0">
<form string="Goal definitions" version="7.0">
<sheet>
<label for="name" class="oe_edit_only"/>
<h1>
@ -263,11 +262,11 @@
</field>
</record>
<record id="goal_type_search_view" model="ir.ui.view">
<field name="name">Goal Type Search</field>
<field name="model">gamification.goal.type</field>
<record id="goal_definition_search_view" model="ir.ui.view">
<field name="name">Goal Definition Search</field>
<field name="model">gamification.goal.definition</field>
<field name="arch" type="xml">
<search string="Search Goal Types">
<search string="Search Goal Definitions">
<field name="name"/>
<field name="model_id"/>
<field name="field_id"/>
@ -302,8 +301,8 @@
<!-- menus in settings - technical feature required -->
<menuitem id="gamification_menu" name="Gamification Tools" parent="base.menu_administration" groups="base.group_no_one" />
<menuitem id="gamification_goal_menu" parent="gamification_menu" action="goal_list_action" sequence="0"/>
<menuitem id="gamification_plan_menu" parent="gamification_menu" action="goal_plan_list_action" sequence="10"/>
<menuitem id="gamification_type_menu" parent="gamification_menu" action="goal_type_list_action" sequence="20"/>
<menuitem id="gamification_challenge_menu" parent="gamification_menu" action="challenge_list_action" sequence="10"/>
<menuitem id="gamification_definition_menu" parent="gamification_menu" action="goal_definition_list_action" sequence="20"/>
<menuitem id="gamification_badge_menu" parent="gamification_menu" action="badge_list_action" sequence="30"/>
</data>

View File

@ -24,7 +24,7 @@
'author': 'OpenERP SA',
'category': 'hidden',
'depends': ['gamification','sale_crm'],
'description': """Example of goal types and plans that can be used related to the usage of the CRM Sale module.""",
'description': """Example of goal definitions and challenges that can be used related to the usage of the CRM Sale module.""",
'data': ['sale_crm_goals.xml'],
'demo': ['sale_crm_goals_demo.xml'],

View File

@ -2,8 +2,8 @@
<openerp>
<data>
<!-- goal types -->
<record model="gamification.goal.type" id="type_crm_tot_invoices">
<!-- goal definitions -->
<record model="gamification.goal.definition" id="definition_crm_tot_invoices">
<field name="name">Total Invoiced</field>
<field name="description"></field>
<field name="computation_mode">sum</field>
@ -14,7 +14,7 @@
<field name="domain">[('state','!=','cancel'),('user_id','=',user.id),('type','=','out_invoice')]</field>
</record>
<record model="gamification.goal.type" id="type_crm_nbr_new_leads">
<record model="gamification.goal.definition" id="definition_crm_nbr_new_leads">
<field name="name">New Leads</field>
<field name="description">Based on the creation date</field>
<field name="computation_mode">count</field>
@ -25,7 +25,7 @@
<field name="domain">[('user_id','=',user.id), '|', ('type', '=', 'lead'), ('type', '=', 'opportunity')]</field>
</record>
<record model="gamification.goal.type" id="type_crm_lead_delay_open">
<record model="gamification.goal.definition" id="definition_crm_lead_delay_open">
<field name="name">Time to Qualify a Lead</field>
<field name="description">The average number of days to open the case (lower than)</field>
<field name="computation_mode">sum</field>
@ -37,7 +37,7 @@
<field name="domain">[('user_id','=',user.id),('type', '=', 'lead')]</field>
</record>
<record model="gamification.goal.type" id="type_crm_lead_delay_close">
<record model="gamification.goal.definition" id="definition_crm_lead_delay_close">
<field name="name">Days to Close a Deal</field>
<field name="description">The average number of days to close the case (lower than)</field>
<field name="computation_mode">sum</field>
@ -50,7 +50,7 @@
</record>
<record model="gamification.goal.type" id="type_crm_nbr_call">
<record model="gamification.goal.definition" id="definition_crm_nbr_call">
<field name="name">Logged Calls</field>
<field name="description">Log a certain number of calls to reach this goal</field>
<field name="computation_mode">count</field>
@ -60,7 +60,7 @@
<field name="domain">[('user_id','=',user.id),('state','=','done')]</field>
</record>
<record model="gamification.goal.type" id="type_crm_nbr_new_opportunities">
<record model="gamification.goal.definition" id="definition_crm_nbr_new_opportunities">
<field name="name">New Opportunities</field>
<field name="description">Based on the opening date</field>
<field name="computation_mode">count</field>
@ -70,7 +70,7 @@
<field name="domain">[('user_id','=',user.id),('type','=','opportunity')]</field>
</record>
<record model="gamification.goal.type" id="type_crm_nbr_sale_order_created">
<record model="gamification.goal.definition" id="definition_crm_nbr_sale_order_created">
<field name="name">New Sales Orders</field>
<field name="description">Based on the creation date</field>
<field name="computation_mode">count</field>
@ -80,7 +80,7 @@
<field name="domain">[('user_id','=',user.id),('state','not in',('draft', 'sent', 'cancel'))]</field>
</record>
<record model="gamification.goal.type" id="type_crm_nbr_paid_sale_order">
<record model="gamification.goal.definition" id="definition_crm_nbr_paid_sale_order">
<field name="name">Paid Sales Orders</field>
<field name="description">Based on the invoice date</field>
<field name="computation_mode">count</field>
@ -89,7 +89,7 @@
<field name="field_date_id" eval="ref('account.field_account_invoice_report_day')" />
<field name="domain">[('state','=','paid'),('user_id','=',user.id),('type','=','out_invoice')]</field>
</record>
<record model="gamification.goal.type" id="type_crm_tot_paid_sale_order">
<record model="gamification.goal.definition" id="definition_crm_tot_paid_sale_order">
<field name="name">Total Paid Sales Orders</field>
<field name="description">Based on the invoice date</field>
<field name="computation_mode">count</field>
@ -101,7 +101,7 @@
</record>
<record model="gamification.goal.type" id="type_crm_nbr_customer_refunds">
<record model="gamification.goal.definition" id="definition_crm_nbr_customer_refunds">
<field name="name">Customer Refunds</field>
<field name="description">Refund the least customers (lower than)</field>
<field name="computation_mode">count</field>
@ -111,7 +111,7 @@
<field name="field_date_id" eval="ref('account.field_account_invoice_report_day')" />
<field name="domain">[('state','!=','cancel'),('user_id','=',user.id),('type','=','out_refund')]</field>
</record>
<record model="gamification.goal.type" id="type_crm_tot_customer_refunds">
<record model="gamification.goal.definition" id="definition_crm_tot_customer_refunds">
<field name="name">Total Customer Refunds</field>
<field name="description">The total refunded value is a negative value. Validated when higher (min refunded).</field>
<field name="computation_mode">sum</field>
@ -125,8 +125,8 @@
<!-- plans -->
<record model="gamification.goal.plan" id="plan_crm_sale">
<!-- challenges -->
<record model="gamification.challenge" id="challenge_crm_sale">
<field name="name">Monthly Sales Targets</field>
<field name="period">monthly</field>
<field name="visibility_mode">board</field>
@ -135,7 +135,7 @@
<field name="report_header">The following message contains the current progress of the sale team based on several criterias. The progress is reinitialised each month and shared weekly.</field>
</record>
<record model="gamification.goal.plan" id="plan_crm_marketing">
<record model="gamification.challenge" id="challenge_crm_marketing">
<field name="name">Lead Acquisition</field>
<field name="period">monthly</field>
<field name="visibility_mode">progressbar</field>
@ -144,30 +144,30 @@
<field name="report_header">The following message contains the current progress of the marketing team based on several criterias. The progress is reinitialised each month and shared weekly.</field>
</record>
<!-- planlines -->
<record model="gamification.goal.planline" id="planline_crm_sale1">
<field name="type_id" eval="ref('type_crm_tot_invoices')" />
<!-- lines -->
<record model="gamification.challenge.line" id="line_crm_sale1">
<field name="definition_id" eval="ref('definition_crm_tot_invoices')" />
<field name="target_goal">20000</field>
<field name="plan_id" eval="ref('plan_crm_sale')" />
<field name="challenge_id" eval="ref('challenge_crm_sale')" />
</record>
<record model="gamification.goal.planline" id="planline_crm_marketing1">
<field name="type_id" eval="ref('type_crm_nbr_new_leads')" />
<record model="gamification.challenge.line" id="line_crm_marketing1">
<field name="definition_id" eval="ref('definition_crm_nbr_new_leads')" />
<field name="target_goal">7</field>
<field name="plan_id" eval="ref('plan_crm_marketing')" />
<field name="challenge_id" eval="ref('challenge_crm_marketing')" />
<field name="sequence">1</field>
</record>
<record model="gamification.goal.planline" id="planline_crm_marketing2">
<field name="type_id" eval="ref('type_crm_lead_delay_open')" />
<record model="gamification.challenge.line" id="line_crm_marketing2">
<field name="definition_id" eval="ref('definition_crm_lead_delay_open')" />
<field name="target_goal">15</field>
<field name="plan_id" eval="ref('plan_crm_marketing')" />
<field name="challenge_id" eval="ref('challenge_crm_marketing')" />
<field name="sequence">2</field>
</record>
<record model="gamification.goal.planline" id="planline_crm_marketing3">
<field name="type_id" eval="ref('type_crm_nbr_new_opportunities')" />
<record model="gamification.challenge.line" id="line_crm_marketing3">
<field name="definition_id" eval="ref('definition_crm_nbr_new_opportunities')" />
<field name="target_goal">5</field>
<field name="plan_id" eval="ref('plan_crm_marketing')" />
<field name="challenge_id" eval="ref('challenge_crm_marketing')" />
<field name="sequence">3</field>
</record>

View File

@ -2,17 +2,17 @@
<openerp>
<data>
<!-- plans -->
<record model="gamification.goal.plan" id="plan_crm_sale">
<!-- challenges -->
<record model="gamification.challenge" id="challenge_crm_sale">
<field name="user_ids" eval="[(4,ref('base.user_demo'))]" />
<field name="state">inprogress</field>
</record>
<!-- goals -->
<record model="gamification.goal" id="goal_crm_sale1">
<field name="type_id" eval="ref('type_crm_tot_invoices')" />
<field name="definition_id" eval="ref('definition_crm_tot_invoices')" />
<field name="user_id" eval="ref('base.user_demo')" />
<field name="planline_id" eval="ref('planline_crm_sale1')" />
<field name="line_id" eval="ref('line_crm_sale1')" />
<field name="start_date" eval="time.strftime('2013-03-01')" />
<field name="end_date" eval="time.strftime('2013-03-31')" />
<field name="target_goal">2000</field>

View File

@ -131,7 +131,7 @@ class hr_employee(osv.osv):
"""Return the list of goals assigned to the employee"""
res = {}
for employee in self.browse(cr, uid, ids, context=context):
res[employee.id] = self.pool.get('gamification.goal').search(cr,uid,[('user_id', '=', employee.user_id.id), ('plan_id.category', '=', 'hr')], context=context)
res[employee.id] = self.pool.get('gamification.goal').search(cr,uid,[('user_id', '=', employee.user_id.id), ('challenge_id.category', '=', 'hr')], context=context)
return res
def _get_employee_badges(self, cr, uid, ids, field_name, arg, context=None):

View File

@ -93,22 +93,22 @@
<field name="name">Goals History</field>
<field name="view_mode">tree,kanban</field>
<field name="context">{'search_default_group_by_user': True, 'search_default_group_by_type': True}</field>
<field name="domain">[('plan_id.category', '=', 'hr')]</field>
<field name="domain">[('challenge_id.category', '=', 'hr')]</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create a goal.
</p>
<p>
A goal is defined by a user and a goal type.
Goals can be created automatically by using goal plans.
Goals can be created automatically by using challenges.
</p>
</field>
</record>
<record id="goal_plan_list_action2" model="ir.actions.act_window">
<record id="challenge_list_action2" model="ir.actions.act_window">
<field name="name">Challenges</field>
<field name="res_model">gamification.goal.plan</field>
<field name="res_model">gamification.challenge</field>
<field name="view_mode">kanban,tree,form</field>
<field name="domain">[('category', '=', 'hr')]</field>
<field name="context">{'search_default_inprogress':True, 'default_inprogress':True}</field>
@ -123,23 +123,23 @@
</p>
</field>
</record>
<record id="goal_plan_list_action2_view1" model="ir.actions.act_window.view">
<record id="challenge_list_action2_view1" model="ir.actions.act_window.view">
<field eval="1" name="sequence"/>
<field name="view_mode">kanban</field>
<field name="act_window_id" ref="goal_plan_list_action2"/>
<field name="view_id" ref="gamification.view_goal_plan_kanban"/>
<field name="act_window_id" ref="challenge_list_action2"/>
<field name="view_id" ref="gamification.view_challenge_kanban"/>
</record>
<record id="goal_plan_list_action2_view2" model="ir.actions.act_window.view">
<record id="challenge_list_action2_view2" model="ir.actions.act_window.view">
<field eval="10" name="sequence"/>
<field name="view_mode">form</field>
<field name="act_window_id" ref="goal_plan_list_action2"/>
<field name="view_id" ref="gamification.goal_plan_form_view"/>
<field name="act_window_id" ref="challenge_list_action2"/>
<field name="view_id" ref="gamification.challenge_form_view"/>
</record>
<menuitem id="menu_hr_gamification" parent="hr.menu_hr_root" name="Engagement" sequence="40"/>
<menuitem id="gamification_badge_menu_hr" parent="menu_hr_gamification" action="gamification.badge_list_action" />
<menuitem id="gamification_plan_menu_hr" parent="menu_hr_gamification" action="goal_plan_list_action2" groups="base.group_hr_user"/>
<menuitem id="gamification_challenge_menu_hr" parent="menu_hr_gamification" action="challenge_list_action2" groups="base.group_hr_user"/>
<menuitem id="gamification_goal_menu_hr" parent="menu_hr_gamification" action="goals_menu_groupby_action2" groups="base.group_hr_user"/>
</data>

View File

@ -1,5 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
plan_officer,"Goal Plan Officer",gamification.model_gamification_goal_plan,base.group_hr_user,1,1,1,1
planline_officer,"Goal Planline Officer",gamification.model_gamification_goal_planline,base.group_hr_user,1,1,1,1
challenge_officer,"Challenge Officer",gamification.model_gamification_challenge,base.group_hr_user,1,1,1,1
challenge_line_officer,"Challenge Line Officer",gamification.model_gamification_challenge_line,base.group_hr_user,1,1,1,1
badge_officer,"Badge Officer",gamification.model_gamification_badge,base.group_hr_user,1,1,1,1
badge_user_officer,"Badge-user Officer",gamification.model_gamification_badge_user,base.group_hr_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 plan_officer challenge_officer Goal Plan Officer Challenge Officer gamification.model_gamification_goal_plan gamification.model_gamification_challenge base.group_hr_user 1 1 1 1
3 planline_officer challenge_line_officer Goal Planline Officer Challenge Line Officer gamification.model_gamification_goal_planline gamification.model_gamification_challenge_line base.group_hr_user 1 1 1 1
4 badge_officer Badge Officer gamification.model_gamification_badge base.group_hr_user 1 1 1 1
5 badge_user_officer Badge-user Officer gamification.model_gamification_badge_user base.group_hr_user 1 1 1 1

View File

@ -1,7 +1,7 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
goal_portal,"Goal Portal",gamification.model_gamification_goal,portal.group_portal,1,1,0,0
goal_type_portal,"Goal Type Portal",gamification.model_gamification_goal_type,portal.group_portal,1,0,0,0
plan_portal,"Goal Plan Portal",gamification.model_gamification_goal_plan,portal.group_portal,1,0,0,0
planline_portal,"Goal Planline Portal",gamification.model_gamification_goal_planline,portal.group_portal,1,0,0,0
goal_definition_portal,"Goal Definition Portal",gamification.model_gamification_goal_definition,portal.group_portal,1,0,0,0
challenge_portal,"Goal Challenge Portal",gamification.model_gamification_challenge,portal.group_portal,1,0,0,0
challenge_line_portal,"Challenge Line Portal",gamification.model_gamification_challenge_line,portal.group_portal,1,0,0,0
badge_portal,"Badge Portal",gamification.model_gamification_badge,portal.group_portal,1,0,0,0
badge_user_portal,"Badge-user Portal",gamification.model_gamification_badge_user,portal.group_portal,1,1,1,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 goal_portal Goal Portal gamification.model_gamification_goal portal.group_portal 1 1 0 0
3 goal_type_portal goal_definition_portal Goal Type Portal Goal Definition Portal gamification.model_gamification_goal_type gamification.model_gamification_goal_definition portal.group_portal 1 0 0 0
4 plan_portal challenge_portal Goal Plan Portal Goal Challenge Portal gamification.model_gamification_goal_plan gamification.model_gamification_challenge portal.group_portal 1 0 0 0
5 planline_portal challenge_line_portal Goal Planline Portal Challenge Line Portal gamification.model_gamification_goal_planline gamification.model_gamification_challenge_line portal.group_portal 1 0 0 0
6 badge_portal Badge Portal gamification.model_gamification_badge portal.group_portal 1 0 0 0
7 badge_user_portal Badge-user Portal gamification.model_gamification_badge_user portal.group_portal 1 1 1 0