From ec0c0f2973459fef8bca12e850af043c1ab90b99 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Tue, 20 Jan 2015 12:04:59 +0100 Subject: [PATCH] [IMP] gamification: performance improvement Reduce the number of goals that are recomputed. Remove the goals for users that did not connect since the last update. Add sql query for faster lookup and restrict on user table --- addons/gamification/models/challenge.py | 27 +++++++----- addons/gamification/models/goal.py | 56 +++++++++++++------------ 2 files changed, 46 insertions(+), 37 deletions(-) diff --git a/addons/gamification/models/challenge.py b/addons/gamification/models/challenge.py index c43b8404854..50f7059dc31 100644 --- a/addons/gamification/models/challenge.py +++ b/addons/gamification/models/challenge.py @@ -300,18 +300,23 @@ class gamification_challenge(osv.Model): goal_obj = self.pool.get('gamification.goal') - # we use yesterday to update the goals that just ended + # include yesterday goals to update the goals that just ended + # exclude goals for users that did not connect since the last update yesterday = date.today() - timedelta(days=1) - goal_ids = goal_obj.search(cr, uid, [ - ('challenge_id', 'in', ids), - '|', - ('state', '=', 'inprogress'), - '&', - ('state', 'in', ('reached', 'failed')), - '|', - ('end_date', '>=', yesterday.strftime(DF)), - ('end_date', '=', False) - ], context=context) + cr.execute("""SELECT gg.id + FROM gamification_goal as gg, + gamification_challenge as gc, + res_users as ru + WHERE gg.challenge_id = gc.id + AND gg.user_id = ru.id + AND gg.write_date < ru.login_date + AND gg.closed IS false + AND gc.id IN %s + AND (gg.state = 'inprogress' + OR (gg.state = 'reached' + AND (gg.end_date >= %s OR gg.end_date IS NULL))) + """, (tuple(ids), yesterday.strftime(DF))) + goal_ids = cr.fetchall() # update every running goal already generated linked to selected challenges goal_obj.update(cr, uid, goal_ids, context=context) diff --git a/addons/gamification/models/goal.py b/addons/gamification/models/goal.py index d85d2a765b0..ad37762d10b 100644 --- a/addons/gamification/models/goal.py +++ b/addons/gamification/models/goal.py @@ -268,6 +268,25 @@ class gamification_goal(osv.Model): return {'to_update': True} return {} + def _get_write_values(self, cr, uid, goal, new_value, context=None): + """Generate values to write after recomputation of a goal score""" + if new_value == goal.current: + # avoid useless write if the new value is the same as the old one + return {} + + result = {goal.id: {'current': new_value}} + if (goal.definition_id.condition == 'higher' and new_value >= goal.target_goal) \ + or (goal.definition_id.condition == 'lower' and new_value <= goal.target_goal): + # success, do no set closed as can still change + result[goal.id]['state'] = 'reached' + + elif goal.end_date and fields.date.today() > goal.end_date: + # check goal failure + result[goal.id]['state'] = 'failed' + result[goal.id]['closed'] = True + + return result + def update(self, cr, uid, ids, context=None): """Update the goals to recomputes values and change of states @@ -281,14 +300,8 @@ class gamification_goal(osv.Model): commit = context.get('commit_gamification', False) goals_by_definition = {} - all_goals = {} for goal in self.browse(cr, uid, ids, context=context): - if goal.state in ('draft', 'canceled'): - # draft or canceled goals should not be recomputed - continue - goals_by_definition.setdefault(goal.definition_id, []).append(goal) - all_goals[goal.id] = goal for definition, goals in goals_by_definition.items(): goals_to_write = dict((goal.id, {}) for goal in goals) @@ -313,8 +326,10 @@ class gamification_goal(osv.Model): # the result of the evaluated codeis put in the 'result' local variable, propagated to the context result = cxt.get('result') if result is not None and type(result) in (float, int, long): - if result != goal.current: - goals_to_write[goal.id]['current'] = result + goals_to_write.update( + self._get_write_values(cr, uid, goal, result, context=context) + ) + else: _logger.exception(_('Invalid return content from the evaluation of code for definition %s') % definition.name) @@ -356,8 +371,9 @@ class gamification_goal(osv.Model): queried_value = queried_value[0] if queried_value == query_goals[goal.id]: new_value = user_value.get(field_name+'_count', goal.current) - if new_value != goal.current: - goals_to_write[goal.id]['current'] = new_value + goals_to_write.update( + self._get_write_values(cr, uid, goal, new_value, context=context) + ) else: for goal in goals: @@ -379,26 +395,14 @@ class gamification_goal(osv.Model): else: # computation mode = count new_value = obj.search(cr, uid, domain, context=context, count=True) - # avoid useless write if the new value is the same as the old one - if new_value != goal.current: - goals_to_write[goal.id]['current'] = new_value + goals_to_write.update( + self._get_write_values(cr, uid, goal, new_value, context=context) + ) 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_id.condition == 'higher' and value.get('current', goal.current) >= goal.target_goal) \ - or (goal.definition_id.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) + self.write(cr, uid, [goal_id], value, context=context) if commit: cr.commit() return True