[REF] gamification: python tests and other improvements

bzr revid: mat@openerp.com-20131218111243-npnauv6mxhb4nttg
This commit is contained in:
Martin Trigaux 2013-12-18 12:12:43 +01:00
parent b63950e0dc
commit e7887b8b8f
9 changed files with 171 additions and 87 deletions

View File

@ -20,4 +20,5 @@
############################################################################## ##############################################################################
import models import models
import wizard
import data import data

View File

@ -38,19 +38,16 @@ Both goals and badges are flexibles and can be adapted to a large range of modul
""", """,
'data': [ 'data': [
'views/challenge.xml', 'wizard/update_goal.xml',
'wizard/grant_badge.xml',
'views/badge.xml', 'views/badge.xml',
'views/challenge.xml',
'views/goal.xml', 'views/goal.xml',
'data/cron.xml', 'data/cron.xml',
'security/gamification_security.xml', 'security/gamification_security.xml',
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'data/goal_base.xml', 'data/goal_base.xml',
'data/badge.xml', 'data/badge.xml',
'wizard/update_goal.xml',
'wizard/grant_badge.xml',
],
'test': [
'test/goal_demo.yml'
], ],
'installable': True, 'installable': True,
'application': True, 'application': True,

View File

@ -14,17 +14,6 @@
<field name="res_id_field">user.id</field> <field name="res_id_field">user.id</field>
</record> </record>
<record model="gamification.goal.definition" id="definition_base_avatar">
<field name="name">Set your Avatar</field>
<field name="description">In your user preference</field>
<field name="computation_mode">manually</field>
<field name="display_mode">checkbox</field>
<!-- problem : default avatar != False -> manually + check in write function -->
<field name="action_id" eval="ref('base.action_res_users_my')" />
<field name="res_id_field">user.id</field>
</record>
<record model="gamification.goal.definition" id="definition_base_company_data"> <record model="gamification.goal.definition" id="definition_base_company_data">
<field name="name">Set your Company Data</field> <field name="name">Set your Company Data</field>
<field name="description">Write some information about your company (specify at least a name)</field> <field name="description">Write some information about your company (specify at least a name)</field>
@ -104,11 +93,6 @@
<field name="target_goal">1</field> <field name="target_goal">1</field>
<field name="challenge_id" eval="ref('challenge_base_discover')" /> <field name="challenge_id" eval="ref('challenge_base_discover')" />
</record> </record>
<record model="gamification.challenge.line" id="line_base_discover2">
<field name="definition_id" eval="ref('definition_base_avatar')" />
<field name="target_goal">1</field>
<field name="challenge_id" eval="ref('challenge_base_discover')" />
</record>
<record model="gamification.challenge.line" id="line_base_admin2"> <record model="gamification.challenge.line" id="line_base_admin2">
<field name="definition_id" eval="ref('definition_base_company_logo')" /> <field name="definition_id" eval="ref('definition_base_company_logo')" />

View File

