summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorMartin Trigaux <mat@openerp.com>2013-12-20 15:45:21 +0100
committerMartin Trigaux <mat@openerp.com>2013-12-20 15:45:21 +0100
commitf77aefc07ace66587bc13f7d2fa55755968437c5 (patch)
treeac8d29d536f2181258fb6cb6d76b5f8da8807afd /addons
parent90dfa5895d61fae037046aea2b64b25d9903df15 (diff)
[REF] gamification: cleaning methods and reports
bzr revid: mat@openerp.com-20131220144521-wyu1gx8bcitu6nk4
Diffstat (limited to 'addons')
-rw-r--r--addons/gamification/data/goal_base.xml64
-rw-r--r--addons/gamification/models/challenge.py215
-rw-r--r--addons/gamification/models/goal.py1
-rw-r--r--addons/gamification/models/res_users.py99
-rw-r--r--addons/gamification/static/src/css/gamification.css6
-rw-r--r--addons/gamification/static/src/js/gamification.js55
-rw-r--r--addons/gamification/static/src/xml/gamification.xml78
-rw-r--r--addons/gamification/templates/badge_received.mako17
-rw-r--r--addons/gamification/templates/base.mako13
-rw-r--r--addons/gamification/templates/group_progress.mako38
-rw-r--r--addons/gamification/templates/personal_progress.mako31
-rw-r--r--addons/gamification/templates/reminder.mako17
-rw-r--r--addons/gamification/views/challenge.xml7
-rw-r--r--addons/gamification/views/goal.xml2
14 files changed, 263 insertions, 380 deletions
diff --git a/addons/gamification/data/goal_base.xml b/addons/gamification/data/goal_base.xml
index 2b21fbc947f..48dd770bbcc 100644
--- a/addons/gamification/data/goal_base.xml
+++ b/addons/gamification/data/goal_base.xml
@@ -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>
diff --git a/addons/gamification/models/challenge.py b/addons/gamification/models/challenge.py
index 0f3e64626cb..7cbd784b9bf 100644
--- a/addons/gamification/models/challenge.py
+++ b/addons/gamification/models/challenge.py
@@ -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
-
- goals.append(goal)
+ line_data.update({
+ 'own_goal_id': False,
+ 'goals': [],
+ })
+ sorting = "completeness desc, current desc"
+ limit = False
+
+ 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"),
diff --git a/addons/gamification/models/goal.py b/addons/gamification/models/goal.py
index 5464b51a4ea..52eebb63857 100644
--- a/addons/gamification/models/goal.py
+++ b/addons/gamification/models/goal.py
@@ -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',
diff --git a/addons/gamification/models/res_users.py b/addons/gamification/models/res_users.py
index d831ab2ea25..408ddddf082 100644
--- a/addons/gamification/models/res_users.py
+++ b/addons/gamification/models/res_users.py
@@ -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):
+
+ def get_serialised_gamification_summary(self, cr, uid, context=None):
+ return self._serialised_goals_summary(cr, uid, user_id=uid, context=context)
- 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 _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]}
diff --git a/addons/gamification/static/src/css/gamification.css b/addons/gamification/static/src/css/gamification.css
index cc20afede80..fd39b130fad 100644
--- a/addons/gamification/static/src/css/gamification.css
+++ b/addons/gamification/static/src/css/gamification.css
@@ -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;
diff --git a/addons/gamification/static/src/js/gamification.js b/addons/gamification/static/src/js/gamification.js
index 9e34d8f09fc..cf5b2228b08 100644
--- a/addons/gamification/static/src/js/gamification.js
+++ b/addons/gamification/static/src/js/gamification.js
@@ -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);
diff --git a/addons/gamification/static/src/xml/gamification.xml b/addons/gamification/static/src/xml/gamification.xml
index 19c622dcb97..6316ca886f0 100644
--- a/addons/gamification/static/src/xml/gamification.xml
+++ b/addons/gamification/static/src/xml/gamification.xml
@@ -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>
diff --git a/addons/gamification/templates/badge_received.mako b/addons/gamification/templates/badge_received.mako
deleted file mode 100644
index b846ab7ff4f..00000000000
--- a/addons/gamification/templates/badge_received.mako
+++ /dev/null
@@ -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> \ No newline at end of file
diff --git a/addons/gamification/templates/base.mako b/addons/gamification/templates/base.mako
deleted file mode 100644
index d5bb0c24c99..00000000000
--- a/addons/gamification/templates/base.mako
+++ /dev/null
@@ -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> \ No newline at end of file
diff --git a/addons/gamification/templates/group_progress.mako b/addons/gamification/templates/group_progress.mako
deleted file mode 100644
index 5d71c36032f..00000000000
--- a/addons/gamification/templates/group_progress.mako
+++ /dev/null
@@ -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 %> \ No newline at end of file
diff --git a/addons/gamification/templates/personal_progress.mako b/addons/gamification/templates/personal_progress.mako
deleted file mode 100644
index 85d2fbb2b16..00000000000
--- a/addons/gamification/templates/personal_progress.mako
+++ /dev/null
@@ -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 %> \ No newline at end of file
diff --git a/addons/gamification/templates/reminder.mako b/addons/gamification/templates/reminder.mako
deleted file mode 100644
index 4d44861a0bc..00000000000
--- a/addons/gamification/templates/reminder.mako
+++ /dev/null
@@ -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>
diff --git a/addons/gamification/views/challenge.xml b/addons/gamification/views/challenge.xml
index 5403630b5b6..973194738cd 100644
--- a/addons/gamification/views/challenge.xml
+++ b/addons/gamification/views/challenge.xml
@@ -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>
diff --git a/addons/gamification/views/goal.xml b/addons/gamification/views/goal.xml
index dbb208ad5fd..d76b41882df 100644
--- a/addons/gamification/views/goal.xml
+++ b/addons/gamification/views/goal.xml
@@ -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>