[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
This commit is contained in:
Martin Trigaux 2015-01-20 12:04:59 +01:00
parent fb8dc5793b
commit ec0c0f2973
2 changed files with 46 additions and 37 deletions

View File

@ -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)

View File

@ -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