diff --git a/addons/gamification/__init__.py b/addons/gamification/__init__.py index 843121ee65b..d644a612108 100644 --- a/addons/gamification/__init__.py +++ b/addons/gamification/__init__.py @@ -19,8 +19,5 @@ # ############################################################################## -import goal -import goal_type_data -import plan -import res_users -import badge +import models +import data diff --git a/addons/gamification/__openerp__.py b/addons/gamification/__openerp__.py index 081908fd3e4..0bf31601876 100644 --- a/addons/gamification/__openerp__.py +++ b/addons/gamification/__openerp__.py @@ -30,7 +30,7 @@ Gamification process The Gamification module provides ways to evaluate and motivate the users of OpenERP. The users can be evaluated using goals and numerical objectives to reach. -**Goals** are assigned through **plans** to evaluate and compare members of a team with each others and through time. +**Goals** are assigned through **challenges** to evaluate and compare members of a team with each others and through time. For non-numerical achievements, **badges** can be granted to users. From a simple "thank you" to an exceptional achievement, a badge is an easy way to exprimate gratitude to a user for their good work. @@ -38,24 +38,23 @@ Both goals and badges are flexibles and can be adapted to a large range of modul """, 'data': [ - 'plan_view.xml', - 'badge_view.xml', - 'goal_view.xml', - 'cron.xml', + 'views/challenge.xml', + 'views/badge.xml', + 'views/goal.xml', + 'data/cron.xml', 'security/gamification_security.xml', 'security/ir.model.access.csv', - 'goal_base_data.xml', - 'badge_data.xml', + 'data/goal_base.xml', + 'data/badge.xml', ], 'test': [ 'test/goal_demo.yml' ], 'installable': True, 'application': True, - 'auto_install': True, + 'auto_install': False, + 'css': ['static/src/css/gamification.css'], - 'js': [ - 'static/src/js/gamification.js', - ], + 'js': ['static/src/js/gamification.js',], 'qweb': ['static/src/xml/gamification.xml'], } diff --git a/addons/gamification/data/__init__.py b/addons/gamification/data/__init__.py new file mode 100644 index 00000000000..b5649616b2c --- /dev/null +++ b/addons/gamification/data/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013 OpenERP SA (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import goal_definition diff --git a/addons/gamification/badge_data.xml b/addons/gamification/data/badge.xml similarity index 100% rename from addons/gamification/badge_data.xml rename to addons/gamification/data/badge.xml diff --git a/addons/gamification/cron.xml b/addons/gamification/data/cron.xml similarity index 67% rename from addons/gamification/cron.xml rename to addons/gamification/data/cron.xml index 52726176841..8585771c97d 100644 --- a/addons/gamification/cron.xml +++ b/addons/gamification/data/cron.xml @@ -1,14 +1,14 @@ - - Run Goal Plan Checker + Run Goal Challenge Checker 1 days -1 - gamification.goal.plan + gamification.challenge _cron_update () diff --git a/addons/gamification/goal_base_data.xml b/addons/gamification/data/goal_base.xml similarity index 75% rename from addons/gamification/goal_base_data.xml rename to addons/gamification/data/goal_base.xml index 48bc562effe..60000ec2805 100644 --- a/addons/gamification/goal_base_data.xml +++ b/addons/gamification/data/goal_base.xml @@ -2,8 +2,8 @@ - - + + Set your Timezone Configure your profile and specify your timezone count @@ -14,7 +14,7 @@ user.id - + Set your Avatar In your user preference manually @@ -25,7 +25,7 @@ - + Set your Company Data Write some information about your company (specify at least a name) count @@ -37,7 +37,7 @@ user.company_id.id - @@ -141,7 +141,7 @@

${object.report_header or ''}

-

You have not updated your progress for the goal ${object.type_id.name} (currently reached at ${object.completeness}%) for at least ${object.remind_update_delay} days. Do not forget to do it.

+

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.

If you have not changed your score yet, you can use the button "The current value is up to date" to indicate so.

]]> @@ -168,15 +168,15 @@ style="font-weight:bold;" % endif > - ${goal.type_id.name} + ${goal.definition_id.name} ${goal.target_goal} - % if goal.type_suffix: - ${goal.type_suffix} + % if goal.definition_suffix: + ${goal.definition_suffix} % endif ${goal.current} - % if goal.type_suffix: - ${goal.type_suffix} + % if goal.definition_suffix: + ${goal.definition_suffix} % endif ${goal.completeness} % @@ -193,10 +193,10 @@

${object.report_header or ''}