@ -197,11 +197,30 @@ class gamification_challenge(osv.Model):
'reward_failure': False, 'reward_failure': False,
} }
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('autojoin_group_id'):
new_group = self.pool.get('res.groups').browse(cr, uid, vals['autojoin_group_id'], context=context)
if 'user_ids' not in vals:
vals['user_ids'] = []
vals['user_ids'] += [(4, user.id) for user in new_group.users]
create_res = super(gamification_challenge, self).create(cr, uid, vals, context=context)
# subscribe new users to the challenge
if vals.get('user_ids'):
# done with browse after super to be sure catch all after orm process
challenge = self.browse(cr, uid, create_res, context=context)
self.message_subscribe_users(cr, uid, [challenge.id], [user.id for user in challenge.user_ids], context=context)
return create_res
def write(self, cr, uid, ids, vals, context=None): def write(self, cr, uid, ids, vals, context=None):
"""Overwrite the write method to add the user of groups""" """Overwrite the write method to add the user of groups"""
if not ids:
return True
# add users when change the group auto-subscription # add users when change the group auto-subscription
if vals.get('autojoin_group_id'): if vals.get('autojoin_group_id'):
new_group = self.pool.get('res.groups').browse(cr, uid, vals['autojoin_group_id'], context=context) new_group = self.pool.get('res.groups').browse(cr, uid, vals['autojoin_group_id'], context=context)
@ -306,6 +325,8 @@ class gamification_challenge(osv.Model):
Change the state of the challenge to in progress and generate related goals Change the state of the challenge to in progress and generate related goals
""" """
if isinstance(ids, (int,long)):
ids = [ids]
# subscribe users if autojoin group # subscribe users if autojoin group
for challenge in self.browse(cr, uid, ids, context=context): for challenge in self.browse(cr, uid, ids, context=context):
if challenge.autojoin_group_id: if challenge.autojoin_group_id:
@ -320,6 +341,8 @@ class gamification_challenge(osv.Model):
Create goals that haven't been created yet (eg: if added users) Create goals that haven't been created yet (eg: if added users)
Recompute the current value for each goal related""" Recompute the current value for each goal related"""
if isinstance(ids, (int,long)):
ids = [ids]
return self._update_all(cr, uid, ids=ids, context=context) return self._update_all(cr, uid, ids=ids, context=context)
def action_close(self, cr, uid, ids, context=None): def action_close(self, cr, uid, ids, context=None):
@ -342,6 +365,8 @@ class gamification_challenge(osv.Model):
Change the state of the challenge to draft Change the state of the challenge to draft
Cancel the related goals""" Cancel the related goals"""
if isinstance(ids, (int,long)):
ids = [ids]
self.write(cr, uid, ids, {'state': 'draft'}, context=context) self.write(cr, uid, ids, {'state': 'draft'}, context=context)
goal_ids = self.pool.get('gamification.goal').search(cr, uid, [('challenge_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) self.pool.get('gamification.goal').write(cr, uid, goal_ids, {'state': 'canceled'}, context=context)
@ -350,6 +375,8 @@ class gamification_challenge(osv.Model):
def action_report_progress(self, cr, uid, ids, context=None): def action_report_progress(self, cr, uid, ids, context=None):
"""Manual report of a goal, does not influence automatic report frequency""" """Manual report of a goal, does not influence automatic report frequency"""
if isinstance(ids, (int,long)):
ids = [ids]
for challenge in self.browse(cr, uid, ids, context): for challenge in self.browse(cr, uid, ids, context):
self.report_progress(cr, uid, challenge, context=context) self.report_progress(cr, uid, challenge, context=context)
return True return True
@ -593,7 +620,7 @@ class gamification_challenge(osv.Model):
result['res_id'] = challenge_id result['res_id'] = challenge_id
return result return result
def check_challenge_reward(self, cr, uid, challenge_ids, force=False, context=None): def check_challenge_reward(self, cr, uid, ids, force=False, context=None):
"""Actions for the end of a challenge """Actions for the end of a challenge
If a reward was selected, grant it to the correct users. If a reward was selected, grant it to the correct users.
@ -603,8 +630,10 @@ class gamification_challenge(osv.Model):
- when a challenge is manually closed - when a challenge is manually closed
(if no end date, a running challenge is never rewarded) (if no end date, a running challenge is never rewarded)
""" """
if isinstance(ids, (int,long)):
ids = [ids]
context = context or {} context = context or {}
for challenge in self.browse(cr, uid, challenge_ids, context=context): for challenge in self.browse(cr, uid, ids, context=context):
(start_date, end_date) = start_end_date_for_period(challenge.period, challenge.start_date, challenge.end_date) (start_date, end_date) = start_end_date_for_period(challenge.period, challenge.start_date, challenge.end_date)
yesterday = date.today() - timedelta(days=1) yesterday = date.today() - timedelta(days=1)
if end_date == yesterday.strftime(DF) or force: if end_date == yesterday.strftime(DF) or force:

View File

@ -307,7 +307,8 @@ class gamification_goal(osv.Model):
def create(self, cr, uid, vals, context=None): def create(self, cr, uid, vals, context=None):
"""Overwrite the create method to add a 'no_remind_goal' field to True""" """Overwrite the create method to add a 'no_remind_goal' field to True"""
context = context or {} if context is None:
context = {}
context['no_remind_goal'] = True context['no_remind_goal'] = True
return super(gamification_goal, self).create(cr, uid, vals, context=context) return super(gamification_goal, self).create(cr, uid, vals, context=context)
@ -317,6 +318,8 @@ class gamification_goal(osv.Model):
If the current value is changed and the report frequency is set to On If the current value is changed and the report frequency is set to On
change, a report is generated change, a report is generated
""" """
if context is None:
context = {}
vals['last_update'] = fields.date.today() vals['last_update'] = fields.date.today()
result = super(gamification_goal, self).write(cr, uid, ids, vals, context=context) result = super(gamification_goal, self).write(cr, uid, ids, vals, context=context)
for goal in self.browse(cr, uid, ids, context=context): for goal in self.browse(cr, uid, ids, context=context):

View File

@ -31,6 +31,7 @@ class res_users_gamification_group(osv.Model):
_inherit = ['res.users'] _inherit = ['res.users']
def write(self, cr, uid, ids, vals, context=None): def write(self, cr, uid, ids, vals, context=None):
"""Overwrite to autosubscribe users if added to a group marked as autojoin, user will be added to challenge"""
write_res = super(res_users_gamification_group, self).write(cr, uid, ids, vals, context=context) write_res = super(res_users_gamification_group, self).write(cr, uid, ids, vals, context=context)
if vals.get('groups_id'): if vals.get('groups_id'):
# form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]} # form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]}
@ -41,12 +42,20 @@ class res_users_gamification_group(osv.Model):
challenge_ids = challenge_obj.search(cr, uid, [('autojoin_group_id', 'in', user_group_ids)], context=context) challenge_ids = challenge_obj.search(cr, uid, [('autojoin_group_id', 'in', user_group_ids)], context=context)
if challenge_ids: if challenge_ids:
challenge_obj.write(cr, uid, challenge_ids, {'user_ids': [(4, user_id) for user_id in ids]}, context=context) challenge_obj.write(cr, uid, challenge_ids, {'user_ids': [(4, user_id) for user_id in ids]}, context=context)
return write_res
if vals.get('image'): def create(self, cr, uid, vals, context=None):
goal_definition_id = self.pool.get('ir.model.data').get_object(cr, uid, 'gamification', 'definition_base_avatar', context) """Overwrite to autosubscribe users if added to a group marked as autojoin, user will be added to challenge"""
goal_ids = self.pool.get('gamification.goal').search(cr, uid, [('definition_id', '=', goal_definition_id.id), ('user_id', 'in', ids)], context=context) write_res = super(res_users_gamification_group, self).create(cr, uid, vals, context=context)
values = {'state': 'reached', 'current': 1} if vals.get('groups_id'):
self.pool.get('gamification.goal').write(cr, uid, goal_ids, values, context=context) # form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]}
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]]
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, write_res)]}, context=context)
return write_res return write_res
def get_goals_todo_info(self, cr, uid, context=None): def get_goals_todo_info(self, cr, uid, context=None):
@ -174,4 +183,3 @@ class res_groups_gamification_group(osv.Model):
if challenge_ids: if challenge_ids:
challenge_obj.write(cr, uid, challenge_ids, {'user_ids': [(4, user_id) for user_id in user_ids]}, context=context) challenge_obj.write(cr, uid, challenge_ids, {'user_ids': [(4, user_id) for user_id in user_ids]}, context=context)
return write_res return write_res
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,53 +0,0 @@
-
In order to test process of the Goals, I assign the discovery challenge to all users
-
!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.challenge, id: challenge_base_discover, string: The autojoin function was not successful}:
- len(user_ids) >= 2
-
I start the challenge and verify 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, [('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.line_id.challenge_id.id == ref('challenge_base_discover'), "Linked line incorrect"
-
I change timezone for demo user
-
!record {model: res.users, id: base.user_demo}:
tz: "Europe/Brussels"
-
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')),('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.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.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"

View File

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2013 OpenERP S.A. <http://openerp.com>
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import test_challenge
checks = [
test_challenge,
]

View File

@ -0,0 +1,89 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Business Applications
# Copyright (c) 2013 OpenERP S.A. <http://openerp.com>
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp.tests import common
class test_challenge(common.TransactionCase):
def setUp(self):
super(test_challenge, self).setUp()
cr, uid = self.cr, self.uid
self.data_obj = self.registry('ir.model.data')
self.user_obj = self.registry('res.users')
self.challenge_obj = self.registry('gamification.challenge')
self.line_obj = self.registry('gamification.challenge.line')
self.goal_obj = self.registry('gamification.goal')
self.badge_obj = self.registry('gamification.badge')
self.badge_user_obj = self.registry('gamification.badge.user')
self.demo_user_id = self.data_obj.get_object_reference(cr, uid, 'base', 'user_demo')[1]
self.group_user_id = self.data_obj.get_object_reference(cr, uid, 'base', 'group_user')[1]
self.challenge_base_id = self.data_obj.get_object_reference(cr, uid, 'gamification', 'challenge_base_discover')[1]
self.definition_timezone_id = self.data_obj.get_object_reference(cr, uid, 'gamification', 'definition_base_timezone')[1]
self.badge_id = self.data_obj.get_object_reference(cr, uid, 'gamification', 'badge_good_job')[1]
def test_00_join_challenge(self):
cr, uid, context = self.cr, self.uid, {}
user_ids = self.user_obj.search(cr, uid, [('groups_id', '=', self.group_user_id)])
challenge = self.challenge_obj.browse(cr, uid, self.challenge_base_id, context=context)
self.assertGreaterEqual(len(challenge.user_ids), len(user_ids), "Not enough users in base challenge")
self.user_obj.create(cr, uid, {
'name': 'R2D2',
'login': 'r2d2@openerp.com',
'email': 'r2d2@openerp.com',
'groups_id': [(6, 0, [self.group_user_id])]
}, {'no_reset_password': True})
challenge = self.challenge_obj.browse(cr, uid, self.challenge_base_id, context=context)
self.assertGreaterEqual(len(challenge.user_ids), len(user_ids)+1, "These are not droids you are looking for")
def test_10_reach_challenge(self):
cr, uid, context = self.cr, self.uid, {}
self.challenge_obj.action_start(cr, uid, [self.challenge_base_id], context=context)
challenge = self.challenge_obj.browse(cr, uid, self.challenge_base_id, context=context)
challenge_user_ids = [user.id for user in challenge.user_ids]
self.assertEqual(challenge.state, 'inprogress', "Challenge failed the change of state")
line_ids = self.line_obj.search(cr, uid, [('challenge_id', '=', self.challenge_base_id)], context=context)
goal_ids = self.goal_obj.search(cr, uid, [('challenge_id', '=', self.challenge_base_id), ('state', '!=', 'draft')], context=context)
self.assertEqual(len(goal_ids), len(line_ids)*len(challenge_user_ids), "Incorrect number of goals generated, should be 1 goal per user, per challenge line")
# demo user will set a timezone
self.user_obj.write(cr, uid, self.demo_user_id, {'tz': "Europe/Brussels"}, context=context)
goal_ids = self.goal_obj.search(cr, uid, [('user_id', '=', self.demo_user_id), ('definition_id', '=', self.definition_timezone_id)], context=context)
self.goal_obj.update(cr, uid, goal_ids, context=context)
reached_goal_ids = self.goal_obj.search(cr, uid, [('id', 'in', goal_ids), ('state', '=', 'reached')], context=context)
self.assertEqual(set(goal_ids), set(reached_goal_ids), "Not every goal was reached after changing timezone")
# reward for two firsts as admin may have timezone
self.challenge_obj.write(cr, uid, self.challenge_base_id, {'reward_first_id': self.badge_id, 'reward_second_id': self.badge_id}, context=context)
self.challenge_obj.action_close(cr, uid, self.challenge_base_id, context=context)
badge_ids = self.badge_user_obj.search(cr, uid, [('badge_id', '=', self.badge_id), ('user_id', '=', self.demo_user_id)])
self.assertGreater(len(badge_ids), 0, "Demo user has not received the badge")