diff --git a/addons/gamification/data/goal_base.xml b/addons/gamification/data/goal_base.xml index 0437dee031c..c12763025f6 100644 --- a/addons/gamification/data/goal_base.xml +++ b/addons/gamification/data/goal_base.xml @@ -164,7 +164,7 @@ once personal never - [('groups_id', 'in', ref('base.group_user'))] + inprogress other @@ -174,7 +174,7 @@ once personal never - [('groups_id', 'in', ref('base.user_root'))] + inprogress other diff --git a/addons/gamification/models/challenge.py b/addons/gamification/models/challenge.py index 081127d6bf0..a0c4b638ae3 100644 --- a/addons/gamification/models/challenge.py +++ b/addons/gamification/models/challenge.py @@ -21,14 +21,13 @@ from openerp import SUPERUSER_ID from openerp.osv import fields, osv -from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as DF +from openerp.tools import ustr, DEFAULT_SERVER_DATE_FORMAT as DF from openerp.tools.safe_eval import safe_eval as eval from openerp.tools.translate import _ from datetime import date, datetime, timedelta import calendar import logging -import functools _logger = logging.getLogger(__name__) # display top 3 in ranking, could be db variable @@ -117,12 +116,6 @@ class gamification_challenge(osv.Model): except ValueError: return False - def _get_challenger_users(self, cr, uid, domain, context=None): - ref = functools.partial(self.pool['ir.model.data'].xmlid_to_res_id, cr, uid) - user_domain = eval(domain, {'ref': ref}) - return self.pool['res.users'].search(cr, uid, user_domain, context=context) - - _order = 'end_date, start_date, name, id' _columns = { 'name': fields.char('Challenge Name', required=True, translate=True), @@ -218,7 +211,6 @@ class gamification_challenge(osv.Model): def create(self, cr, uid, vals, context=None): """Overwrite the create method to add the user of groups""" - # add users when change the group auto-subscription if vals.get('user_domain'): user_ids = self._get_challenger_users(cr, uid, vals.get('user_domain'), context=context) @@ -240,14 +232,18 @@ class gamification_challenge(osv.Model): if isinstance(ids, (int,long)): ids = [ids] - if vals.get('state') == 'inprogress': - for challenge in self.browse(cr, uid, ids, context=context): - user_ids = self._get_challenger_users(cr, uid, challenge.user_domain, context=context) - write_op = [(4, user_id) for user_id in user_ids] - self.write(cr, uid, [challenge.id], {'user_ids': write_op}, context=context) - self.message_subscribe_users(cr, uid, [challenge.id], user_ids, context=context) + if vals.get('user_domain'): + user_ids = self._get_challenger_users(cr, uid, vals.get('user_domain'), context=context) - self.generate_goals_from_challenge(cr, uid, ids, context=context) + if not vals.get('user_ids'): + vals['user_ids'] = [] + vals['user_ids'] += [(4, user_id) for user_id in user_ids] + + write_res = super(gamification_challenge, self).write(cr, uid, ids, vals, 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) elif vals.get('state') == 'done': self.check_challenge_reward(cr, uid, ids, force=True, context=context) @@ -256,9 +252,6 @@ class gamification_challenge(osv.Model): # resetting progress if self.pool.get('gamification.goal').search(cr, uid, [('challenge_id', 'in', ids), ('state', '=', 'inprogress')], context=context): raise osv.except_osv("Error", "You can not reset a challenge with unfinished goals.") - - write_res = super(gamification_challenge, self).write(cr, uid, ids, vals, context=context) - return write_res @@ -314,18 +307,10 @@ class gamification_challenge(osv.Model): # update every running goal already generated linked to selected challenges goal_obj.update(cr, uid, goal_ids, context=context) + self._recompute_challenge_users(cr, uid, ids, context=context) + self._generate_goals_from_challenge(cr, uid, ids, context=context) + for challenge in self.browse(cr, uid, ids, context=context): - # in case of new users matching the domain - old_user_ids = [user.id for user in challenge.user_ids] - new_user_ids = self._get_challenger_users(cr, uid, challenge.user_domain, context=context) - to_remove_ids = list(set(old_user_ids) - set(new_user_ids)) - to_add_ids = list(set(new_user_ids) - set(old_user_ids)) - - write_op = [(3, user_id) for user_id in to_remove_ids] - write_op += [(4, user_id) for user_id in to_add_ids] - self.write(cr, uid, [challenge.id], {'user_ids': write_op}, context=context) - - self.generate_goals_from_challenge(cr, uid, [challenge.id], context=context) # goals closed but still opened at the last report date closed_goals_to_report = goal_obj.search(cr, uid, [ @@ -345,11 +330,37 @@ class gamification_challenge(osv.Model): return True def quick_update(self, cr, uid, challenge_id, context=None): - """Update all the goals of a challenge, no generation of new goals""" + """Update all the goals of a specific challenge, no generation of new goals""" goal_ids = self.pool.get('gamification.goal').search(cr, uid, [('challenge_id', '=', challenge_id)], context=context) self.pool.get('gamification.goal').update(cr, uid, goal_ids, context=context) return True + def _get_challenger_users(self, cr, uid, domain, context=None): + user_domain = eval(ustr(domain)) + return self.pool['res.users'].search(cr, uid, user_domain, context=context) + + def _recompute_challenge_users(self, cr, uid, challenge_ids, context=None): + """Recompute the domain to add new users and remove the one no longer matching the domain""" + for challenge in self.browse(cr, uid, challenge_ids, context=context): + if challenge.user_domain: + + old_user_ids = [user.id for user in challenge.user_ids] + new_user_ids = self._get_challenger_users(cr, uid, challenge.user_domain, context=context) + to_remove_ids = list(set(old_user_ids) - set(new_user_ids)) + to_add_ids = list(set(new_user_ids) - set(old_user_ids)) + + write_op = [(3, user_id) for user_id in to_remove_ids] + write_op += [(4, user_id) for user_id in to_add_ids] + 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) + + return True + def action_check(self, cr, uid, ids, context=None): """Check a challenge @@ -370,6 +381,10 @@ class gamification_challenge(osv.Model): ##### Automatic actions ##### def generate_goals_from_challenge(self, cr, uid, ids, context=None): + _logger.warning("Deprecated, use private method _generate_goals_from_challenge(...) instead.") + return self._generate_goals_from_challenge(cr, uid, ids, context=context) + + def _generate_goals_from_challenge(self, cr, uid, ids, context=None): """Generate the goals for each line and user. If goals already exist for this line and user, the line is skipped. This @@ -388,44 +403,49 @@ class gamification_challenge(osv.Model): end_date = challenge.end_date for line in challenge.line_ids: - # FIXME: allow to restrict to a subset of users - for user in challenge.user_ids: - domain = [('line_id', '=', line.id), ('user_id', '=', user.id)] - if start_date: - domain.append(('start_date', '=', start_date)) + # there is potentially a lot of users + # detect the ones with no goal linked to this line + date_clause = "" + query_params = [line.id] + if start_date: + date_clause += "AND g.start_date = %s" + query_params.append(start_date) + if end_date: + date_clause += "AND g.end_date = %s" + query_params.append(end_date) + + query = """SELECT u.id AS user_id + FROM res_users u + LEFT JOIN gamification_goal g + ON (u.id = g.user_id) + WHERE line_id = %s + {date_clause} + """.format(date_clause=date_clause) - # goal already existing for this line ? - if len(goal_obj.search(cr, uid, domain, context=context)) > 0: + cr.execute(query, query_params) + user_with_goal_ids = cr.dictfetchall() + user_without_goal_ids = list(set([user.id for user in challenge.user_ids]) - set([user['user_id'] for user in user_with_goal_ids])) - # resume canceled goals - domain.append(('state', '=', 'canceled')) - canceled_goal_ids = goal_obj.search(cr, uid, domain, context=context) - if canceled_goal_ids: - goal_obj.write(cr, uid, canceled_goal_ids, {'state': 'inprogress'}, context=context) - to_update.extend(canceled_goal_ids) + values = { + 'definition_id': line.definition_id.id, + 'line_id': line.id, + 'target_goal': line.target_goal, + 'state': 'inprogress', + } - # skip to next user - continue + if start_date: + values['start_date'] = start_date + if end_date: + values['end_date'] = end_date - values = { - 'definition_id': line.definition_id.id, - 'line_id': line.id, - 'user_id': user.id, - 'target_goal': line.target_goal, - 'state': 'inprogress', - } + if challenge.remind_update_delay: + values['remind_update_delay'] = challenge.remind_update_delay - if start_date: - values['start_date'] = start_date - if end_date: - values['end_date'] = end_date - - if challenge.remind_update_delay: - values['remind_update_delay'] = challenge.remind_update_delay - - new_goal_id = goal_obj.create(cr, uid, values, context=context) - to_update.append(new_goal_id) + for user_id in user_without_goal_ids: + values.update({'user_id': user_id}) + goal_id = goal_obj.create(cr, uid, values, context=context) + to_update.append(goal_id) goal_obj.update(cr, uid, to_update, context=context) @@ -638,7 +658,7 @@ class gamification_challenge(osv.Model): message = "%s has joined the challenge" % user.name self.message_post(cr, SUPERUSER_ID, challenge_ids, body=message, 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, SUPERUSER_ID, challenge_ids, context=context) + return self._generate_goals_from_challenge(cr, SUPERUSER_ID, challenge_ids, context=context) # TODO in trunk, remove unused parameter user_id def discard_challenge(self, cr, uid, challenge_ids, context=None, user_id=None): diff --git a/addons/gamification/models/res_users.py b/addons/gamification/models/res_users.py index 330311ba05e..67020520467 100644 --- a/addons/gamification/models/res_users.py +++ b/addons/gamification/models/res_users.py @@ -19,7 +19,6 @@ # ############################################################################## -from openerp import SUPERUSER_ID from openerp.osv import osv from challenge import MAX_VISIBILITY_RANKING diff --git a/addons/gamification/views/challenge.xml b/addons/gamification/views/challenge.xml index 75dcd4a7959..87dea680222 100644 --- a/addons/gamification/views/challenge.xml +++ b/addons/gamification/views/challenge.xml @@ -48,6 +48,7 @@