[IMP] gamification : add ranks based on karma

To encourage forum and slides users to be more active ranks are now added.
They are directly linked to karma. The more the user has karma the more his
rank will be high.

The default rank is Newbie, with 1 point of karma. Users with 0 karma are
considered as inactive on forum or slides. When a user reach a new rank
a mail is sent to him to congratulate him with his new rank.

To add a button in the mail template to allow users to go directly on
a website section (like forum or slides) simply override
get_gamification_redirection_data to add the target url.

Partial commit linked to eLearning project. Main specifications related
to gamification and user profile can be found on task 1922159 (PR #30514).
Main specifications related to eLearning can be found on task 1902304
(PR #29876).
This commit is contained in:
David Beguin
2019-02-07 12:13:28 +00:00
committed by Thibault Delavallée
parent 3b9dcb6d8d
commit 9e082662af
14 changed files with 277 additions and 7 deletions
+6
View File
@@ -30,6 +30,12 @@ Both goals and badges are flexibles and can be adapted to a large range of modul
'security/ir.model.access.csv',
'data/goal_base.xml',
'data/badge.xml',
'data/gamification_karma_rank_data.xml',
'views/gamification.xml',
'views/gamification_karma_rank_views.xml',
'views/mail_templates.xml'
],
'demo': [
'data/gamification_karma_rank_demo.xml'
],
}
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!--Ranks-->
<record id="rank_newbie" model="gamification.karma.rank">
<field name="name">Newbie</field>
<field name="description" type="html"><p>You just began the adventure! Welcome!</p></field>
<field name="description_reach_next" type="html"><p>Keep going !</p></field>
<field name="karma_required">1</field>
<field name="image" type="base64" file="gamification/static/img/rank_newbie.png"/>
</record>
</data>
</odoo>
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo><data>
<!--Ranks-->
<record id="rank_student" model="gamification.karma.rank">
<field name="name">Student</field>
<field name="description" type="html"><p>You begin to understand things ! Keep going!</p></field>
<field name="description_reach_next" type="html"><p>Keep going !</p></field>
<field name="karma_required">100</field>
<field name="image" type="base64" file="gamification/static/img/rank_student.png"/>
</record>
<record id="rank_bachelor" model="gamification.karma.rank">
<field name="name">Bachelor</field>
<field name="description" type="html"><p>Hey ! You're not so dumb after all !</p></field>
<field name="description_reach_next" type="html"><p>Keep going !</p></field>
<field name="karma_required">500</field>
<field name="image" type="base64" file="gamification/static/img/rank_bachelor.png"/>
</record>
<record id="rank_master" model="gamification.karma.rank">
<field name="name">Master</field>
<field name="description" type="html"><p>You're good ! Really Good !</p></field>
<field name="description_reach_next" type="html"><p>Keep going !</p></field>
<field name="karma_required">2000</field>
<field name="image" type="base64" file="gamification/static/img/rank_master.png"/>
</record>
<record id="rank_doctor" model="gamification.karma.rank">
<field name="name">Doctor</field>
<field name="description" type="html"><p>Ok, RESPECT !</p></field>
<field name="description_reach_next" type="html"><p>Keep going !</p></field>
<field name="karma_required">10000</field>
<field name="image" type="base64" file="gamification/static/img/rank_doctor.png"/>
</record>
</data></odoo>
@@ -3,7 +3,52 @@
from werkzeug.exceptions import Forbidden
from odoo import api, tools, fields, models
from odoo.tools.translate import html_translate
class KarmaError(Forbidden):
""" Karma-related error, used for forum and posts. """
pass
class KarmaRank(models.Model):
_name = 'gamification.karma.rank'
_description = 'Rank based on karma'
_order = 'karma_required'
name = fields.Text(string='Rank Name', translate=True, required=True)
description = fields.Html(string='Description', translate=html_translate, sanitize_attributes=False,)
description_reach_next = fields.Html(
string='Motivation phrase', translate=html_translate, sanitize_attributes=False,
help="Motivation phrase to reach next rank")
karma_required = fields.Integer(string='Required Karma', help='Minimum karma needed to reach this rank')
user_ids = fields.One2many('res.users', 'rank_id', string='Users', help="Users having this rank")
image = fields.Binary('Rank Icon')
image_medium = fields.Binary(
"Medium-sized rank icon",
help="Medium-sized icon of the rank. It is automatically "
"resized as a 128x128px image, with aspect ratio preserved. "
"Use this field in form views or some kanban views.")
image_small = fields.Binary(
"Small-sized rank icon",
help="Small-sized icon of the rank. It is automatically "
"resized as a 64x64px image, with aspect ratio preserved. "
"Use this field anywhere a small image is required.")
@api.model_create_multi
def create(self, values_list):
for vals in values_list:
tools.image_resize_images(vals)
res = super(KarmaRank, self).create(values_list)
users = self.env['res.users'].sudo().search([('karma', '>', 0)])
users._recompute_rank()
return res
@api.multi
def write(self, vals):
tools.image_resize_images(vals)
res = super(KarmaRank, self).write(vals)
users = self.env['res.users'].sudo().search([('karma', '>', 0)])
users._recompute_rank()
return res
+51 -6
View File
@@ -12,12 +12,8 @@ class Users(models.Model):
gold_badge = fields.Integer('Gold badges count', compute="_get_user_badge_level")
silver_badge = fields.Integer('Silver badges count', compute="_get_user_badge_level")
bronze_badge = fields.Integer('Bronze badges count', compute="_get_user_badge_level")
@api.multi
def add_karma(self, karma):
for user in self:
user.karma += karma
return True
rank_id = fields.Many2one('gamification.karma.rank', 'Rank', index=False)
next_rank_id = fields.Many2one('gamification.karma.rank', 'Next Rank', index=False)
@api.multi
@api.depends('badge_ids')
@@ -42,3 +38,52 @@ class Users(models.Model):
for (user_id, level, count) in self.env.cr.fetchall():
# levels are gold, silver, bronze but fields have _badge postfix
self.browse(user_id)['{}_badge'.format(level)] = count
@api.model_create_multi
def create(self, values_list):
res = super(Users, self).create(values_list)
res._recompute_rank()
return res
@api.multi
def write(self, vals):
if 'karma' in vals:
self._recompute_rank()
return super(Users, self).write(vals)
@api.multi
def add_karma(self, karma):
for user in self:
user.karma += karma
return True
def _rank_changed(self):
if self.rank_id.karma_required > 0:
template = self.env.ref('gamification.mail_template_data_new_rank_reached', raise_if_not_found=False)
if template:
template.send_mail(self.id, force_send=True, notif_layout='mail.mail_notification_light')
def _recompute_rank(self):
"""
The caller should filter the users on karma > 0 before calling this method
to avoid looping on every single users
"""
ranks = [{'rank': rank, 'karma_required': rank.karma_required} for rank in
self.env['gamification.karma.rank'].search([], order="karma_required DESC")]
for user in self:
for i in range(0, len(ranks)):
if user.karma >= ranks[i]['karma_required']:
if user.rank_id != ranks[i]['rank']:
user.rank_id = ranks[i]['rank'].id
user.next_rank_id = ranks[i - 1]['rank'].id if i > 0 else False
user._rank_changed()
break
def get_gamification_redirection_data(self):
"""
Hook for other modules to add redirect button(s) in new rank reached mail
Must return a list of dictionnary including url and label.
E.g. return [{'url': '/forum', label: 'Go to Forum'}]
"""
self.ensure_one()
return []
@@ -25,3 +25,6 @@ badge_user_employee,"Badge-user Employee",model_gamification_badge_user,base.gro
badge_user_manager,"Badge-user Manager",model_gamification_badge_user,base.group_erp_manager,1,1,1,1
badge_user_portal,"Badge-user Portal",gamification.model_gamification_badge_user,base.group_portal,1,1,1,0
badge_user_public,"Badge-user Public",gamification.model_gamification_badge_user,base.group_public,1,0,0,0
gamification_karma_rank_access_all,gamification.karma.rank.access.all,gamification.model_gamification_karma_rank,,1,0,0,0
gamification_karma_rank_access_user_manager,gamification.karma.rank.access.user.manager,gamification.model_gamification_karma_rank,base.group_erp_manager,1,1,1,1
1 id name model_id/id group_id/id perm_read perm_write perm_create perm_unlink
25
26
27
28
29
30
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

