[REF] gamification: performance improvements when running with a large number of users

- gamification.goal do not depends of mail.thread anymore (message posting and fields tracking was slow)
- check reward works in batch mode
- fix update method that did not reset variable in loop
- when the cron is running commit after each goal definition update

bzr revid: mat@openerp.com-20140506145623-476s3q3vxoybya77
This commit is contained in:
Martin Trigaux
2014-05-06 16:56:23 +02:00
3 changed files with 51 additions and 41 deletions
+32 -18
View File
@@ -240,6 +240,11 @@ class gamification_challenge(osv.Model):
write_res = super(gamification_challenge, self).write(cr, uid, ids, vals, context=context)
if vals.get('report_message_frequency', 'never') != 'never':
# _recompute_challenge_users do not set users for challenges with no reports, subscribing them now
for challenge in self.browse(cr, uid, ids, context=context):
self.message_subscribe(cr, uid, [challenge.id], [user.partner_id.id for user in challenge.user_ids], context=context)
if vals.get('state') == 'inprogress':
self._recompute_challenge_users(cr, uid, ids, context=context)
self._generate_goals_from_challenge(cr, uid, ids, context=context)
@@ -264,6 +269,9 @@ class gamification_challenge(osv.Model):
- Create the missing goals (eg: modified the challenge to add lines)
- Update every running challenge
"""
if context is None:
context = {}
# start scheduled challenges
planned_challenge_ids = self.search(cr, uid, [
('state', '=', 'draft'),
@@ -281,6 +289,9 @@ class gamification_challenge(osv.Model):
if not ids:
ids = self.search(cr, uid, [('state', '=', 'inprogress')], context=context)
# in cron mode, will do intermediate commits
# TODO in trunk: replace by parameter
context.update({'commit_gamification': True})
return self._update_all(cr, uid, ids, context=context)
def _update_all(self, cr, uid, ids, context=None):
@@ -355,10 +366,12 @@ class gamification_challenge(osv.Model):
if write_op:
self.write(cr, uid, [challenge.id], {'user_ids': write_op}, context=context)
if to_remove_ids:
self.message_unsubscribe_users(cr, uid, [challenge.id], to_remove_ids, context=None)
if to_add_ids:
self.message_subscribe_users(cr, uid, [challenge.id], to_add_ids, context=context)
# TODO for trunk: use proper parameter
if challenge.report_message_frequency != 'never':
if to_remove_ids:
self.message_unsubscribe_users(cr, uid, [challenge.id], to_remove_ids, context=context)
if to_add_ids:
self.message_subscribe_users(cr, uid, [challenge.id], to_add_ids, context=context)
return True
@@ -393,9 +406,9 @@ class gamification_challenge(osv.Model):
:param list(int) ids: the list of challenge concerned"""
goal_obj = self.pool.get('gamification.goal')
to_update = []
for challenge in self.browse(cr, uid, ids, context=context):
(start_date, end_date) = start_end_date_for_period(challenge.period)
to_update = []
# if no periodicity, use challenge dates
if not start_date and challenge.start_date:
@@ -698,34 +711,35 @@ class gamification_challenge(osv.Model):
rewarded_users = []
challenge_ended = end_date == yesterday.strftime(DF) or force
if challenge.reward_id and challenge_ended or challenge.reward_realtime:
for user in challenge.user_ids:
reached_goal_ids = self.pool.get('gamification.goal').search(cr, uid, [
('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(challenge.line_ids):
# not using start_date as intemportal goals have a start date but no end_date
reached_goals = self.pool.get('gamification.goal').read_group(cr, uid, [
('challenge_id', '=', challenge.id),
('end_date', '=', end_date),
('state', '=', 'reached')
], fields=['user_id'], groupby=['user_id'], context=context)
for reach_goals_user in reached_goals:
if reach_goals_user['user_id_count'] == len(challenge.line_ids):
# the user has succeeded every assigned goal
user_id = reach_goals_user['user_id'][0]
if challenge.reward_realtime:
badges = self.pool['gamification.badge.user'].search(cr, uid, [
('challenge_id', '=', challenge.id),
('badge_id', '=', challenge.reward_id.id),
('user_id', '=', user.id),
('user_id', '=', user_id),
], count=True, context=context)
if badges > 0:
# has already recieved the badge for this challenge
continue
self.reward_user(cr, uid, user.id, challenge.reward_id.id, challenge.id, context=context)
rewarded_users.append(user)
self.reward_user(cr, uid, user_id, challenge.reward_id.id, challenge.id, context=context)
rewarded_users.append(user_id)
if challenge_ended:
# open chatter message
message_body = _("The challenge %s is finished." % challenge.name)
if 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])))
user_names = self.pool['res.users'].name_get(cr, uid, rewarded_users, context=context)
message_body += _("<br/>Reward (badge %s) for every succeeding user was sent to %s." % (challenge.reward_id.name, ", ".join([name for (user_id, name) in user_names])))
else:
message_body += _("<br/>Nobody has succeeded to reach every goal, no badge is rewared for this challenge.")
+19 -19
View File
@@ -138,7 +138,6 @@ class gamification_goal(osv.Model):
_name = 'gamification.goal'
_description = 'Gamification goal instance'
_inherit = 'mail.thread'
def _get_completion(self, cr, uid, ids, field_name, arg, context=None):
"""Return the percentage of completeness of the goal, between 0 and 100"""
@@ -226,8 +225,7 @@ class gamification_goal(osv.Model):
temp_obj = self.pool.get('email.template')
template_id = self.pool['ir.model.data'].get_object(cr, uid, 'gamification', 'email_template_goal_reminder', context)
body_html = temp_obj.render_template(cr, uid, template_id.body_html, 'gamification.goal', goal.id, context=context)
self.message_post(cr, uid, goal.id, body=body_html, partner_ids=[goal.user_id.partner_id.id], context=context, subtype='mail.mt_comment')
self.pool['mail.thread'].message_post(cr, uid, 0, body=body_html, partner_ids=[goal.user_id.partner_id.id], context=context, subtype='mail.mt_comment')
return {'to_update': True}
return {}
@@ -241,9 +239,9 @@ class gamification_goal(osv.Model):
the target value being reached, the goal is set as failed."""
if context is None:
context = {}
commit = context.get('commit_gamification', False)
goals_by_definition = {}
goals_to_write = {}
all_goals = {}
for goal in self.browse(cr, uid, ids, context=context):
if goal.state in ('draft', 'canceled'):
@@ -251,10 +249,10 @@ class gamification_goal(osv.Model):
continue
goals_by_definition.setdefault(goal.definition_id, []).append(goal)
goals_to_write[goal.id] = {}
all_goals[goal.id] = goal
for definition, goals in goals_by_definition.items():
goals_to_write = dict((goal.id, {}) for goal in goals)
if definition.computation_mode == 'manually':
for goal in goals:
goals_to_write[goal.id].update(self._check_remind_delay(cr, uid, goal, context))
@@ -345,22 +343,24 @@ class gamification_goal(osv.Model):
if new_value != goal.current:
goals_to_write[goal.id]['current'] = new_value
for goal_id, value in goals_to_write.items():
if not value:
continue
goal = all_goals[goal_id]
for goal_id, value in goals_to_write.items():
if not value:
continue
goal = all_goals[goal_id]
# check goal target reached
if (goal.definition_condition == 'higher' and value.get('current', goal.current) >= goal.target_goal) \
or (goal.definition_condition == 'lower' and value.get('current', goal.current) <= goal.target_goal):
value['state'] = 'reached'
# check goal target reached
if (goal.definition_condition == 'higher' and value.get('current', goal.current) >= goal.target_goal) \
or (goal.definition_condition == 'lower' and value.get('current', goal.current) <= goal.target_goal):
value['state'] = 'reached'
# check goal failure
elif goal.end_date and fields.date.today() > goal.end_date:
value['state'] = 'failed'
value['closed'] = True
if value:
self.write(cr, uid, [goal.id], value, context=context)
# check goal failure
elif goal.end_date and fields.date.today() > goal.end_date:
value['state'] = 'failed'
value['closed'] = True
if value:
self.write(cr, uid, [goal.id], value, context=context)
if commit:
cr.commit()
return True
def action_start(self, cr, uid, ids, context=None):
-4
View File
@@ -86,10 +86,6 @@
</group>
</group>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids" widget="mail_followers"/>
<field name="message_ids" widget="mail_thread"/>
</div>
</form>
</field>
</record>