[REF] gamification: cleaning methods and reports

bzr revid: mat@openerp.com-20131220144521-wyu1gx8bcitu6nk4
This commit is contained in:
Martin Trigaux
2013-12-20 15:45:21 +01:00
parent 90dfa5895d
commit f77aefc07a
14 changed files with 261 additions and 378 deletions
+31 -33
View File
@@ -7,7 +7,7 @@
<field name="name">Set your Timezone</field>
<field name="description">Configure your profile and specify your timezone</field>
<field name="computation_mode">count</field>
<field name="display_mode">checkbox</field>
<field name="display_mode">boolean</field>
<field name="model_id" eval="ref('base.model_res_users')" />
<field name="domain">[('id','=',user.id),('partner_id.tz', '!=', False)]</field>
<field name="action_id" eval="ref('base.action_res_users_my')" />
@@ -18,7 +18,7 @@
<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>
<field name="display_mode">checkbox</field>
<field name="display_mode">boolean</field>
<field name="model_id" eval="ref('base.model_res_company')" />
<field name="domain">[('user_ids', 'in', [user.id]), ('name', '=', 'Your Company')]</field>
<field name="condition">lower</field>
@@ -29,7 +29,7 @@
<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>
<field name="display_mode">boolean</field>
<field name="model_id" eval="ref('base.model_res_company')" />
<field name="domain">[('user_ids', 'in', user.id),('logo', '!=', False)]</field>
<field name="action_id" eval="ref('base.action_res_company_form')" />
@@ -50,7 +50,7 @@
<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>
<field name="display_mode">boolean</field>
<field name="computation_mode">count</field>
<field name="model_id" eval="ref('base.model_res_users')" />
<field name="domain">[('id', '!=', user.id)]</field>
@@ -146,24 +146,24 @@
<th>Current</th>
<th>Completeness</th>
</tr>
% for goal in ctx["goals"]:
% for line in ctx["challenge_lines"]:
<tr
% if goal.completeness >= 100:
% if line['completeness'] >= 100:
style="font-weight:bold;"
% endif
>
<td>${goal.definition_id.name}</td>
<td>${goal.target_goal}
% if goal.definition_suffix:
${goal.definition_suffix}
<td>${line['name']}</td>
<td>${line['target']}
% if line['suffix']:
${line['suffix']}
% endif
</td>
<td>${goal.current}
% if goal.definition_suffix:
${goal.definition_suffix}
<td>${line['current']}
% if line['suffix']:
${line['suffix']}
% endif
</td>
<td>${goal.completeness} %</td>
<td>${line['completeness']} %</td>
</tr>
% endfor
</table>]]></field>
@@ -177,10 +177,10 @@
</header>
<p class="oe_grey">${object.report_header or ''}</p>
% for line in ctx['lines_boards']:
% for line in ctx["challenge_lines"]:
<table width="100%" border="1">
<tr>
<th colspan="4">${line.goal_definition.name}</th>
<th colspan="4">${line['name']}</th>
</tr>
<tr>
<th>#</th>
@@ -188,23 +188,21 @@
<th>Completeness</th>
<th>Current</th>
</tr>
% for idx, goal in line.board_goals:
% if idx < 3 or goal.user_id.id == user.id:
<tr
% if goal.completeness >= 100:
style="font-weight:bold;"
% endif
>
<td>${idx+1}</td>
<td>${goal.user_id.name}</td>
<td>${goal.completeness}%</td>
<td>${goal.current}/${goal.target_goal}
% if goal.definition_suffix:
${goal.definition_suffix}
% endif
</td>
</tr>
% endif
% for goal in line['goals']:
<tr
% if goal.completeness >= 100:
style="font-weight:bold;"
% endif
>
<td>${goal['rank']}</td>
<td>${goal['name']}</td>
<td>${goal['completeness']}%</td>
<td>${goal['current']}/${line['target']}
% if line['suffix']:
${line['suffix']}
% endif
</td>
</tr>
% endfor
</table>
+130 -81
View File
@@ -19,17 +19,18 @@
#
##############################################################################
from openerp import SUPERUSER_ID
from openerp.osv import fields, osv
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as DF
from openerp.tools.translate import _
# from templates import TemplateHelper
from datetime import date, datetime, timedelta
import calendar
import logging
_logger = logging.getLogger(__name__)
# display top 3 in ranking, could be db variable
MAX_VISIBILITY_RANKING = 3
def start_end_date_for_period(period, default_start_date=False, default_end_date=False):
"""Return the start and end date for a goal period based on today
@@ -107,7 +108,7 @@ class gamification_challenge(osv.Model):
('other', 'Settings / Gamification Tools'),
]
_sort = 'end_date, start_date, name, id'
_order = 'end_date, start_date, name, id'
_columns = {
'name': fields.char('Challenge Name', required=True, translate=True),
'description': fields.text('Description', translate=True),
@@ -444,89 +445,137 @@ class gamification_challenge(osv.Model):
##### JS utilities #####
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"""
def _get_serialized_challenge_lines(self, cr, uid, challenge, user_id=False, restrict_goal_ids=False, restrict_top=False, context=None):
"""Return a serialised version of the goals information
:challenge: browse record of challenge to compute
:user_id: res.users id of the user retrieving progress (False if no distinction, only for ranking challenges)
:restrict_goal_ids: <list(int)> compute only the results for this subset if gamification.goal ids, if False retrieve every goal of current running challenge
:restrict_top: <int> for challenge lines where visibility_mode == 'ranking', retrieve only these bests results and itself, if False retrieve all
restrict_goal_ids has priority over restrict_top
format list
# if visibility_mode == 'ranking'
{
'name': <gamification.goal.description name>,
'description': <gamification.goal.description description>,
'condition': <reach condition {lower,higher}>,
'computation_mode': <target computation {manually,count,sum,python}>,
'monetary': <{True,False}>,
'suffix': <value suffix>,
'action': <{True,False}>,
'display_mode': <{progress,boolean}>,
'target': <challenge line target>,
'own_goal_id': <gamification.goal id where user_id == uid>,
'goals': [
{
'id': <gamification.goal id>,
'rank': <user ranking>,
'user_id': <res.users id>,
'name': <res.users name>,
'state': <gamification.goal state {draft,inprogress,inprogress_update,reached,failed,canceled}>,
'completeness': <percentage>,
'current': <current value>,
}
]
},
# if visibility_mode == 'personal'
{
'id': <gamification.goal id>,
'name': <gamification.goal.description name>,
'description': <gamification.goal.description description>,
'condition': <reach condition {lower,higher}>,
'computation_mode': <target computation {manually,count,sum,python}>,
'monetary': <{True,False}>,
'suffix': <value suffix>,
'action': <{True,False}>,
'display_mode': <{progress,boolean}>,
'target': <challenge line target>,
'state': <gamification.goal state {draft,inprogress,inprogress_update,reached,failed,canceled}>,
'completeness': <percentage>,
'current': <current value>,
}
"""
goal_obj = self.pool.get('gamification.goal')
lines_boards = []
(start_date, end_date) = start_end_date_for_period(challenge.period)
res_lines = []
for line in challenge.line_ids:
line_data = {
'name': line.definition_id.name,
'description': line.definition_id.description,
'condition': line.definition_id.condition,
'computation_mode': line.definition_id.computation_mode,
'monetary': line.definition_id.monetary,
'suffix': line.definition_id.suffix,
'action': True if line.definition_id.action_id else False,
'display_mode': line.definition_id.display_mode,
'target': line.target_goal,
}
domain = [
('line_id', '=', line.id),
('state', 'in', ('inprogress', 'inprogress_update',
'reached', 'failed')),
('state', '!=', 'draft'),
]
if subset_goal_ids:
goal_ids = goal_obj.search(cr, uid, domain, context=context)
common_goal_ids = [goal for goal in goal_ids if goal in subset_goal_ids]
if restrict_goal_ids:
domain.append(('ids', 'in', restrict_goal_ids))
else:
# if no subset goals, use the dates for restriction
if start_date:
domain.append(('start_date', '=', start_date))
if end_date:
domain.append(('end_date', '=', end_date))
common_goal_ids = goal_obj.search(cr, uid, domain, context=context)
board_goals = [goal for goal in goal_obj.browse(cr, uid, common_goal_ids, context=context)]
if len(board_goals) == 0:
# 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))
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, challenge, subset_goal_ids=False, context=None):
"""Get the list of latest goals of a user for a challenge"""
domain = [
('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(challenge.period)
if subset_goal_ids:
# use the domain for safety, don't want irrelevant report if wrong argument
goal_ids = goal_obj.search(cr, uid, domain, context=context)
related_goal_ids = [goal for goal in goal_ids if goal in subset_goal_ids]
else:
# if no subset goals, use the dates for restriction
if start_date:
domain.append(('start_date', '=', start_date))
if end_date:
domain.append(('end_date', '=', end_date))
related_goal_ids = goal_obj.search(cr, uid, domain, context=context)
if len(related_goal_ids) == 0:
return False
goals = []
all_done = True
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 challenge run
continue
else:
all_done = False
if challenge.visibility_mode == 'personal':
if not user_id:
raise osv.except_osv(_('Error!'),_("Retrieving progress for personal challenge without user information"))
domain.append(('user_id', '=', user_id))
sorting = goal_obj._order
limit = 1
# initialise in case search returns no results
line_data.update({
'id': 0,
'current': 0,
'completeness': 0,
'state': 'draft',
})
else:
if goal.state == 'inprogress' or goal.state == 'inprogress_update':
all_done = False
line_data.update({
'own_goal_id': False,
'goals': [],
})
sorting = "completeness desc, current desc"
limit = False
goals.append(goal)
goal_ids = goal_obj.search(cr, uid, domain, order=sorting, limit=limit, context=context)
ranking = 0
for goal in goal_obj.browse(cr, uid, goal_ids, context=context):
if challenge.visibility_mode == 'personal':
# limit=1 so only one result
line_data.update({
'id': goal.id,
'current': goal.current,
'completeness': goal.completeness,
'state': goal.state,
})
else:
ranking += 1
if user_id and goal.user_id.id == user_id:
line_data['own_goal_id'] = goal.id
elif restrict_top and ranking > restrict_top:
# not own goal, over top, skipping
continue
if all_done:
# skip challenges where all goal are done or failed
return False
else:
return goals
line_data['goals'].append({
'id': goal.id,
'user_id': goal.user_id.id,
'name': goal.user_id.name,
'rank': ranking,
'current': goal.current,
'completeness': goal.completeness,
'state': goal.state,
})
res_lines.append(line_data)
return res_lines
##### Reporting #####
@@ -550,11 +599,11 @@ class gamification_challenge(osv.Model):
temp_obj = self.pool.get('email.template')
ctx = context.copy()
if challenge.visibility_mode == 'ranking':
lines_boards = self.get_board_goal_info(cr, uid, challenge, subset_goal_ids, context)
lines_boards = self._get_serialized_challenge_lines(cr, uid, challenge, user_id=False, restrict_goal_ids=subset_goal_ids, restrict_top=False, context=context)
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.challenge', challenge.id, context=context)
ctx.update({'challenge_lines': lines_boards})
template_id = self.pool.get('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.challenge', challenge.id, context=ctx)
# body_html = template_env.get_template('group_progress.mako').render({'object': challenge, 'lines_boards': lines_boards, 'uid': uid})
@@ -572,14 +621,14 @@ class gamification_challenge(osv.Model):
else:
# generate individual reports
for user in users or challenge.user_ids:
goals = self.get_indivual_goal_info(cr, uid, user.id, challenge, subset_goal_ids, context=context)
goals = self._get_serialized_challenge_lines(cr, uid, challenge, user.id, restrict_goal_ids=subset_goal_ids, context=context)
if not goals:
continue
ctx.update({'goals': goals})
ctx.update({'challenge_lines': 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.challenge', challenge.id, context=context)
# send message only to users
body_html = temp_obj.render_template(cr, user.id, template_id.body_html, 'gamification.challenge', challenge.id, context=ctx)
# send message only to users, not on the challenge
self.message_post(cr, uid, 0,
body=body_html,
partner_ids=[(4, user.partner_id.id)],
@@ -600,7 +649,7 @@ class gamification_challenge(osv.Model):
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, 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)
self.write(cr, SUPERUSER_ID, 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, challenge_ids, context=None, user_id=None):
@@ -608,7 +657,7 @@ class gamification_challenge(osv.Model):
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, challenge_ids, body=message, context=context)
self.message_post(cr, SUPERUSER_ID, 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, challenge_id, context=None):
@@ -710,7 +759,7 @@ class gamification_challenge(osv.Model):
for goal in goal_obj.browse(cr, uid, goal_ids, context=context):
if goal.state != 'reached':
all_reached = False
if goal.definition_condition == 'higher':
if goal.condition == 'higher':
# can be over 100
total_completness += 100.0 * goal.current / goal.target_goal
elif goal.state == 'reached':
@@ -761,7 +810,7 @@ class gamification_challenge_line(osv.Model):
goal_definition = goal_definition.browse(cr, uid, definition_id, context=context)
ret = {
'value': {
'definition_condition': goal_definition.condition,
'condition': goal_definition.condition,
'definition_full_suffix': goal_definition.full_suffix
}
}
@@ -781,7 +830,7 @@ class gamification_challenge_line(osv.Model):
required=True),
'sequence': fields.integer('Sequence',
help='Sequence number for ordering'),
'definition_condition': fields.related('definition_id', 'condition', type="selection",
'condition': fields.related('definition_id', 'condition', type="selection",
readonly=True, string="Condition", selection=[('lower', '<='), ('higher', '>=')]),
'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"),
+1
View File
@@ -76,6 +76,7 @@ class gamification_goal_definition(osv.Model):
'display_mode': fields.selection([
('progress', 'Progressive (using numerical values)'),
('checkbox', 'Checkbox (done or not-done)'),
('boolean', 'Exclusive (done or not-done)'),
],
string="Displayed as", required=True),
'model_id': fields.many2one('ir.model',
+23 -76
View File
@@ -20,7 +20,7 @@
##############################################################################
from openerp.osv import osv
from challenge import MAX_VISIBILITY_RANKING
class res_users_gamification_group(osv.Model):
""" Update of res.users class
@@ -58,14 +58,23 @@ class res_users_gamification_group(osv.Model):
challenge_obj.write(cr, uid, challenge_ids, {'user_ids': [(4, write_res)]}, 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 challenge
# def get_goals_todo_info(self, cr, uid, context=None):
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
definitions
:return: list of dictionnaries for each goal to display
def get_serialised_gamification_summary(self, cr, uid, context=None):
return self._serialised_goals_summary(cr, uid, user_id=uid, context=context)
def _serialised_goals_summary(self, cr, uid, user_id, context=None):
"""Return a serialised list of goals assigned to the user, grouped by challenge
[
{
'id': <gamification.challenge id>,
'name': <gamification.challenge name>,
'visibility_mode': <visibility {ranking,personal}>,
'currency': <res.currency id>,
'lines': [(see gamification_challenge._get_serialized_challenge_lines() format)]
},
]
"""
all_goals_info = []
challenge_obj = self.pool.get('gamification.challenge')
@@ -74,78 +83,14 @@ class res_users_gamification_group(osv.Model):
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 = {
all_goals_info.append({
'id': challenge.id,
'name': challenge.name,
'visibility_mode': challenge.visibility_mode,
'currency': user.company_id.currency_id.id
}
'currency': user.company_id.currency_id.id,
'lines': challenge_obj._get_serialized_challenge_lines(cr, uid, challenge, user_id, restrict_top=MAX_VISIBILITY_RANKING, context=context),
})
if challenge.visibility_mode == 'ranking':
# 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 not goals_info:
# challenge with no valid lines
continue
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 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
vals['goals'].append({
'rank': goal[0] + 1,
'id': goal[1].id,
'user_id': goal[1].user_id.id,
'user_name': goal[1].user_id.name,
'state': goal[1].state,
'completeness': goal[1].completeness,
'current': goal[1].current,
'target_goal': goal[1].target_goal,
})
if uid == goal[1].user_id.id:
vals['own_goal_id'] = goal[1].id
serialized_goals_info['lines'].append(vals)
else:
# individual report are simply a list of goal
goals_info = challenge_obj.get_indivual_goal_info(cr, uid, uid, challenge, subset_goal_ids=False, context=context)
if not goals_info:
continue
serialized_goals_info['goals'] = []
for goal in goals_info:
serialized_goals_info['goals'].append({
'id': goal.id,
'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,
'current': goal.current,
'target_goal': goal.target_goal,
})
all_goals_info.append(serialized_goals_info)
return all_goals_info
def get_challenge_suggestions(self, cr, uid, context=None):
@@ -171,7 +116,9 @@ class res_groups_gamification_group(osv.Model):
_name = 'res.groups'
_inherit = 'res.groups'
# No need to overwrite create as very unlikely to be the value in the autojoin_group_id field
def write(self, cr, uid, ids, vals, context=None):
"""Overwrite to autosubscribe users if add users to a group marked as autojoin, these will be added to the challenge"""
write_res = super(res_groups_gamification_group, self).write(cr, uid, ids, vals, context=context)
if vals.get('users'):
# form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]}
@@ -73,7 +73,7 @@
width: 280px;
border-radius: 2px;
}
.openerp .oe_mail_wall .oe_mail_wall_aside > .oe_gamification_goal {
.openerp .oe_mail_wall .oe_mail_wall_aside .oe_gamification_challenge_list {
background-color: #EDEDF6;
}
.openerp .oe_mail_wall_aside .oe_gamification_suggestion {
@@ -188,7 +188,7 @@
width: 24px;
padding-right: 5px;
}
.openerp .oe_mail_wall .oe_mail_wall_aside .oe_goal .oe_update_plan.oe_e,
.openerp .oe_mail_wall .oe_mail_wall_aside .oe_goal .oe_update_challenge.oe_e,
.openerp .oe_mail_wall .oe_mail_wall_aside .oe_goal .oe_goal_action.oe_e {
visibility: hidden;
font-size: 25px;
@@ -196,7 +196,7 @@
/*margin-right: 15px;*/
position: relative;
}
.openerp .oe_mail_wall .oe_mail_wall_aside .oe_goal div:hover > .oe_update_plan,
.openerp .oe_mail_wall .oe_mail_wall_aside .oe_goal div:hover > .oe_update_challenge,
.openerp .oe_mail_wall .oe_mail_wall_aside .oe_goal div:hover > .oe_goal_action,
.openerp .oe_mail_wall .oe_mail_wall_aside .oe_goal th:hover > .oe_goal_action {
visibility: visible;
@@ -2,12 +2,12 @@ openerp.gamification = function(instance) {
var QWeb = instance.web.qweb;
instance.gamification.Sidebar = instance.web.Widget.extend({
template: 'gamification.user_wall_sidebar',
template: 'gamification.UserWallSidebar',
init: function (parent, action) {
var self = this;
this._super(parent, action);
this.deferred = $.Deferred();
this.res_user = new instance.web.DataSetSearch(this, 'res.users');
// this.res_user = new instance.web.DataSetSearch(this, 'res.users');
this.goals_info = {};
this.challenge_suggestions = {};
},
@@ -51,54 +51,53 @@ openerp.gamification = function(instance) {
});
}
},
renderElement: function() {
start: function() {
var self = this;
this._super.apply(this, arguments);
self.get_goal_todo_info();
self.get_challenge_suggestions();
},
render_template: function(target,template) {
var self = this;
target.append(QWeb.render(template,{'widget': self}));
},
// render_template: function(target,template) {
// var self = this;
// target.append(QWeb.render(template,{'widget': self}));
// },
render_template_replace: function(target,template) {
var self = this;
target.html(QWeb.render(template,{'widget': self}));
},
get_goal_todo_info: function() {
var self = this;
var goals_info = this.res_user.call('get_goals_todo_info', {}).then(function(res) {
self.goals_info['info'] = res;
});
$.when(goals_info).done(function() {
if(self.goals_info.info.length > 0){
self.render_template_replace(self.$el.filter(".oe_gamification_goal"),'gamification.goal_list_to_do');
self.render_money_fields(self.goals_info.info[0].currency);
self.render_user_avatars();
var challenges = new instance.web.Model('res.users').call('get_serialised_gamification_summary', []).then(function(result) {
if (result.length === 0) {
self.$el.find(".oe_gamification_challenge_list").hide();
} else {
self.$el.filter(".oe_gamification_goal").hide();
_.each(result, function(item){
console.log(item);
var $item = $(QWeb.render("gamification.ChallengeSummary", {challenge: item}));
self.render_money_fields($item);
self.render_user_avatars($item);
self.$el.find('.oe_gamification_challenge_list').append($item);
});
}
});
},
get_challenge_suggestions: function() {
var self = this;
var challenge_suggestions = this.res_user.call('get_challenge_suggestions', {}).then(function(res) {
self.challenge_suggestions['info'] = res;
});
$.when(challenge_suggestions).done(function() {
if(self.challenge_suggestions.info.length > 0){
self.render_template_replace(self.$el.filter(".oe_gamification_suggestion"),'gamification.challenge_suggestions');
var challenge_suggestions = new instance.web.Model('res.users').call('get_challenge_suggestions', []).then(function(result) {
if (result.length === 0) {
self.$el.find(".oe_gamification_suggestion").hide();
} else {
self.$el.filter(".oe_gamification_suggestion").hide();
var $item = $(QWeb.render("gamification.ChallengeSuggestion", {challenges: result}));
self.$el.find('.oe_gamification_suggestion').append($item);
}
});
},
render_money_fields: function(currency_id) {
render_money_fields: function(item) {
var self = this;
self.dfm = new instance.web.form.DefaultFieldManager(self);
// Generate a FieldMonetary for each .oe_goal_field_monetary
self.$(".oe_goal_field_monetary").each(function() {
item.find(".oe_goal_field_monetary").each(function() {
var currency_id = parseInt( $(this).attr('data-id'), 10);
money_field = new instance.web.form.FieldMonetary(self.dfm, {
attrs: {
modifiers: '{"readonly": true}'
@@ -110,9 +109,9 @@ openerp.gamification = function(instance) {
money_field.replace($(this));
});
},
render_user_avatars: function() {
render_user_avatars: function(item) {
var self = this;
self.$(".oe_user_avatar").each(function() {
item.find(".oe_user_avatar").each(function() {
var user_id = parseInt( $(this).attr('data-id'), 10);
var url = instance.session.url('/web/binary/image', {model: 'res.users', field: 'image_small', id: user_id});
$(this).attr("src", url);
@@ -1,10 +1,12 @@
<templates>
<t t-name="gamification.user_wall_sidebar" class="oe_gamification_user_wall_sidebar">
<div class="oe_gamification_suggestion"><em>No challenge suggestions</em></div>
<div class="oe_gamification_goal"><em>Your goal list is empty</em></div>
<t t-name="gamification.UserWallSidebar" class="oe_gamification_user_wall_sidebar">
<div>
<div class="oe_gamification_suggestion"></div>
<div class="oe_gamification_challenge_list"></div>
</div>
</t>
<t t-name="gamification.goal_list_to_do">
<div t-foreach="widget.goals_info.info" t-as="challenge" class="oe_goal">
<t t-name="gamification.ChallengeSummary">
<div class="oe_goal">
<div>
<a class="oe_update_challenge oe_e" rol="button" t-attf-id="{challenge.id}">e</a>
<h4><t t-esc="challenge.name" /></h4>
@@ -12,29 +14,29 @@
<t t-if="challenge.visibility_mode == 'personal'">
<div class="oe_table oe_goals_list">
<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 t-foreach="challenge.lines" t-as="line" t-attf-class="oe_row oe_goal_outer_box #{line.state == 'reached' ? 'oe_goal_reached' : ''} #{line.display_mode != 'progress' ? 'oe_no_progress' : ''}">
<t t-if="line.display_mode == 'progress'">
<div class="oe_goal_progress_background"></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>
<div class="oe_goal_progress" t-attf-style="#{line.display_mode == 'progress' ? 'width: '+line.completeness+'%;' : 'width:0;'}"></div>
<div class="oe_cell oe_goal_current"><t t-esc="line.current" /></div>
</t>
<div class="oe_cell">
<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-if="line.computation_mode != 'manually' and !line.action">
<span t-att-title="line.description"><t t-esc="line.name" /></span>
</t>
<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-if="line.action or line.computation_mode == 'manually'">
<span t-att-title="line.description"><a class="oe_goal_action" t-att-id="line.id"><t t-esc="line.name" /></a></span>
</t>
<t t-if="goal.definition_display == 'progress'"><br/>
<t t-if="line.display_mode == 'progress'"><br/>
<div class="oe_grey">
<t t-if="goal.definition_condition == 'higher'">
<t t-if="line.definition_condition == 'higher'">
Target:
</t>
<t t-if="goal.definition_condition == 'lower'">
<t t-if="line.definition_condition == 'lower'">
Target: &lt;=
</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>
<span t-attf-class="#{line.monetary ? 'oe_goal_field_monetary' : ''}" t-attf-data-id="#{line.monetary ? challenge.currency : ''}"><t t-esc="line.target"/></span>
<t t-if="line.suffix"><t t-esc="line.suffix"/></t>
</div>
</t>
</div>
@@ -45,8 +47,8 @@
<t t-if="challenge.visibility_mode == 'ranking'">
<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="#{line.definition_description ? line.definition_description : ''}">
<strong><t t-esc="line.definition_name"/></strong>
<div class="oe_cell oe_thead" colspan="3" t-attf-title="#{line.description ? line.description : ''}">
<strong><t t-esc="line.name"/></strong>
<br/>
<div class="oe_grey">
<t t-if="line.definition_condition == 'higher'">
@@ -55,39 +57,39 @@
<t t-if="line.definition_condition == 'lower'">
Target: &lt;=
</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>
<span t-attf-class="#{line.monetary ? 'oe_goal_field_monetary' : ''}" t-attf-data-id="#{line.monetary ? challenge.currency : ''}"><t t-esc="line.target" /></span><t t-if="line.suffix"> <t t-esc="line.suffix"/></t>
</div>
</div>
</div>
<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 t-attf-class="oe_row oe_goal_outer_box #{goal.state == 'reached' ? 'oe_goal_reached' : ''} #{line.display_mode != 'progress' ? 'oe_no_progress' : ''}">
<t t-if="line.display_mode == 'progress'">
<div class="oe_goal_progress_background"></div>
<div class="oe_goal_progress" t-attf-style="#{line.definition_display == 'progress' ? 'width: '+goal.completeness+'%;' : 'width:0;'}"></div>
<div class="oe_goal_progress" t-attf-style="#{line.display_mode == '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 col1"><img class="oe_user_avatar" t-attf-alt="#{goal.name}" t-attf-data-id="#{goal.user_id}"/></div>
<div class="oe_cell col2">
<t t-if="line.definition_display == 'progress'">
<t t-if="line.display_mode == 'progress'">
<!-- progress, action on current value -->
<t t-esc="goal.user_name"/><br/>
<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-esc="goal.name"/><br/>
<t t-if="goal.id != line.own_goal_id or (line.computation_mode != 'manually' and !line.action)">
<span t-attf-class="#{line.monetary ? 'oe_goal_field_monetary' : ''}" t-attf-data-id="#{line.monetary ? challenge.currency : ''}"><t t-esc="goal.current" /></span><t t-if="line.suffix"> <t t-esc="line.suffix"/></t>
</t>
<t t-if="goal.id == line.own_goal_id and (line.definition_action or line.computation_mode == 'manually')">
<t t-if="goal.id == line.own_goal_id and (line.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>
<span t-attf-class="#{line.monetary ? 'oe_goal_field_monetary' : ''}" t-attf-data-id="#{line.monetary ? challenge.currency : ''}"><t t-esc="goal.current" /></span><t t-if="line.suffix"> <t t-esc="line.suffix"/></t>
</a>
</t>
</t>
<t t-if="line.definition_display != 'progress'">
<t t-if="line.display_mode != 'progress'">
<!-- not progress, action on user name -->
<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-if="goal.id != line.own_goal_id or (line.computation_mode != 'manually' and !line.action)">
<t t-esc="goal.name"/>
</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"><t t-esc="goal.user_name"/></a>
<t t-if="goal.id == line.own_goal_id and (line.action or line.computation_mode == 'manually')">
<a class="oe_goal_action" t-att-id="line.own_goal_id"><t t-esc="goal.name"/></a>
</t>
</t>
</div>
@@ -98,10 +100,10 @@
</div>
</t>
<t t-name="gamification.challenge_suggestions">
<t t-name="gamification.ChallengeSuggestion">
<div class="oe_goal">
<h4>Invited Challenges</h4>
<ul t-foreach="widget.challenge_suggestions.info" t-as="challenge" class="oe_goals_list">
<ul t-foreach="challenges" 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>
</li>
@@ -1,17 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<style type="text_css">${css}</style>
</head>
<body>
<p>Congratulation, you have received the badge <strong>${badge_user.badge_id.name}</strong> !
% if user_from
This badge was granted by <strong>${user_from.name}</strong>.
% endif
</p>
% if badge_user.comment
<p><em>${badge_user.comment}</em></p>
% endif
</body>
</html>
-13
View File
@@ -1,13 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<style type="text_css">${css}</style>
</head>
<body>
<header>
<strong>${object.name}</strong>
</header>
<p class="oe_grey">${object.report_header or ''}</p>
<% block body %><% endblock %>
</body>
@@ -1,38 +0,0 @@
<% extends 'base.mako' %>
<% block body %>
% for line in lines_boards:
<table width="100%" border="1">
<tr>
<th colspan="4">${line.goal_definition.name}</th>
</tr>
<tr>
<th>#</th>
<th>Person</th>
<th>Completeness</th>
<th>Current</th>
</tr>
% for idx, goal in line.board_goals:
% if idx < 3 or goal.user_id.id == uid:
<tr
% if goal.completeness >= 100:
style="font-weight:bold;"
% endif
>
<td>${idx+1}</td>
<td>${goal.user_id.name}</td>
<td>${goal.completeness}%</td>
<td>${goal.current}/${goal.target_goal}
% if goal.definition_suffix:
${goal.definition_suffix}
% endif
</td>
</tr>
% endif
% endfor
</table>
<br/><br/>
% endfor
<% endblock %>
@@ -1,31 +0,0 @@
<% extends 'base.mako' %>
<% block body %><table width="100%" border="1">
<tr>
<th>Goal</th>
<th>Target</th>
<th>Current</th>
<th>Completeness</th>
</tr>
% for goal in goals:
<tr
% if goal.completeness >= 100:
style="font-weight:bold;"
% endif
>
<td>${goal.definition_id.name}</td>
<td>${goal.target_goal}
% if goal.definition_suffix:
${goal.definition_suffix}
% endif
</td>
<td>${goal.current}
% if goal.definition_suffix:
${goal.definition_suffix}
% endif
</td>
<td>${goal.completeness} %</td>
</tr>
% endfor
</table>
<% endblock %>
@@ -1,17 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<style type="text_css">${css}</style>
</head>
<body>
<header>
<strong>Reminder ${object.name}</strong>
</header>
<p class="oe_grey">${object.report_header or ''}</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>
</html>
+5 -2
View File
@@ -76,7 +76,7 @@
<tree string="Line List" version="7.0" editable="bottom" >
<field name="sequence" widget="handle"/>
<field name="definition_id" on_change="on_change_definition_id(definition_id)" />
<field name="definition_condition"/>
<field name="condition"/>
<field name="target_goal"/>
<field name="definition_full_suffix"/>
</tree>
@@ -101,6 +101,9 @@
<field name="invited_user_ids" widget="many2many_tags" />
</group>
<group string="Notification Messages">
<div class="oe_grey">
<p>Depending on the Diasplay mode, reports will be individual or shared.</p>
</div>
<field name="report_message_frequency" />
<field name="report_header" placeholder="e.g. The following message contains the current progress of the sale team..." attrs="{'invisible': [('report_message_frequency','=','never')]}" />
<field name="report_message_group_id" attrs="{'invisible': [('report_message_frequency','=','never')]}" />
@@ -247,7 +250,7 @@
<tree string="Challenge Lines" version="7.0" editable="bottom" >
<field name="sequence" widget="handle"/>
<field name="definition_id"/>
<field name="definition_condition"/>
<field name="condition"/>
<field name="target_goal"/>
<field name="definition_full_suffix"/>
</tree>
+1 -1
View File
@@ -154,7 +154,7 @@
</div>
<field name="user_id" />
<div class="oe_goal_state_block">
<t t-if="record.definition_display.raw_value == 'checkbox'">
<t t-if="record.definition_display.raw_value == 'boolean'">
<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>