@@ -0,0 +1,71 @@
<odoo>
<data>
<!-- Ranks views -->
<record id="gamification_karma_ranks_action" model="ir.actions.act_window">
<field name="name">Ranks</field>
<field name="res_model">gamification.karma.rank</field>
<field name="view_mode">tree,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Create a new rank
</p><p>
A rank correspond to a fixed karma level. The more you have karma, the more your rank is high.
This is used to quickly know which user is new or old or highly or not active.
</p>
</field>
</record>
<record id="gamification_karma_ranks_view_search" model="ir.ui.view">
<field name="name">gamification.karma.ranks.view.search</field>
<field name="model">gamification.karma.rank</field>
<field name="arch" type="xml">
<search string="Search Ranks">
<field name="name"/>
<field name="karma_required"/>
<field name="description"/>
<field name="user_ids"/>
</search>
</field>
</record>
<record id="gamification_karma_ranks_view_tree" model="ir.ui.view">
<field name="name">gamification.karma.ranks.view.tree</field>
<field name="model">gamification.karma.rank</field>
<field name="arch" type="xml">
<tree string="Ranks List">
<field name="name"/>
<field name="karma_required"/>
<field name="description"/>
<field name="user_ids"/>
</tree>
</field>
</record>
<record id="gamification_karma_rank_view_form" model="ir.ui.view">
<field name="name">gamification.karma.rank.view.form</field>
<field name="model">gamification.karma.rank</field>
<field name="arch" type="xml">
<form string="Rank">
<sheet>
<div class="oe_button_box" name="button_box"/>
<field name="image" widget='image' class="oe_avatar"/>
<div class="oe_title">
<label for="name" class="oe_edit_only"/>
<h1>
<field name="name" placeholder="e.g. Master Chief"/>
</h1>
</div>
<group>
<field name="karma_required"/>
<field name="description" placeholder="e.g. A Master Chief knows quite everything on the forum! You cannot beat him!"/>
<field name="create_date" invisible="1"/>
</group>
<group string="Users to that rank" attrs="{'invisible': [('create_date','=',False)]}">
<field name="user_ids" readonly="1" />
</group>
</sheet>
</form>
</field>
</record>
</data>
</odoo>
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="mail_template_data_new_rank_reached" model="mail.template">
<field name="name">New rank reached</field>
<field name="model_id" ref="base.model_res_users"/>
<field name="subject">Reception of ${object.name}</field>
<field name="email_to">${(object.partner_id.email_formatted or object.email) |safe}</field>
<field name="partner_to">${object.partner_id.id}</field>
<field name="subject">New rank: ${object.rank_id.name}</field>
<field name="lang">${object.partner_id.lang}</field>
<field name="auto_delete" eval="True" />
<field name="body_html" type="html">
<div style="background:#F0F0F0;color:#515166;padding:10px 0px;font-family:Arial,Helvetica,sans-serif;font-size:14px;">
<table style="width:600px;margin:0px auto;background:white;border:1px solid #e1e1e1;">
<tbody>
<tr>
<td style="padding:15px 20px 10px 20px;">
<p>
Congratulations
<span>${object.partner_id.name or ''}</span>!
</p>
<p>
You just reached a new rank : <strong>${object.rank_id.name}</strong>
</p>
<p>Continue your work to become a <strong>${object.next_rank_id.name}</strong> !</p>
<div style="margin: 16px 0px 16px 0px;">
% set gamification_redirection_data = object.get_gamification_redirection_data()
% for data in gamification_redirection_data
% set url = data['url']
% set label = data['label']
<a href="${url}" style="background-color: #875A7B; padding: 8px 16px 8px 16px; text-decoration: none; color: #fff; border-radius: 5px; font-size:13px;">${label}</a>
% endfor
</div>
</td>
<td style="padding:15px 20px 10px 20px;">
<p style="text-align: center;">
<img src="/web/image/gamification.karma.rank/${object.rank_id.id}/image_medium"/>
</p>
</td>
</tr>
</tbody>
</table>
</div>
<div>
Thank you,<br/>
</div>
</field>
</record>
</data>
</odoo>
+1 -1
View File
@@ -293,6 +293,6 @@
</record>
<menuitem id="menu_forum_tag_global" parent="menu_website_forum_global" name="Tag" action="forum_tag_action" sequence="30"/>
<menuitem id="menu_forum_rank_global" parent="menu_website_forum_global" name="Ranks" action="gamification.gamification_karma_ranks_action" sequence="5"/>
</data>
</odoo>