- % for planline in ctx['planlines_boards']: + % for line in ctx['lines_boards']: - + @@ -204,7 +204,7 @@ - % for idx, goal in planline.board_goals: + % for idx, goal in line.board_goals: % if idx < 3 or goal.user_id.id == user.id: = 100: @@ -215,8 +215,8 @@ diff --git a/addons/gamification/goal_type_data.py b/addons/gamification/data/goal_definition.py similarity index 89% rename from addons/gamification/goal_type_data.py rename to addons/gamification/data/goal_definition.py index 3fef79f9a79..937168fc0a2 100644 --- a/addons/gamification/goal_type_data.py +++ b/addons/gamification/data/goal_definition.py @@ -22,14 +22,14 @@ from openerp.osv import osv -class gamification_goal_type_data(osv.Model): - """Goal type data +class gamification_goal_definition_data(osv.Model): + """Goal definition data Methods for more complex goals not possible with the 'sum' and 'count' mode. Each method should return the value that will be set in the 'current' field - of a user's goal. The return type must be a float or integer. + of a user's goal. The return definition must be a float or integer. """ - _inherit = 'gamification.goal.type' + _inherit = 'gamification.goal.definition' def number_following(self, cr, uid, xml_id="mail.thread", context=None): """Return the number of 'xml_id' objects the user is following diff --git a/addons/gamification/models/__init__.py b/addons/gamification/models/__init__.py new file mode 100644 index 00000000000..812a08b1e19 --- /dev/null +++ b/addons/gamification/models/__init__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013 OpenERP SA (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import goal +import challenge +import res_users +import badge diff --git a/addons/gamification/badge.py b/addons/gamification/models/badge.py similarity index 98% rename from addons/gamification/badge.py rename to addons/gamification/models/badge.py index 8e09a2a397b..6aa2f89e74e 100644 --- a/addons/gamification/badge.py +++ b/addons/gamification/models/badge.py @@ -141,11 +141,11 @@ class gamification_badge(osv.Model): 'remaining_sending': fields.function(_remaining_sending_calc, type='integer', string='Remaining Sending Allowed', help="If a maxium is set"), - 'plan_ids': fields.one2many('gamification.goal.plan', 'reward_id', + 'challenge_ids': fields.one2many('gamification.challenge', 'reward_id', string="Reward of Challenges"), - 'goal_type_ids': fields.many2many('gamification.goal.type', - string='Goals Linked', + 'goal_definition_ids': fields.many2many('gamification.goal.definition', 'badge_unlocked_definition_rel', + string='Rewarded by', help="The users that have succeeded theses goals will receive automatically the badge."), 'owner_ids': fields.one2many('gamification.badge.user', 'badge_id', diff --git a/addons/gamification/plan.py b/addons/gamification/models/challenge.py similarity index 61% rename from addons/gamification/plan.py rename to addons/gamification/models/challenge.py index 80631518b3d..f0873a368d7 100644 --- a/addons/gamification/plan.py +++ b/addons/gamification/models/challenge.py @@ -61,19 +61,19 @@ def start_end_date_for_period(period, default_start_date=False, default_end_date return (start_date, end_date) -class gamification_goal_plan(osv.Model): - """Gamification goal plan +class gamification_challenge(osv.Model): + """Gamification challenge - Set of predifined goals to be able to automate goal settings or - quickly apply several goals manually to a group of users + Set of predifined objectives assigned to people with rules for recurrence and + rewards If 'user_ids' is defined and 'period' is different than 'one', the set will be assigned to the users for each period (eg: every 1st of each month if 'monthly' is selected) """ - _name = 'gamification.goal.plan' - _description = 'Gamification goal plan' + _name = 'gamification.challenge' + _description = 'Gamification challenge' _inherit = 'mail.thread' def _get_next_report_date(self, cr, uid, ids, field_name, arg, context=None): @@ -82,32 +82,27 @@ class gamification_goal_plan(osv.Model): :return: a string in DEFAULT_SERVER_DATE_FORMAT representing the date""" res = {} - for plan in self.browse(cr, uid, ids, context): - last = datetime.strptime(plan.last_report_date, DF).date() - if plan.report_message_frequency == 'daily': + for challenge in self.browse(cr, uid, ids, context): + last = datetime.strptime(challenge.last_report_date, DF).date() + if challenge.report_message_frequency == 'daily': next = last + timedelta(days=1) - res[plan.id] = next.strftime(DF) - elif plan.report_message_frequency == 'weekly': + res[challenge.id] = next.strftime(DF) + elif challenge.report_message_frequency == 'weekly': next = last + timedelta(days=7) - res[plan.id] = next.strftime(DF) - elif plan.report_message_frequency == 'monthly': + res[challenge.id] = next.strftime(DF) + elif challenge.report_message_frequency == 'monthly': month_range = calendar.monthrange(last.year, last.month) next = last.replace(day=month_range[1]) + timedelta(days=1) - res[plan.id] = next.strftime(DF) - elif plan.report_message_frequency == 'yearly': - res[plan.id] = last.replace(year=last.year + 1).strftime(DF) + res[challenge.id] = next.strftime(DF) + elif challenge.report_message_frequency == 'yearly': + res[challenge.id] = last.replace(year=last.year + 1).strftime(DF) # frequency == 'once', reported when closed only else: - res[plan.id] = False + res[challenge.id] = False return res - def _planline_count(self, cr, uid, ids, field_name, arg, context=None): - res = dict.fromkeys(ids, 0) - for plan in self.browse(cr, uid, ids, context): - res[plan.id] = len(plan.planline_ids) - return res - + _sort = 'end_date, start_date, name, id' _columns = { 'name': fields.char('Challenge Name', required=True, translate=True), 'description': fields.text('Description', translate=True), @@ -123,10 +118,10 @@ class gamification_goal_plan(osv.Model): 'user_ids': fields.many2many('res.users', 'user_ids', string='Users', - help="List of users to which the goal will be set"), + help="List of users participating to the challenge"), 'autojoin_group_id': fields.many2one('res.groups', string='Auto-subscription Group', - help='Group of users whose members will automatically be added to the users'), + help='Group of users whose members will be automatically added to user_ids once the challenge is started'), 'period': fields.selection([ ('once', 'Non recurring'), @@ -143,14 +138,13 @@ class gamification_goal_plan(osv.Model): 'end_date': fields.date('End Date', help="The day a new challenge will be automatically closed. If no periodicity is set, will use this date as the goal end date."), - 'proposed_user_ids': fields.many2many('res.users', 'proposed_user_ids', + 'invited_user_ids': fields.many2many('res.users', 'invited_user_ids', string="Suggest to users"), - 'planline_ids': fields.one2many('gamification.goal.planline', 'plan_id', - string='Planline', + 'line_ids': fields.one2many('gamification.challenge.line', 'challenge_id', + string='Lines', help="List of goals that will be set", required=True), - 'planline_count': fields.function(_planline_count, type='integer', string="Planlines"), 'reward_id': fields.many2one('gamification.badge', string="For Every Succeding User"), 'reward_first_id': fields.many2one('gamification.badge', string="For 1st user"), @@ -202,70 +196,53 @@ class gamification_goal_plan(osv.Model): 'reward_failure': False, } - _sort = 'end_date, start_date, name' - def write(self, cr, uid, ids, vals, context=None): """Overwrite the write method to add the user of groups""" context = context or {} if not ids: return True - # unsubscribe removed users from the plan - # users are not able to manually unsubscribe to challenges so should - # do it for them when not concerned anymore - if vals.get('user_ids'): - for action_tuple in vals['user_ids']: - if action_tuple[0] == 3: - # form (3, ID), remove one - self.message_unsubscribe_users(cr, uid, ids, [action_tuple[1]], context=context) - if action_tuple[0] == 5: - # form (5,), remove all - for plan in self.browse(cr, uid, ids, context=context): - self.message_unsubscribe_users(cr, uid, [plan.id], [user.id for user in plan.user_ids], context=context) - if action_tuple[0] == 6: - # form (6, False, [IDS]), replace by IDS - for plan in self.browse(cr, uid, ids, context=context): - removed_users = set([user.id for user in plan.user_ids]) - set(action_tuple[2]) - self.message_unsubscribe_users(cr, uid, [plan.id], list(removed_users), context=context) - - write_res = super(gamification_goal_plan, self).write(cr, uid, ids, vals, context=context) - # add users when change the group auto-subscription - if 'autojoin_group_id' in vals: + if vals.get('autojoin_group_id'): new_group = self.pool.get('res.groups').browse(cr, uid, vals['autojoin_group_id'], context=context) - group_user_ids = [user.id for user in new_group.users] - for plan in self.browse(cr, uid, ids, context=context): - self.write(cr, uid, [plan.id], {'user_ids': [(4, user) for user in group_user_ids]}, context=context) - # subscribe new users to the plan - if 'user_ids' in vals: - for plan in self.browse(cr, uid, ids, context=context): - self.message_subscribe_users(cr, uid, ids, [user.id for user in plan.user_ids], context=context) + if 'user_ids' not in vals: + vals['user_ids'] = [] + vals['user_ids'] += [(4, user.id) for user in new_group.users] + + write_res = super(gamification_challenge, self).write(cr, uid, ids, vals, context=context) + + # subscribe new users to the challenge + if vals.get('user_ids'): + # done with browse after super if changes in groups + for challenge in self.browse(cr, uid, ids, context=context): + self.message_subscribe_users(cr, uid, [challenge.id], [user.id for user in challenge.user_ids], context=context) + return write_res + ##### Update ##### def _cron_update(self, cr, uid, context=None, ids=False): """Daily cron check. - Start planned plans (in draft and with start_date = today) - Create the goals for planlines not linked to goals (eg: modified the - plan to add planlines) - Update every plan running + - Start planned challenges (in draft and with start_date = today) + - Create the missing goals (eg: modified the challenge to add lines) + - Update every running challenge """ if not context: context = {} - # start planned plans - planned_plan_ids = self.search(cr, uid, [ + # start planned challenges + planned_challenge_ids = self.search(cr, uid, [ ('state', '=', 'draft'), ('start_date', '<=', fields.date.today())]) - self.action_start(cr, uid, planned_plan_ids, context=context) + self.action_start(cr, uid, planned_challenge_ids, context=context) - # close planned plans - planned_plan_ids = self.search(cr, uid, [ + # close planned challenges + planned_challenge_ids = self.search(cr, uid, [ ('state', '=', 'inprogress'), ('end_date', '>=', fields.date.today())]) - self.action_close(cr, uid, planned_plan_ids, context=context) + self.action_close(cr, uid, planned_challenge_ids, context=context) if not ids: ids = self.search(cr, uid, [('state', '=', 'inprogress')], context=context) @@ -273,17 +250,17 @@ class gamification_goal_plan(osv.Model): return self._update_all(cr, uid, ids, context=context) def _update_all(self, cr, uid, ids, context=None): - """Update the plans and related goals + """Update the challenges and related goals - :param list(int) ids: the ids of the plans to update, if False will - update only plans in progress.""" + :param list(int) ids: the ids of the challenges to update, if False will + update only challenges in progress.""" if not context: context = {} goal_obj = self.pool.get('gamification.goal') # we use yesterday to update the goals that just ended yesterday = date.today() - timedelta(days=1) goal_ids = goal_obj.search(cr, uid, [ - ('plan_id', 'in', ids), + ('challenge_id', 'in', ids), '|', ('state', 'in', ('inprogress', 'inprogress_update')), '&', @@ -292,121 +269,123 @@ class gamification_goal_plan(osv.Model): ('end_date', '>=', yesterday.strftime(DF)), ('end_date', '=', False) ], context=context) - # update every running goal already generated linked to selected plans + # update every running goal already generated linked to selected challenges goal_obj.update(cr, uid, goal_ids, context=context) - for plan in self.browse(cr, uid, ids, context=context): - if plan.autojoin_group_id: - # check in case of new users in plan, this happens if manager removed users in plan manually - self.write(cr, uid, [plan.id], {'user_ids': [(4, user.id) for user in plan.autojoin_group_id.users]}, context=context) - self.generate_goals_from_plan(cr, uid, [plan.id], context=context) + for challenge in self.browse(cr, uid, ids, context=context): + if challenge.autojoin_group_id: + # check in case of new users in challenge, this happens if manager removed users in challenge manually + self.write(cr, uid, [challenge.id], {'user_ids': [(4, user.id) for user in challenge.autojoin_group_id.users]}, 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, [ - ('plan_id', '=', plan.id), - ('start_date', '>=', plan.last_report_date), - ('end_date', '<=', plan.last_report_date) + ('challenge_id', '=', challenge.id), + ('start_date', '>=', challenge.last_report_date), + ('end_date', '<=', challenge.last_report_date) ]) if len(closed_goals_to_report) > 0: # some goals need a final report - self.report_progress(cr, uid, plan, subset_goal_ids=closed_goals_to_report, context=context) + self.report_progress(cr, uid, challenge, subset_goal_ids=closed_goals_to_report, context=context) - if fields.date.today() == plan.next_report_date: - self.report_progress(cr, uid, plan, context=context) + if fields.date.today() == challenge.next_report_date: + self.report_progress(cr, uid, challenge, context=context) self.check_challenge_reward(cr, uid, ids, context=context) return True - def quick_update(self, cr, uid, plan_id, context=None): - """Update all the goals of a plan, no generation of new goals""" + def quick_update(self, cr, uid, challenge_id, context=None): + """Update all the goals of a challenge, no generation of new goals""" if not context: context = {} - goal_ids = self.pool.get('gamification.goal').search(cr, uid, [('plan_id', '=', plan_id)], context=context) + 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 + ##### User actions ##### def action_start(self, cr, uid, ids, context=None): - """Start a draft goal plan + """Start a draft challenge - Change the state of the plan to in progress and generate related goals + Change the state of the challenge to in progress and generate related goals """ # subscribe users if autojoin group - for plan in self.browse(cr, uid, ids, context=context): - if plan.autojoin_group_id: - self.write(cr, uid, [plan.id], {'user_ids': [(4, user.id) for user in plan.autojoin_group_id.users]}, context=context) + for challenge in self.browse(cr, uid, ids, context=context): + if challenge.autojoin_group_id: + self.write(cr, uid, [challenge.id], {'user_ids': [(4, user.id) for user in challenge.autojoin_group_id.users]}, context=context) - self.write(cr, uid, plan.id, {'state': 'inprogress'}, context=context) - self.message_post(cr, uid, plan.id, body="New challenge started.", context=context) - return self.generate_goals_from_plan(cr, uid, ids, context=context) + self.write(cr, uid, challenge.id, {'state': 'inprogress'}, context=context) + self.message_post(cr, uid, challenge.id, body="New challenge started.", context=context) + return self.generate_goals_from_challenge(cr, uid, ids, context=context) def action_check(self, cr, uid, ids, context=None): - """Check a goal plan + """Check a challenge - Create goals that haven't been created yet (eg: if added users of planlines) + Create goals that haven't been created yet (eg: if added users) Recompute the current value for each goal related""" return self._update_all(cr, uid, ids=ids, context=context) def action_close(self, cr, uid, ids, context=None): - """Close a plan in progress + """Close a challenge in progress - Change the state of the plan to in done + Change the state of the challenge to in done Does NOT close the related goals, this is handled by the goal itself""" self.check_challenge_reward(cr, uid, ids, force=True, context=context) return self.write(cr, uid, ids, {'state': 'done'}, context=context) def action_reset(self, cr, uid, ids, context=None): - """Reset a closed goal plan + """Reset a closed challenge - Change the state of the plan to in progress - Closing a plan does not affect the goals so neither does reset""" + Change the state of the challenge to in progress + Closing a challenge does not affect the goals so neither does reset""" return self.write(cr, uid, ids, {'state': 'inprogress'}, context=context) def action_cancel(self, cr, uid, ids, context=None): - """Cancel a plan in progress + """Cancel a challenge in progress - Change the state of the plan to draft + Change the state of the challenge to draft Cancel the related goals""" self.write(cr, uid, ids, {'state': 'draft'}, context=context) - goal_ids = self.pool.get('gamification.goal').search(cr, uid, [('plan_id', 'in', ids)], context=context) + goal_ids = self.pool.get('gamification.goal').search(cr, uid, [('challenge_id', 'in', ids)], context=context) self.pool.get('gamification.goal').write(cr, uid, goal_ids, {'state': 'canceled'}, context=context) return True def action_report_progress(self, cr, uid, ids, context=None): """Manual report of a goal, does not influence automatic report frequency""" - for plan in self.browse(cr, uid, ids, context): - self.report_progress(cr, uid, plan, context=context) + for challenge in self.browse(cr, uid, ids, context): + self.report_progress(cr, uid, challenge, context=context) return True + ##### Automatic actions ##### - def generate_goals_from_plan(self, cr, uid, ids, context=None): - """Generate the list of goals linked to a plan. + 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 planline, the planline is skipped. This - can be called after each change in the user or planline list. - :param list(int) ids: the list of plan concerned""" + If goals already exist for this line and user, the line is skipped. This + can be called after each change in the list of users or lines. + :param list(int) ids: the list of challenge concerned""" - for plan in self.browse(cr, uid, ids, context): - (start_date, end_date) = start_end_date_for_period(plan.period) + for challenge in self.browse(cr, uid, ids, context): + (start_date, end_date) = start_end_date_for_period(challenge.period) - # if no periodicity, use plan dates - if not start_date and plan.start_date: - start_date = plan.start_date - if not end_date and plan.end_date: - end_date = plan.end_date + # if no periodicity, use challenge dates + if not start_date and challenge.start_date: + start_date = challenge.start_date + if not end_date and challenge.end_date: + end_date = challenge.end_date - for planline in plan.planline_ids: - for user in plan.user_ids: + for line in challenge.line_ids: + for user in challenge.user_ids: goal_obj = self.pool.get('gamification.goal') - domain = [('planline_id', '=', planline.id), ('user_id', '=', user.id)] + domain = [('line_id', '=', line.id), ('user_id', '=', user.id)] if start_date: domain.append(('start_date', '=', start_date)) - # goal already existing for this planline ? + # goal already existing for this line ? if len(goal_obj.search(cr, uid, domain, context=context)) > 0: # resume canceled goals @@ -419,10 +398,10 @@ class gamification_goal_plan(osv.Model): continue values = { - 'type_id': planline.type_id.id, - 'planline_id': planline.id, + 'definition_id': line.definition_id.id, + 'line_id': line.id, 'user_id': user.id, - 'target_goal': planline.target_goal, + 'target_goal': line.target_goal, 'state': 'inprogress', } @@ -431,8 +410,8 @@ class gamification_goal_plan(osv.Model): if end_date: values['end_date'] = end_date - if planline.plan_id.remind_update_delay: - values['remind_update_delay'] = planline.plan_id.remind_update_delay + if challenge.remind_update_delay: + values['remind_update_delay'] = challenge.remind_update_delay new_goal_id = goal_obj.create(cr, uid, values, context) @@ -442,17 +421,17 @@ class gamification_goal_plan(osv.Model): ##### JS utilities ##### - def get_board_goal_info(self, cr, uid, plan, subset_goal_ids=False, context=None): - """Get the list of latest goals for a plan, sorted by user ranking for each planline""" + 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""" goal_obj = self.pool.get('gamification.goal') - planlines_boards = [] - (start_date, end_date) = start_end_date_for_period(plan.period) + lines_boards = [] + (start_date, end_date) = start_end_date_for_period(challenge.period) - for planline in plan.planline_ids: + for line in challenge.line_ids: domain = [ - ('planline_id', '=', planline.id), + ('line_id', '=', line.id), ('state', 'in', ('inprogress', 'inprogress_update', 'reached', 'failed')), ] @@ -471,24 +450,24 @@ class gamification_goal_plan(osv.Model): board_goals = [goal for goal in goal_obj.browse(cr, uid, common_goal_ids, context=context)] if len(board_goals) == 0: - # planline has no generated goals + # 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)) - planlines_boards.append({'goal_type': planline.type_id, 'board_goals': sorted_board, 'target_goal': planline.target_goal}) - return planlines_boards + 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, plan, subset_goal_ids=False, context=None): - """Get the list of latest goals of a user for a plan""" + 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 = [ - ('plan_id', '=', plan.id), + ('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(plan.period) + (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 @@ -510,7 +489,7 @@ class gamification_goal_plan(osv.Model): 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 plan run + # do not include goals of previous challenge run continue else: all_done = False @@ -521,24 +500,24 @@ class gamification_goal_plan(osv.Model): goals.append(goal) if all_done: - # skip plans where all goal are done or failed + # skip challenges where all goal are done or failed return False else: return goals ##### Reporting ##### - def report_progress(self, cr, uid, plan, context=None, users=False, subset_goal_ids=False): + def report_progress(self, cr, uid, challenge, context=None, users=False, subset_goal_ids=False): """Post report about the progress of the goals - :param plan: the plan object that need to be reported + :param challenge: the challenge object that need to be reported :param users: the list(res.users) of users that are concerned by the report. If False, will send the report to every user concerned - (goal users and group that receive a copy). Only used for plan with + (goal users and group that receive a copy). Only used for challenge with a visibility mode set to 'personal'. - :param goal_ids: the list(int) of goal ids linked to the plan for - the report. If not specified, use the goals for the current plan - period. This parameter can be used to produce report for previous plan + :param goal_ids: the list(int) of goal ids linked to the challenge for + the report. If not specified, use the goals for the current challenge + period. This parameter can be used to produce report for previous challenge periods. :param subset_goal_ids: a list(int) of goal ids to restrict the report """ @@ -547,81 +526,81 @@ class gamification_goal_plan(osv.Model): # template_env = TemplateHelper() temp_obj = self.pool.get('email.template') ctx = context.copy() - if plan.visibility_mode == 'board': - planlines_boards = self.get_board_goal_info(cr, uid, plan, subset_goal_ids, context) + if challenge.visibility_mode == 'board': + lines_boards = self.get_board_goal_info(cr, uid, challenge, subset_goal_ids, context) - ctx.update({'planlines_boards': planlines_boards}) + 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.goal.plan', plan.id, context=context) + body_html = temp_obj.render_template(cr, uid, template_id.body_html, 'gamification.challenge', challenge.id, context=context) - # body_html = template_env.get_template('group_progress.mako').render({'object': plan, 'planlines_boards': planlines_boards, 'uid': uid}) + # body_html = template_env.get_template('group_progress.mako').render({'object': challenge, 'lines_boards': lines_boards, 'uid': uid}) - # send to every follower of the plan - self.message_post(cr, uid, plan.id, + # send to every follower of the challenge + self.message_post(cr, uid, challenge.id, body=body_html, context=context, subtype='mail.mt_comment') - if plan.report_message_group_id: - self.pool.get('mail.group').message_post(cr, uid, plan.report_message_group_id.id, + if challenge.report_message_group_id: + self.pool.get('mail.group').message_post(cr, uid, challenge.report_message_group_id.id, body=body_html, context=context, subtype='mail.mt_comment') else: # generate individual reports - for user in users or plan.user_ids: - goals = self.get_indivual_goal_info(cr, uid, user.id, plan, subset_goal_ids, context=context) + for user in users or challenge.user_ids: + goals = self.get_indivual_goal_info(cr, uid, user.id, challenge, subset_goal_ids, context=context) if not goals: continue ctx.update({'goals': 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.goal.plan', plan.id, context=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 self.message_post(cr, uid, 0, body=body_html, partner_ids=[(4, user.partner_id.id)], context=context, subtype='mail.mt_comment') - if plan.report_message_group_id: - self.pool.get('mail.group').message_post(cr, uid, plan.report_message_group_id.id, + if challenge.report_message_group_id: + self.pool.get('mail.group').message_post(cr, uid, challenge.report_message_group_id.id, body=body_html, context=context, subtype='mail.mt_comment') - return self.write(cr, uid, plan.id, {'last_report_date': fields.date.today()}, context=context) + return self.write(cr, uid, challenge.id, {'last_report_date': fields.date.today()}, context=context) ##### Challenges ##### - def accept_challenge(self, cr, uid, plan_ids, context=None, user_id=None): + def accept_challenge(self, cr, uid, challenge_ids, context=None, user_id=None): """The user accept the suggested challenge""" context = context or {} user_id = user_id or uid 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, plan_ids, body=message, context=context) - self.write(cr, uid, plan_ids, {'proposed_user_ids': [(3, user_id)], 'user_ids': [(4, user_id)]}, context=context) - return self.generate_goals_from_plan(cr, uid, plan_ids, context=context) + 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) + return self.generate_goals_from_challenge(cr, uid, challenge_ids, context=context) - def discard_challenge(self, cr, uid, plan_ids, context=None, user_id=None): + def discard_challenge(self, cr, uid, challenge_ids, context=None, user_id=None): """The user discard the suggested challenge""" context = context or {} 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, plan_ids, body=message, context=context) - return self.write(cr, uid, plan_ids, {'proposed_user_ids': (3, user_id)}, context=context) + self.message_post(cr, uid, 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, plan_id, context=None): + def reply_challenge_wizard(self, cr, uid, challenge_id, context=None): context = context or {} mod_obj = self.pool.get('ir.model.data') act_obj = self.pool.get('ir.actions.act_window') result = mod_obj.get_object_reference(cr, uid, 'gamification', 'challenge_wizard') id = result and result[1] or False result = act_obj.read(cr, uid, [id], context=context)[0] - result['res_id'] = plan_id + result['res_id'] = challenge_id return result - def check_challenge_reward(self, cr, uid, plan_ids, force=False, context=None): + def check_challenge_reward(self, cr, uid, challenge_ids, force=False, context=None): """Actions for the end of a challenge If a reward was selected, grant it to the correct users. @@ -632,55 +611,55 @@ class gamification_goal_plan(osv.Model): (if no end date, a running challenge is never rewarded) """ context = context or {} - for plan in self.browse(cr, uid, plan_ids, context=context): - (start_date, end_date) = start_end_date_for_period(plan.period, plan.start_date, plan.end_date) + for challenge in self.browse(cr, uid, challenge_ids, context=context): + (start_date, end_date) = start_end_date_for_period(challenge.period, challenge.start_date, challenge.end_date) yesterday = date.today() - timedelta(days=1) if end_date == yesterday.strftime(DF) or force: # open chatter message - message_body = _("The challenge %s is finished." % plan.name) + message_body = _("The challenge %s is finished." % challenge.name) # reward for everybody succeeding rewarded_users = [] - if plan.reward_id: - for user in plan.user_ids: + if challenge.reward_id: + for user in challenge.user_ids: reached_goal_ids = self.pool.get('gamification.goal').search(cr, uid, [ - ('plan_id', '=', plan.id), + ('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(plan.planline_ids): - self.reward_user(cr, uid, user.id, plan.reward_id.id, context) + if len(reached_goal_ids) == len(challenge.line_ids): + self.reward_user(cr, uid, user.id, challenge.reward_id.id, context) rewarded_users.append(user) if rewarded_users: - message_body += _("
Reward (badge %s) for every succeeding user was sent to %s." % (plan.reward_id.name, ", ".join([user.name for user in rewarded_users]))) + message_body += _("
Reward (badge %s) for every succeeding user was sent to %s." % (challenge.reward_id.name, ", ".join([user.name for user in rewarded_users]))) else: message_body += _("
Nobody has succeeded to reach every goal, no badge is rewared for this challenge.") # reward bests - if plan.reward_first_id: - (first_user, second_user, third_user) = self.get_top3_users(cr, uid, plan, context) + if challenge.reward_first_id: + (first_user, second_user, third_user) = self.get_top3_users(cr, uid, challenge, context) if first_user: - self.reward_user(cr, uid, first_user.id, plan.reward_first_id.id, context) + self.reward_user(cr, uid, first_user.id, challenge.reward_first_id.id, context) message_body += _("
Special rewards were sent to the top competing users. The ranking for this challenge is :") - message_body += "
1. %s - %s" % (first_user.name, plan.reward_first_id.name) + message_body += "
1. %s - %s" % (first_user.name, challenge.reward_first_id.name) else: message_body += _("Nobody reached the required conditions to receive special badges.") - if second_user and plan.reward_second_id: - self.reward_user(cr, uid, second_user.id, plan.reward_second_id.id, context) - message_body += "
2. %s - %s" % (second_user.name, plan.reward_second_id.name) - if third_user and plan.reward_third_id: - self.reward_user(cr, uid, third_user.id, plan.reward_second_id.id, context) - message_body += "
3. %s - %s" % (third_user.name, plan.reward_third_id.name) + if second_user and challenge.reward_second_id: + self.reward_user(cr, uid, second_user.id, challenge.reward_second_id.id, context) + message_body += "
2. %s - %s" % (second_user.name, challenge.reward_second_id.name) + if third_user and challenge.reward_third_id: + self.reward_user(cr, uid, third_user.id, challenge.reward_second_id.id, context) + message_body += "
3. %s - %s" % (third_user.name, challenge.reward_third_id.name) - self.message_post(cr, uid, plan.id, body=message_body, context=context) + self.message_post(cr, uid, challenge.id, body=message_body, context=context) return True - def get_top3_users(self, cr, uid, plan, context=None): - """Get the top 3 users for a defined plan + def get_top3_users(self, cr, uid, challenge, context=None): + """Get the top 3 users for a defined challenge Ranking criterias: 1. succeed every goal of the challenge @@ -694,14 +673,14 @@ class gamification_goal_plan(osv.Model): higher one (eg: if 'second' == False, 'third' will be False) """ goal_obj = self.pool.get('gamification.goal') - (start_date, end_date) = start_end_date_for_period(plan.period, plan.start_date, plan.end_date) + (start_date, end_date) = start_end_date_for_period(challenge.period, challenge.start_date, challenge.end_date) challengers = [] - for user in plan.user_ids: + for user in challenge.user_ids: all_reached = True total_completness = 0 # every goal of the user for the running period goal_ids = goal_obj.search(cr, uid, [ - ('plan_id', '=', plan.id), + ('challenge_id', '=', challenge.id), ('user_id', '=', user.id), ('start_date', '=', start_date), ('end_date', '=', end_date) @@ -709,7 +688,7 @@ class gamification_goal_plan(osv.Model): for goal in goal_obj.browse(cr, uid, goal_ids, context=context): if goal.state != 'reached': all_reached = False - if goal.type_condition == 'higher': + if goal.definition_condition == 'higher': # can be over 100 total_completness += 100.0 * goal.current / goal.target_goal elif goal.state == 'reached': @@ -719,13 +698,13 @@ class gamification_goal_plan(osv.Model): challengers.append({'user': user, 'all_reached': all_reached, 'total_completness': total_completness}) sorted_challengers = sorted(challengers, key=lambda k: (k['all_reached'], k['total_completness']), reverse=True) - if len(sorted_challengers) == 0 or (not plan.reward_failure and not sorted_challengers[0]['all_reached']): + if len(sorted_challengers) == 0 or (not challenge.reward_failure and not sorted_challengers[0]['all_reached']): # nobody succeeded return (False, False, False) - if len(sorted_challengers) == 1 or (not plan.reward_failure and not sorted_challengers[1]['all_reached']): + if len(sorted_challengers) == 1 or (not challenge.reward_failure and not sorted_challengers[1]['all_reached']): # only one user succeeded return (sorted_challengers[0]['user'], False, False) - if len(sorted_challengers) == 2 or (not plan.reward_failure and not sorted_challengers[2]['all_reached']): + if len(sorted_challengers) == 2 or (not challenge.reward_failure and not sorted_challengers[2]['all_reached']): # only one user succeeded return (sorted_challengers[0]['user'], sorted_challengers[1]['user'], False) return (sorted_challengers[0]['user'], sorted_challengers[1]['user'], sorted_challengers[2]['user']) @@ -736,66 +715,50 @@ class gamification_goal_plan(osv.Model): return self.pool.get('gamification.badge').send_badge(cr, uid, badge_id, [user_badge_id], user_from=None, context=context) -class gamification_goal_planline(osv.Model): - """Gamification goal planline +class gamification_challenge_line(osv.Model): + """Gamification challenge line - Predifined goal for 'gamification_goal_plan' + Predifined goal for 'gamification_challenge' These are generic list of goals with only the target goal defined - Should only be created for the gamification_goal_plan object + Should only be created for the gamification_challenge object """ - _name = 'gamification.goal.planline' - _description = 'Gamification generic goal for plan' - _order = "sequence, sequence_type, id" + _name = 'gamification.challenge.line' + _description = 'Gamification generic goal for challenge' + _order = "sequence, id" - def _get_planline_types(self, cr, uid, ids, context=None): - """Return the ids of planline items related to the gamification.goal.type - objects in 'ids (used to update the value of 'sequence_type')'""" - - result = {} - for goal_type in self.pool.get('gamification.goal.type').browse(cr, uid, ids, context=context): - domain = [('type_id', '=', goal_type.id)] - planline_ids = self.pool.get('gamification.goal.planline').search(cr, uid, domain, context=context) - for p_id in planline_ids: - result[p_id] = True - return result.keys() - - def on_change_type_id(self, cr, uid, ids, type_id=False, context=None): - goal_type = self.pool.get('gamification.goal.type') - if not type_id: - return {'value': {'type_id': False}} - goal_type = goal_type.browse(cr, uid, type_id, context=context) - ret = {'value': { - 'type_condition': goal_type.condition, - 'type_full_suffix': goal_type.full_suffix}} + def on_change_definition_id(self, cr, uid, ids, definition_id=False, context=None): + goal_definition = self.pool.get('gamification.goal.definition') + if not definition_id: + return {'value': {'definition_id': False}} + goal_definition = goal_definition.browse(cr, uid, definition_id, context=context) + ret = { + 'value': { + 'definition_condition': goal_definition.condition, + 'definition_full_suffix': goal_definition.full_suffix + } + } return ret _columns = { - 'name': fields.related('type_id', 'name', string="Name"), - 'plan_id': fields.many2one('gamification.goal.plan', - string='Plan', + 'name': fields.related('definition_id', 'name', string="Name"), + 'challenge_id': fields.many2one('gamification.challenge', + string='Challenge', required=True, ondelete="cascade"), - 'type_id': fields.many2one('gamification.goal.type', - string='Goal Type', + 'definition_id': fields.many2one('gamification.goal.definition', + string='Goal Definition', required=True, ondelete="cascade"), 'target_goal': fields.float('Target Value to Reach', required=True), 'sequence': fields.integer('Sequence', help='Sequence number for ordering'), - 'sequence_type': fields.related('type_id', 'sequence', - type='integer', - string='Sequence', - readonly=True, - store={ - 'gamification.goal.type': (_get_planline_types, ['sequence'], 10), - }), - 'type_condition': fields.related('type_id', 'condition', type="selection", + 'definition_condition': fields.related('definition_id', 'condition', type="selection", readonly=True, string="Condition", selection=[('lower', '<='), ('higher', '>=')]), - 'type_suffix': fields.related('type_id', 'suffix', type="char", readonly=True, string="Unit"), - 'type_monetary': fields.related('type_id', 'monetary', type="boolean", readonly=True, string="Monetary"), - 'type_full_suffix': fields.related('type_id', 'full_suffix', type="char", readonly=True, string="Suffix"), + '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"), + 'definition_full_suffix': fields.related('definition_id', 'full_suffix', type="char", readonly=True, string="Suffix"), } _default = { diff --git a/addons/gamification/goal.py b/addons/gamification/models/goal.py similarity index 80% rename from addons/gamification/goal.py rename to addons/gamification/models/goal.py index 11a3b2517ff..5a170a904c6 100644 --- a/addons/gamification/goal.py +++ b/addons/gamification/models/goal.py @@ -32,16 +32,15 @@ import logging _logger = logging.getLogger(__name__) -class gamification_goal_type(osv.Model): - """Goal type definition +class gamification_goal_definition(osv.Model): + """Goal definition - A goal type defining a way to set an objective and evaluate it + A goal definition contains the way to evaluate an objective Each module wanting to be able to set goals to the users needs to create - a new gamification_goal_type + a new gamification_goal_definition """ - _name = 'gamification.goal.type' - _description = 'Gamification goal type' - _order = 'sequence' + _name = 'gamification.goal.definition' + _description = 'Gamification goal definition' def _get_suffix(self, cr, uid, ids, field_name, arg, context=None): res = dict.fromkeys(ids, '') @@ -60,7 +59,7 @@ class gamification_goal_type(osv.Model): return res _columns = { - 'name': fields.char('Goal Type', required=True, translate=True), + 'name': fields.char('Goal Definition', required=True, translate=True), 'description': fields.text('Goal Description'), 'monetary': fields.boolean('Monetary Value', help="The target and current value are defined in the company currency."), 'suffix': fields.char('Suffix', help="The unit of the target and current values", translate=True), @@ -92,7 +91,7 @@ class gamification_goal_type(osv.Model): help="Technical filters rules to apply. Use 'user.id' (without marks) to limit the search to the evaluated user.", required=True), 'compute_code': fields.char('Compute Code', - help="The name of the python method that will be executed to compute the current value. See the file gamification/goal_type_data.py for examples."), + help="The name of the python method that will be executed to compute the current value. See the file gamification/goal_definition_data.py for examples."), 'condition': fields.selection([ ('higher', 'The higher the better'), ('lower', 'The lower the better') @@ -100,7 +99,6 @@ class gamification_goal_type(osv.Model): string='Goal Performance', help='A goal is considered as completed when the current value is compared to the value to reach', required=True), - 'sequence': fields.integer('Sequence', help='Sequence number for ordering', required=True), 'action_id': fields.many2one('ir.actions.act_window', string="Action", help="The action that will be called to update the goal value."), 'res_id_field': fields.char("ID Field of user", @@ -108,7 +106,6 @@ class gamification_goal_type(osv.Model): } _defaults = { - 'sequence': 1, 'condition': 'higher', 'computation_mode': 'manually', 'domain': "[]", @@ -130,7 +127,7 @@ class gamification_goal(osv.Model): """Return the percentage of completeness of the goal, between 0 and 100""" res = dict.fromkeys(ids, 0.0) for goal in self.browse(cr, uid, ids, context=context): - if goal.type_condition == 'higher': + if goal.definition_condition == 'higher': if goal.current >= goal.target_goal: res[goal.id] = 100.0 else: @@ -142,21 +139,21 @@ class gamification_goal(osv.Model): res[goal.id] = 0.0 return res - def on_change_type_id(self, cr, uid, ids, type_id=False, context=None): - goal_type = self.pool.get('gamification.goal.type') - if not type_id: - return {'value': {'type_id': False}} - goal_type = goal_type.browse(cr, uid, type_id, context=context) - return {'value': {'computation_mode': goal_type.computation_mode, 'type_condition': goal_type.condition}} + def on_change_definition_id(self, cr, uid, ids, definition_id=False, context=None): + goal_definition = self.pool.get('gamification.goal.definition') + if not definition_id: + return {'value': {'definition_id': False}} + goal_definition = goal_definition.browse(cr, uid, definition_id, context=context) + return {'value': {'computation_mode': goal_definition.computation_mode, 'definition_condition': goal_definition.condition}} _columns = { - 'type_id': fields.many2one('gamification.goal.type', string='Goal Type', required=True, ondelete="cascade"), + 'definition_id': fields.many2one('gamification.goal.definition', string='Goal Definition', required=True, ondelete="cascade"), 'user_id': fields.many2one('res.users', string='User', required=True), - 'planline_id': fields.many2one('gamification.goal.planline', string='Goal Planline', ondelete="cascade"), - 'plan_id': fields.related('planline_id', 'plan_id', - string="Plan", + 'line_id': fields.many2one('gamification.challenge.line', string='Goal Line', ondelete="cascade"), + 'challenge_id': fields.related('line_id', 'challenge_id', + string="Challenge", type='many2one', - relation='gamification.goal.plan', + relation='gamification.challenge', store=True), 'start_date': fields.date('Start Date'), 'end_date': fields.date('End Date'), # no start and end = always active @@ -177,16 +174,16 @@ class gamification_goal(osv.Model): required=True, track_visibility='always'), - 'computation_mode': fields.related('type_id', 'computation_mode', type='char', string="Type computation mode"), + 'computation_mode': fields.related('definition_id', 'computation_mode', type='char', string="Computation mode"), 'remind_update_delay': fields.integer('Remind delay', help="The number of days after which the user assigned to a manual goal will be reminded. Never reminded if no value is specified."), 'last_update': fields.date('Last Update', - help="In case of manual goal, reminders are sent if the goal as not been updated for a while (defined in goal plan). Ignored in case of non-manual goal or goal not linked to a plan."), + help="In case of manual goal, reminders are sent if the goal as not been updated for a while (defined in challenge). Ignored in case of non-manual goal or goal not linked to a challenge."), - 'type_description': fields.related('type_id', 'description', type='char', string='Type Description', readonly=True), - 'type_condition': fields.related('type_id', 'condition', type='char', string='Type Condition', readonly=True), - 'type_suffix': fields.related('type_id', 'full_suffix', type="char", string="Suffix", readonly=True), - 'type_display': fields.related('type_id', 'display_mode', type="char", string="Display Mode", readonly=True), + 'definition_description': fields.related('definition_id', 'description', type='char', string='Definition Description', readonly=True), + 'definition_condition': fields.related('definition_id', 'condition', type='char', string='Definition Condition', readonly=True), + 'definition_suffix': fields.related('definition_id', 'full_suffix', type="char", string="Suffix", readonly=True), + 'definition_display': fields.related('definition_id', 'display_mode', type="char", string="Display Mode", readonly=True), } _defaults = { @@ -194,7 +191,7 @@ class gamification_goal(osv.Model): 'state': 'draft', 'start_date': fields.date.today, } - _order = 'create_date desc, end_date desc, type_id, id' + _order = 'create_date desc, end_date desc, definition_id, id' def _check_remind_delay(self, cr, uid, goal, context=None): """Verify if a goal has not been updated for some time and send a @@ -230,26 +227,26 @@ class gamification_goal(osv.Model): # skip if goal draft or canceled continue - if goal.type_id.computation_mode == 'manually': + if goal.definition_id.computation_mode == 'manually': towrite.update(self._check_remind_delay(cr, uid, goal, context)) - elif goal.type_id.computation_mode == 'python': + elif goal.definition_id.computation_mode == 'python': # execute the chosen method - values = {'cr': cr, 'uid': goal.user_id.id, 'context': context, 'self': self.pool.get('gamification.goal.type')} - result = safe_eval(goal.type_id.compute_code, values, {}) + values = {'cr': cr, 'uid': goal.user_id.id, 'context': context, 'self': self.pool.get('gamification.goal.definition')} + result = safe_eval(goal.definition_id.compute_code, values, {}) if type(result) in (float, int, long) and result != goal.current: towrite['current'] = result else: - _logger.exception(_('Unvalid return content from the evaluation of %s' % str(goal.type_id.compute_code))) - # raise osv.except_osv(_('Error!'), _('Unvalid return content from the evaluation of %s' % str(goal.type_id.compute_code))) + _logger.exception(_('Unvalid return content from the evaluation of %s' % str(goal.definition_id.compute_code))) + # raise osv.except_osv(_('Error!'), _('Unvalid return content from the evaluation of %s' % str(goal.definition_id.compute_code))) else: # count or sum - obj = self.pool.get(goal.type_id.model_id.model) - field_date_name = goal.type_id.field_date_id.name + obj = self.pool.get(goal.definition_id.model_id.model) + field_date_name = goal.definition_id.field_date_id.name # eval the domain with user replaced by goal user object - domain = safe_eval(goal.type_id.domain, {'user': goal.user_id}) + domain = safe_eval(goal.definition_id.domain, {'user': goal.user_id}) #add temporal clause(s) to the domain if fields are filled on the goal if goal.start_date and field_date_name: @@ -257,8 +254,8 @@ class gamification_goal(osv.Model): if goal.end_date and field_date_name: domain.append((field_date_name, '<=', goal.end_date)) - if goal.type_id.computation_mode == 'sum': - field_name = goal.type_id.field_id.name + if goal.definition_id.computation_mode == 'sum': + field_name = goal.definition_id.field_id.name res = obj.read_group(cr, uid, domain, [field_name], [''], context=context) new_value = res and res[0][field_name] or 0.0 @@ -270,7 +267,7 @@ class gamification_goal(osv.Model): towrite['current'] = new_value # check goal target reached - if (goal.type_id.condition == 'higher' and towrite.get('current', goal.current) >= goal.target_goal) or (goal.type_id.condition == 'lower' and towrite.get('current', goal.current) <= goal.target_goal): + if (goal.definition_id.condition == 'higher' and towrite.get('current', goal.current) >= goal.target_goal) or (goal.definition_id.condition == 'lower' and towrite.get('current', goal.current) <= goal.target_goal): towrite['state'] = 'reached' # check goal failure @@ -323,7 +320,7 @@ class gamification_goal(osv.Model): vals['last_update'] = fields.date.today() result = super(gamification_goal, self).write(cr, uid, ids, vals, context=context) for goal in self.browse(cr, uid, ids, context=context): - if goal.state != "draft" and ('type_id' in vals or 'user_id' in vals): + if goal.state != "draft" and ('definition_id' in vals or 'user_id' in vals): # avoid drag&drop in kanban view raise osv.except_osv(_('Error!'), _('Can not modify the configuration of a started goal')) @@ -332,8 +329,8 @@ class gamification_goal(osv.Model): # new goals should not be reported continue - if goal.plan_id and goal.plan_id.report_message_frequency == 'onchange': - self.pool.get('gamification.goal.plan').report_progress(cr, SUPERUSER_ID, goal.plan_id, users=[goal.user_id], context=context) + if goal.challenge_id and goal.challenge_id.report_message_frequency == 'onchange': + self.pool.get('gamification.challenge').report_progress(cr, SUPERUSER_ID, goal.challenge_id, users=[goal.user_id], context=context) return result def get_action(self, cr, uid, goal_id, context=None): @@ -344,13 +341,13 @@ class gamification_goal(osv.Model): """ goal = self.browse(cr, uid, goal_id, context=context) - if goal.type_id.action_id: + if goal.definition_id.action_id: #open a the action linked on the goal - action = goal.type_id.action_id.read()[0] + action = goal.definition_id.action_id.read()[0] - if goal.type_id.res_id_field: + if goal.definition_id.res_id_field: current_user = self.pool.get('res.users').browse(cr, uid, uid, context=context) - action['res_id'] = safe_eval(goal.type_id.res_id_field, {'user': current_user}) + action['res_id'] = safe_eval(goal.definition_id.res_id_field, {'user': current_user}) # if one element to display, should see it in form mode if possible views = action['views'] @@ -364,7 +361,7 @@ class gamification_goal(osv.Model): if goal.computation_mode == 'manually': #open a wizard window to update the value manually action = { - 'name': _("Update %s") % goal.type_id.name, + 'name': _("Update %s") % goal.definition_id.name, 'id': goal_id, 'type': 'ir.actions.act_window', 'views': [[False, 'form']], @@ -378,7 +375,7 @@ class gamification_goal(osv.Model): class goal_manual_wizard(osv.TransientModel): - """Wizard type to update a manual goal""" + """Wizard to update a manual goal""" _name = 'gamification.goal.wizard' _columns = { 'goal_id': fields.many2one("gamification.goal", string='Goal', required=True), diff --git a/addons/gamification/res_users.py b/addons/gamification/models/res_users.py similarity index 56% rename from addons/gamification/res_users.py rename to addons/gamification/models/res_users.py index 3450dc1492c..38e784247fd 100644 --- a/addons/gamification/res_users.py +++ b/addons/gamification/models/res_users.py @@ -24,7 +24,7 @@ from openerp.osv import osv class res_users_gamification_group(osv.Model): """ Update of res.users class - - if adding groups to an user, check gamification.goal.plan linked to + - if adding groups to an user, check gamification.challenge linked to this group, and the user. This is done by overriding the write method. """ _name = 'res.users' @@ -37,62 +37,62 @@ class res_users_gamification_group(osv.Model): user_group_ids = [command[1] for command in vals['groups_id'] if command[0] == 4] user_group_ids += [id for command in vals['groups_id'] if command[0] == 6 for id in command[2]] - goal_plan_obj = self.pool.get('gamification.goal.plan') - plan_ids = goal_plan_obj.search(cr, uid, [('autojoin_group_id', 'in', user_group_ids)], context=context) - if plan_ids: - goal_plan_obj.write(cr, uid, plan_ids, {'user_ids': [(4, user_id) for user_id in ids]}, context=context) + challenge_obj = self.pool.get('gamification.challenge') + challenge_ids = challenge_obj.search(cr, uid, [('autojoin_group_id', 'in', user_group_ids)], context=context) + if challenge_ids: + challenge_obj.write(cr, uid, challenge_ids, {'user_ids': [(4, user_id) for user_id in ids]}, context=context) if vals.get('image'): - goal_type_id = self.pool.get('ir.model.data').get_object(cr, uid, 'gamification', 'type_base_avatar', context) - goal_ids = self.pool.get('gamification.goal').search(cr, uid, [('type_id', '=', goal_type_id.id), ('user_id', 'in', ids)], context=context) + goal_definition_id = self.pool.get('ir.model.data').get_object(cr, uid, 'gamification', 'definition_base_avatar', context) + goal_ids = self.pool.get('gamification.goal').search(cr, uid, [('definition_id', '=', goal_definition_id.id), ('user_id', 'in', ids)], context=context) values = {'state': 'reached', 'current': 1} self.pool.get('gamification.goal').write(cr, uid, goal_ids, values, 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 plan + """Return the list of goals assigned to the user, grouped by challenge 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 - types + definitions :return: list of dictionnaries for each goal to display """ all_goals_info = [] - plan_obj = self.pool.get('gamification.goal.plan') + challenge_obj = self.pool.get('gamification.challenge') - plan_ids = plan_obj.search(cr, uid, [('user_ids', 'in', uid), ('state', '=', 'inprogress')], context=context) - for plan in plan_obj.browse(cr, uid, plan_ids, context=context): + 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 = { - 'id': plan.id, - 'name': plan.name, - 'visibility_mode': plan.visibility_mode, + 'id': challenge.id, + 'name': challenge.name, + 'visibility_mode': challenge.visibility_mode, } user = self.browse(cr, uid, uid, context=context) serialized_goals_info['currency'] = user.company_id.currency_id.id - if plan.visibility_mode == 'board': - # board report should be grouped by planline for all users - goals_info = plan_obj.get_board_goal_info(cr, uid, plan, subset_goal_ids=False, context=context) + if challenge.visibility_mode == 'board': + # 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 len(goals_info) == 0: - # plan with no valid planlines + # challenge with no valid lines continue - serialized_goals_info['planlines'] = [] - for planline_board in goals_info: - vals = {'type_name': planline_board['goal_type'].name, - 'type_description': planline_board['goal_type'].description, - 'type_condition': planline_board['goal_type'].condition, - 'computation_mode': planline_board['goal_type'].computation_mode, - 'type_monetary': planline_board['goal_type'].monetary, - 'type_suffix': planline_board['goal_type'].suffix, - 'type_action': True if planline_board['goal_type'].action_id else False, - 'type_display': planline_board['goal_type'].display_mode, - 'target_goal': planline_board['target_goal'], + 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 planline_board['board_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 @@ -109,11 +109,11 @@ class res_users_gamification_group(osv.Model): }) if uid == goal[1].user_id.id: vals['own_goal_id'] = goal[1].id - serialized_goals_info['planlines'].append(vals) + serialized_goals_info['lines'].append(vals) else: # individual report are simply a list of goal - goals_info = plan_obj.get_indivual_goal_info(cr, uid, uid, plan, subset_goal_ids=False, context=context) + goals_info = challenge_obj.get_indivual_goal_info(cr, uid, uid, challenge, subset_goal_ids=False, context=context) if not goals_info: continue @@ -122,13 +122,13 @@ class res_users_gamification_group(osv.Model): for goal in goals_info: serialized_goals_info['goals'].append({ 'id': goal.id, - 'type_name': goal.type_id.name, - 'type_description': goal.type_description, - 'type_condition': goal.type_id.condition, - 'type_monetary': goal.type_id.monetary, - 'type_suffix': goal.type_id.suffix, - 'type_action': True if goal.type_id.action_id else False, - 'type_display': goal.type_id.display_mode, + '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, @@ -140,23 +140,23 @@ class res_users_gamification_group(osv.Model): return all_goals_info def get_challenge_suggestions(self, cr, uid, context=None): - """Return the list of goal plans suggested to the user""" - plan_info = [] - goal_plan_obj = self.pool.get('gamification.goal.plan') - plan_ids = goal_plan_obj.search(cr, uid, [('proposed_user_ids', 'in', uid), ('state', '=', 'inprogress')], context=context) - for plan in goal_plan_obj.browse(cr, uid, plan_ids, context=context): + """Return the list of challenges suggested to the user""" + challenge_info = [] + challenge_obj = self.pool.get('gamification.challenge') + challenge_ids = challenge_obj.search(cr, uid, [('invited_user_ids', 'in', uid), ('state', '=', 'inprogress')], context=context) + for challenge in challenge_obj.browse(cr, uid, challenge_ids, context=context): values = { - 'id': plan.id, - 'name': plan.name, - 'description': plan.description, + 'id': challenge.id, + 'name': challenge.name, + 'description': challenge.description, } - plan_info.append(values) - return plan_info + challenge_info.append(values) + return challenge_info class res_groups_gamification_group(osv.Model): """ Update of res.groups class - - if adding users from a group, check gamification.goal.plan linked to + - if adding users from a group, check gamification.challenge linked to this group, and the user. This is done by overriding the write method. """ _name = 'res.groups' @@ -169,9 +169,9 @@ class res_groups_gamification_group(osv.Model): user_ids = [command[1] for command in vals['users'] if command[0] == 4] user_ids += [id for command in vals['users'] if command[0] == 6 for id in command[2]] - goal_plan_obj = self.pool.get('gamification.goal.plan') - plan_ids = goal_plan_obj.search(cr, uid, [('autojoin_group_id', 'in', ids)], context=context) - if plan_ids: - goal_plan_obj.write(cr, uid, plan_ids, {'user_ids': [(4, user_id) for user_id in user_ids]}, context=context) + challenge_obj = self.pool.get('gamification.challenge') + challenge_ids = challenge_obj.search(cr, uid, [('autojoin_group_id', 'in', ids)], context=context) + if challenge_ids: + challenge_obj.write(cr, uid, challenge_ids, {'user_ids': [(4, user_id) for user_id in user_ids]}, context=context) return write_res # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/gamification/security/gamification_security.xml b/addons/gamification/security/gamification_security.xml index 9baf2131505..c783828c7ae 100644 --- a/addons/gamification/security/gamification_security.xml +++ b/addons/gamification/security/gamification_security.xml @@ -13,7 +13,7 @@ - User can only see his/her goals or goal from the same plan in board visibility + User can only see his/her goals or goal from the same challenge in board visibility @@ -24,8 +24,8 @@ '|', ('user_id','=',user.id), '&', - ('plan_id.user_ids','in',user.id), - ('plan_id.visibility_mode','=','board')] + ('challenge_id.user_ids','in',user.id), + ('challenge_id.visibility_mode','=','board')] diff --git a/addons/gamification/security/ir.model.access.csv b/addons/gamification/security/ir.model.access.csv index 3ea86d2c25b..932c5d97c62 100644 --- a/addons/gamification/security/ir.model.access.csv +++ b/addons/gamification/security/ir.model.access.csv @@ -3,14 +3,14 @@ id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink goal_employee,"Goal Employee",model_gamification_goal,base.group_user,1,1,0,0 goal_manager,"Goal Manager",model_gamification_goal,group_goal_manager,1,1,1,1 -goal_type_employee,"Goal Type Employee",model_gamification_goal_type,base.group_user,1,0,0,0 -goal_type_manager,"Goal Type Manager",model_gamification_goal_type,group_goal_manager,1,1,1,1 +goal_definition_employee,"Goal Definition Employee",model_gamification_goal_definition,base.group_user,1,0,0,0 +goal_definition_manager,"Goal Definition Manager",model_gamification_goal_definition,group_goal_manager,1,1,1,1 -plan_employee,"Goal Plan Employee",model_gamification_goal_plan,base.group_user,1,0,0,0 -plan_manager,"Goal Plan Manager",model_gamification_goal_plan,group_goal_manager,1,1,1,1 +challenge_employee,"Goal Challenge Employee",model_gamification_challenge,base.group_user,1,0,0,0 +challenge_manager,"Goal Challenge Manager",model_gamification_challenge,group_goal_manager,1,1,1,1 -planline_employee,"Goal Planline Employee",model_gamification_goal_planline,base.group_user,1,0,0,0 -planline_manager,"Goal Planline Manager",model_gamification_goal_planline,group_goal_manager,1,1,1,1 +challenge_line_employee,"Challenge Line Employee",model_gamification_challenge_line,base.group_user,1,0,0,0 +challenge_line_manager,"Challenge Line Manager",model_gamification_challenge_line,group_goal_manager,1,1,1,1 badge_employee,"Badge Employee",model_gamification_badge,base.group_user,1,0,0,0 badge_manager,"Badge Manager",model_gamification_badge,group_goal_manager,1,1,1,1 diff --git a/addons/gamification/static/src/js/gamification.js b/addons/gamification/static/src/js/gamification.js index ccd5204b2ba..9e34d8f09fc 100644 --- a/addons/gamification/static/src/js/gamification.js +++ b/addons/gamification/static/src/js/gamification.js @@ -12,11 +12,11 @@ openerp.gamification = function(instance) { this.challenge_suggestions = {}; }, events: { - // update a plan and related goals - 'click a.oe_update_plan': function(event) { + // update a challenge and related goals + 'click a.oe_update_challenge': function(event) { var self = this; - var plan_id = parseInt(event.currentTarget.id, 10); - var goals_updated = new instance.web.Model('gamification.goal.plan').call('quick_update', [plan_id]); + var challenge_id = parseInt(event.currentTarget.id, 10); + var goals_updated = new instance.web.Model('gamification.challenge').call('quick_update', [challenge_id]); $.when(goals_updated).done(function() { self.get_goal_todo_info(); }); @@ -40,12 +40,12 @@ openerp.gamification = function(instance) { // get more info about a challenge request 'click a.oe_challenge_reply': function(event) { var self = this; - var plan_id = parseInt(event.currentTarget.id, 10); - var plan_action = new instance.web.Model('gamification.goal.plan').call('reply_challenge_wizard', [plan_id]).then(function(res) { - plan_action['action'] = res; + var challenge_id = parseInt(event.currentTarget.id, 10); + var challenge_action = new instance.web.Model('gamification.challenge').call('reply_challenge_wizard', [challenge_id]).then(function(res) { + challenge_action['action'] = res; }); - $.when(plan_action).done(function() { - self.do_action(plan_action.action).done(function () { + $.when(challenge_action).done(function() { + self.do_action(challenge_action.action).done(function () { self.get_goal_todo_info(); }); }); @@ -132,7 +132,7 @@ openerp.gamification = function(instance) { instance.web_kanban.KanbanRecord.include({ // open related goals when clicking on challenge kanban view on_card_clicked: function() { - if (this.view.dataset.model === 'gamification.goal.plan') { + if (this.view.dataset.model === 'gamification.challenge') { this.$('.oe_kanban_project_list a').first().click(); } else { this._super.apply(this, arguments); diff --git a/addons/gamification/static/src/xml/gamification.xml b/addons/gamification/static/src/xml/gamification.xml index 9f6bc1c81e9..6bfd4c2ffc9 100644 --- a/addons/gamification/static/src/xml/gamification.xml +++ b/addons/gamification/static/src/xml/gamification.xml @@ -4,37 +4,37 @@
Your goal list is empty
-
+
- e -

+ e +

- +
-
- +
+
-
+
- - + + - - + + -
+
- + Target: - + Target: <= - - + +
@@ -42,52 +42,52 @@
- -
+ +
-
- +
+
- + Target: - + Target: <= - +
-
-
- +
+
+
-
+
- +
- - + + - - - + + + - + - + - - + +
@@ -100,7 +100,7 @@
-

Proposed Challenges

+

Invited Challenges

  • diff --git a/addons/gamification/templates.py b/addons/gamification/templates.py deleted file mode 100644 index b6d071c8f3a..00000000000 --- a/addons/gamification/templates.py +++ /dev/null @@ -1,42 +0,0 @@ -# We use a jinja2 sandboxed environment to render mako templates. -# Note that the rendering does not cover all the mako syntax, in particular -# arbitrary Python statements are not accepted, and not all expressions are -# allowed: only "public" attributes (not starting with '_') of objects may -# be accessed. -# This is done on purpose: it prevents incidental or malicious execution of -# Python code that may break the security of the server. - -from jinja2.sandbox import SandboxedEnvironment -from jinja2 import FileSystemLoader - -from urllib import urlencode, quote as quote -import os.path - -#TODO: to check: new dependancies in openerp? fine or not? -#TODO: to check: if it's ok, i think it would be better directly in the server (tools) so that other modules that doesn't depend on gamification can use it -#TODO; someone else should check this code, i'm not the good one - -class TemplateHelper(SandboxedEnvironment): - - GAMIFICATION_PATH = os.path.dirname(os.path.abspath(__file__)) - - def __init__(self): - - super(TemplateHelper, self).__init__( - loader=FileSystemLoader(os.path.join(self.GAMIFICATION_PATH, 'templates/')), - block_start_string="<%", - block_end_string="%>", - variable_start_string="${", - variable_end_string="}", - comment_start_string="<%doc>", - comment_end_string="", - line_statement_prefix="%", - line_comment_prefix="##", - trim_blocks=True, # do not output newline after blocks - autoescape=True, # XML/HTML automatic escaping - ) - self.globals.update({ - 'str': str, - 'quote': quote, - 'urlencode': urlencode, - }) diff --git a/addons/gamification/templates/group_progress.mako b/addons/gamification/templates/group_progress.mako index 00751f98134..5d71c36032f 100644 --- a/addons/gamification/templates/group_progress.mako +++ b/addons/gamification/templates/group_progress.mako @@ -1,10 +1,10 @@ <% extends 'base.mako' %> <% block body %> - % for planline in planlines_boards: + % for line in lines_boards:
${planline.goal_type.name}${line.goal_definition.name}
#Completeness Current
${goal.user_id.name} ${goal.completeness}% ${goal.current}/${goal.target_goal} - % if goal.type_suffix: - ${goal.type_suffix} + % if goal.definition_suffix: + ${goal.definition_suffix} % endif
- + @@ -12,7 +12,7 @@ - % for idx, goal in planline.board_goals: + % for idx, goal in line.board_goals: % if idx < 3 or goal.user_id.id == uid: = 100: @@ -23,8 +23,8 @@ diff --git a/addons/gamification/templates/personal_progress.mako b/addons/gamification/templates/personal_progress.mako index bd744bb63a5..85d2fbb2b16 100644 --- a/addons/gamification/templates/personal_progress.mako +++ b/addons/gamification/templates/personal_progress.mako @@ -13,15 +13,15 @@ style="font-weight:bold;" % endif > - + diff --git a/addons/gamification/templates/reminder.mako b/addons/gamification/templates/reminder.mako index d3f8a584a03..4d44861a0bc 100644 --- a/addons/gamification/templates/reminder.mako +++ b/addons/gamification/templates/reminder.mako @@ -10,7 +10,7 @@

${object.report_header or ''}

-

You have not updated your progress for the goal ${object.type_id.name} (currently reached at ${object.completeness}%) for at least ${object.remind_update_delay} days. Do not forget to do it.

+

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.

If you have not changed your score yet, you can use the button "The current value is up to date" to indicate so.

diff --git a/addons/gamification/test/goal_demo.yml b/addons/gamification/test/goal_demo.yml index bfa8240fb50..1efc6cc7e74 100644 --- a/addons/gamification/test/goal_demo.yml +++ b/addons/gamification/test/goal_demo.yml @@ -1,29 +1,29 @@ - - In order to test process of the Goals, I assign the discovery goal plan to all users + In order to test process of the Goals, I assign the discovery challenge to all users - - !record {model: gamification.goal.plan, id: plan_base_discover}: + !record {model: gamification.challenge, id: challenge_base_discover}: autojoin_group_id: base.group_user - I verify that the users of the group are added (at least admin and demo) - - !assert {model: gamification.goal.plan, id: plan_base_discover, string: The autojoin function was not successful}: + !assert {model: gamification.challenge, id: challenge_base_discover, string: The autojoin function was not successful}: - len(user_ids) >= 2 - - I start the goal plan and verify the change of state + I start the challenge and verify the change of state - - !python {model: gamification.goal.plan}: | - plan = self.browse(cr, uid, ref('plan_base_discover')) - self.action_start(cr, uid, [plan.id], context=context) - assert plan.state == 'inprogress', "Plan failed the change of state" + !python {model: gamification.challenge}: | + challenge = self.browse(cr, uid, ref('challenge_base_discover')) + self.action_start(cr, uid, [challenge.id], context=context) + assert challenge.state == 'inprogress', "Challenge failed the change of state" - I verify the goals are generated correctly - !python {model: gamification.goal}: | - goal_ids = self.search(cr, uid, [('plan_id', '=', ref('plan_base_discover'))], context=context) + goal_ids = self.search(cr, uid, [('challenge_id', '=', ref('challenge_base_discover'))], context=context) assert len(goal_ids) >= 4, "Not enough goals have been generated" for goal in self.browse(cr, uid, goal_ids, context=context): assert goal.state != 'draft', "Draft goal have been generated" - assert goal.planline_id.plan_id.id == ref('plan_base_discover'), "Linked planline incorrect" + assert goal.line_id.challenge_id.id == ref('challenge_base_discover'), "Linked line incorrect" - I change timezone for demo user - @@ -33,21 +33,21 @@ I check the goal for demo user is successful - !python {model: gamification.goal}: | - goal_ids = self.search(cr, uid, [('user_id', '=', ref('base.user_demo')),('type_id','=',ref('type_base_timezone'))]) + goal_ids = self.search(cr, uid, [('user_id', '=', ref('base.user_demo')),('definition_id','=',ref('definition_base_timezone'))]) self.update(cr, uid, goal_ids, context=context) for goal in self.browse(cr, uid, goal_ids, context=context): assert goal.state == "reached", "Goal not successful %s" % goal.state - I add a reward to the challenge - - !record {model: gamification.goal.plan, id: plan_base_discover}: + !record {model: gamification.challenge, id: challenge_base_discover}: reward_first_id: gamification.badge_good_job reward_failure: True - I check the demo user received the badge - - !python {model: gamification.goal.plan}: | - plan = self.browse(cr, uid, ref('plan_base_discover'), context=context) - self.action_close(cr, uid, [plan.id]) + !python {model: gamification.challenge}: | + challenge = self.browse(cr, uid, ref('challenge_base_discover'), context=context) + self.action_close(cr, uid, [challenge.id]) badge_ids = self.pool.get('gamification.badge.user').search(cr, uid, [('badge_id', '=', ref('badge_good_job')), ('user_id', '=', ref('base.user_demo'))]) assert badge_ids, "Demo users didn't received the badge" \ No newline at end of file diff --git a/addons/gamification/badge_view.xml b/addons/gamification/views/badge.xml similarity index 99% rename from addons/gamification/badge_view.xml rename to addons/gamification/views/badge.xml index 3f2199d1a2b..af401b1f7de 100644 --- a/addons/gamification/badge_view.xml +++ b/addons/gamification/views/badge.xml @@ -101,7 +101,7 @@ - + diff --git a/addons/gamification/plan_view.xml b/addons/gamification/views/challenge.xml similarity index 79% rename from addons/gamification/plan_view.xml rename to addons/gamification/views/challenge.xml index 1f0a872acf5..45cc58056d2 100644 --- a/addons/gamification/plan_view.xml +++ b/addons/gamification/views/challenge.xml @@ -2,11 +2,11 @@ - + Challenges List - gamification.goal.plan + gamification.challenge - + @@ -15,11 +15,11 @@ - + gamification.goal Related Goals kanban,tree - {'search_default_group_by_type': True, 'search_default_inprogress': True, 'search_default_plan_id': active_id, 'default_plan_id': active_id} + {'search_default_group_by_definition': True, 'search_default_inprogress': True, 'search_default_challenge_id': active_id, 'default_challenge_id': active_id}

There is no goals associated to this challenge matching your search. @@ -28,11 +28,11 @@ - + Challenge Form - gamification.goal.plan + gamification.challenge -
+

${planline.goal_type.name}${line.goal_definition.name}
#Completeness Current
${goal.user_id.name} ${goal.completeness}% ${goal.current}/${goal.target_goal} - % if goal.type_suffix: - ${goal.type_suffix} + % if goal.definition_suffix: + ${goal.definition_suffix} % endif
${goal.type_id.name}${goal.definition_id.name} ${goal.target_goal} - % if goal.type_suffix: - ${goal.type_suffix} + % if goal.definition_suffix: + ${goal.definition_suffix} % endif ${goal.current} - % if goal.type_suffix: - ${goal.type_suffix} + % if goal.definition_suffix: + ${goal.definition_suffix} % endif ${goal.completeness} %