[MERGE] gamification: track karma changes on users
PURPOSE Allow karma gain tracking enabling notably display of top users based on weekly / monthly gain in website profile. SPECIFICATIONS Each time a user gains karma a record is created in the gamification karma tracking model. Scheduled activity runs to consolidate the records into monthly gain records to avoid having crowdy table and unnecessary noise in karma gain. This model is made private and only accessible through some dedicated compute methods / controllers used in website profile. In website profile module buttons are added to see users ranking based on their total karma (like before) but also by last week and last month gains (using the newly introduced tracking model). Some fixes are provided in this merge as well as tests. LINKS Task ID 2003505 PR #34594 Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
@@ -33,9 +33,11 @@ Both goals and badges are flexibles and can be adapted to a large range of modul
|
||||
'data/gamification_karma_rank_data.xml',
|
||||
'views/gamification.xml',
|
||||
'views/gamification_karma_rank_views.xml',
|
||||
'views/gamification_karma_tracking_views.xml',
|
||||
'views/mail_templates.xml'
|
||||
],
|
||||
'demo': [
|
||||
'data/gamification_karma_rank_demo.xml'
|
||||
'data/gamification_karma_rank_demo.xml',
|
||||
'data/gamification_karma_tracking_demo.xml',
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record forcecreate="True" id="ir_cron_check_challenge" model="ir.cron">
|
||||
@@ -10,5 +11,17 @@
|
||||
<field name="numbercall">-1</field>
|
||||
<field eval="False" name="doall" />
|
||||
</record>
|
||||
|
||||
<record id="ir_cron_consolidate_last_month" model="ir.cron">
|
||||
<field name="name">Gamification: Karma tracking consolidation</field>
|
||||
<field name="model_id" ref="model_gamification_karma_tracking"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model._consolidate_last_month()</field>
|
||||
<field name="active" eval="True"/>
|
||||
<field name="interval_number">1</field>
|
||||
<field name="interval_type">months</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="nextcall" eval="(DateTime.now() + relativedelta(day=1, months=1)).strftime('%Y-%m-%d 04:00:00')" />
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo><data noupdate="1">
|
||||
<!-- Set demo karma for test tour as demo user-->
|
||||
<record id="base.user_demo" model="res.users">
|
||||
<field name="karma">20</field>
|
||||
</record>
|
||||
<record id="base.demo_user0" model="res.users">
|
||||
<field name="karma">10</field>
|
||||
</record>
|
||||
|
||||
<!--Ranks-->
|
||||
<record id="rank_student" model="gamification.karma.rank">
|
||||
<field name="description_motivational" type="html">
|
||||
@@ -44,5 +36,4 @@
|
||||
</div>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data></odoo>
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo><data noupdate="1">
|
||||
|
||||
<!--base.user_demo-->
|
||||
<record id="karma_tracking_user_demo_1st_day_last_month" model="gamification.karma.tracking">
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="old_value">0</field>
|
||||
<field name="new_value">1000</field>
|
||||
<field name="tracking_date" eval="(DateTime.now() - relativedelta(day=1, months=1)).strftime('%Y-%m-%d')"/>
|
||||
</record>
|
||||
<record id="karma_tracking_user_demo_2nd_day_last_month" model="gamification.karma.tracking">
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="old_value">1000</field>
|
||||
<field name="new_value">1500</field>
|
||||
<field name="tracking_date" eval="(DateTime.now() - relativedelta(day=2, months=1)).strftime('%Y-%m-%d')"/>
|
||||
</record>
|
||||
<record id="karma_tracking_user_demo_5th_day_last_month" model="gamification.karma.tracking">
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="old_value">1500</field>
|
||||
<field name="new_value">2000</field>
|
||||
<field name="tracking_date" eval="(DateTime.now() - relativedelta(day=5, months=1)).strftime('%Y-%m-%d')"/>
|
||||
</record>
|
||||
<record id="karma_tracking_user_demo_20th_day_last_month" model="gamification.karma.tracking">
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="old_value">2000</field>
|
||||
<field name="new_value">2050</field>
|
||||
<field name="tracking_date" eval="(DateTime.now() - relativedelta(day=20, months=1)).strftime('%Y-%m-%d')"/>
|
||||
</record>
|
||||
<record id="base.user_demo" model="res.users">
|
||||
<field name="karma">2500</field>
|
||||
</record>
|
||||
|
||||
<!--base.demo_user0 -->
|
||||
<record id="karma_tracking_user_portal_2nd_day_last_month" model="gamification.karma.tracking">
|
||||
<field name="user_id" ref="base.demo_user0"/>
|
||||
<field name="old_value">0</field>
|
||||
<field name="new_value">5</field>
|
||||
<field name="tracking_date" eval="(DateTime.now() - relativedelta(day=2, months=1)).strftime('%Y-%m-%d')"/>
|
||||
</record>
|
||||
<record id="karma_tracking_user_portal_3rd_day_last_month" model="gamification.karma.tracking">
|
||||
<field name="user_id" ref="base.demo_user0"/>
|
||||
<field name="old_value">5</field>
|
||||
<field name="new_value">10</field>
|
||||
<field name="tracking_date" eval="(DateTime.now() - relativedelta(day=3, months=1)).strftime('%Y-%m-%d')"/>
|
||||
</record>
|
||||
<record id="karma_tracking_user_portal_10th_day_last_month" model="gamification.karma.tracking">
|
||||
<field name="user_id" ref="base.demo_user0"/>
|
||||
<field name="old_value">10</field>
|
||||
<field name="new_value">20</field>
|
||||
<field name="tracking_date" eval="(DateTime.now() - relativedelta(day=10, months=1)).strftime('%Y-%m-%d')"/>
|
||||
</record>
|
||||
<record id="karma_tracking_user_portal_today_0" model="gamification.karma.tracking">
|
||||
<field name="user_id" ref="base.demo_user0"/>
|
||||
<field name="old_value">20</field>
|
||||
<field name="new_value">25</field>
|
||||
<field name="tracking_date" eval="(DateTime.now()).strftime('%Y-%m-%d')"/>
|
||||
</record>
|
||||
<record id="base.demo_user0" model="res.users">
|
||||
<field name="karma">30</field>
|
||||
</record>
|
||||
|
||||
<!--base.user_admin (already have a tracking to 2500)-->
|
||||
<record id="karma_tracking_user_admin_1st_day_last_month" model="gamification.karma.tracking">
|
||||
<field name="user_id" ref="base.user_admin"/>
|
||||
<field name="old_value">0</field>
|
||||
<field name="new_value">2000</field>
|
||||
<field name="tracking_date" eval="(DateTime.now() - relativedelta(day=1, months=1)).strftime('%Y-%m-%d')"/>
|
||||
</record>
|
||||
<record id="karma_tracking_user_admin_5th_day_last_month" model="gamification.karma.tracking">
|
||||
<field name="user_id" ref="base.user_admin"/>
|
||||
<field name="old_value">2000</field>
|
||||
<field name="new_value">2250</field>
|
||||
<field name="tracking_date" eval="(DateTime.now() - relativedelta(day=5, months=1)).strftime('%Y-%m-%d')"/>
|
||||
</record>
|
||||
<record id="karma_tracking_user_admin_today" model="gamification.karma.tracking">
|
||||
<field name="user_id" ref="base.user_admin"/>
|
||||
<field name="old_value">2250</field>
|
||||
<field name="new_value">2500</field>
|
||||
<field name="tracking_date" eval="(DateTime.now()).strftime('%Y-%m-%d')"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -5,4 +5,5 @@ from . import goal
|
||||
from . import challenge
|
||||
from . import badge
|
||||
from . import gamification_karma_rank
|
||||
from . import gamification_karma_tracking
|
||||
from . import res_users
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import calendar
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class KarmaTracking(models.Model):
|
||||
_name = 'gamification.karma.tracking'
|
||||
_description = 'Track Karma Changes'
|
||||
_rec_name = 'user_id'
|
||||
_order = 'tracking_date DESC'
|
||||
|
||||
user_id = fields.Many2one('res.users', 'User', index=True, readonly=True, required=True, ondelete='cascade')
|
||||
old_value = fields.Integer('Old Karma Value', required=True, readonly=True)
|
||||
new_value = fields.Integer('New Karma Value', required=True, readonly=True)
|
||||
consolidated = fields.Boolean('Consolidated')
|
||||
tracking_date = fields.Date(default=fields.Date.context_today)
|
||||
|
||||
@api.model
|
||||
def _consolidate_last_month(self):
|
||||
""" Consolidate last month. Used by a cron to cleanup tracking records. """
|
||||
previous_month_start = fields.Date.today() + relativedelta(months=-1, day=1)
|
||||
return self._process_consolidate(previous_month_start)
|
||||
|
||||
def _process_consolidate(self, from_date):
|
||||
""" Consolidate trackings into a single record for a given month, starting
|
||||
at a from_date (included). End date is set to last day of current month
|
||||
using a smart calendar.monthrange construction. """
|
||||
end_date = from_date + relativedelta(day=calendar.monthrange(from_date.year, from_date.month)[1])
|
||||
select_query = """
|
||||
SELECT user_id,
|
||||
(
|
||||
SELECT old_value from gamification_karma_tracking old_tracking
|
||||
WHERE old_tracking.user_id = gamification_karma_tracking.user_id
|
||||
AND tracking_date::timestamp BETWEEN %(from_date)s AND %(to_date)s
|
||||
AND consolidated IS NOT TRUE
|
||||
ORDER BY tracking_date ASC LIMIT 1
|
||||
), (
|
||||
SELECT new_value from gamification_karma_tracking new_tracking
|
||||
WHERE new_tracking.user_id = gamification_karma_tracking.user_id
|
||||
AND tracking_date::timestamp BETWEEN %(from_date)s AND %(to_date)s
|
||||
AND consolidated IS NOT TRUE
|
||||
ORDER BY tracking_date DESC LIMIT 1
|
||||
)
|
||||
FROM gamification_karma_tracking
|
||||
WHERE tracking_date::timestamp BETWEEN %(from_date)s AND %(to_date)s
|
||||
AND consolidated IS NOT TRUE
|
||||
GROUP BY user_id """
|
||||
self.env.cr.execute(select_query, {
|
||||
'from_date': from_date,
|
||||
'to_date': end_date,
|
||||
})
|
||||
results = self.env.cr.dictfetchall()
|
||||
if results:
|
||||
for result in results:
|
||||
result['consolidated'] = True
|
||||
result['tracking_date'] = fields.Date.to_string(from_date)
|
||||
self.create(results)
|
||||
|
||||
self.search([
|
||||
('tracking_date', '>=', from_date),
|
||||
('tracking_date', '<=', end_date),
|
||||
('consolidated', '!=', True)]
|
||||
).unlink()
|
||||
return True
|
||||
@@ -3,16 +3,12 @@
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
import logging
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Users(models.Model):
|
||||
_inherit = 'res.users'
|
||||
|
||||
|
||||
karma = fields.Integer('Karma', default=0)
|
||||
karma_tracking_ids = fields.One2many('gamification.karma.tracking', 'user_id', string='Karma Changes', groups="base.group_system")
|
||||
badge_ids = fields.One2many('gamification.badge.user', 'user_id', string='Badges', copy=False)
|
||||
gold_badge = fields.Integer('Gold badges count', compute="_get_user_badge_level")
|
||||
silver_badge = fields.Integer('Silver badges count', compute="_get_user_badge_level")
|
||||
@@ -46,11 +42,28 @@ class Users(models.Model):
|
||||
@api.model_create_multi
|
||||
def create(self, values_list):
|
||||
res = super(Users, self).create(values_list)
|
||||
|
||||
karma_trackings = []
|
||||
for user in res:
|
||||
if user.karma:
|
||||
karma_trackings.append({'user_id': user.id, 'old_value': 0, 'new_value': user.karma})
|
||||
if karma_trackings:
|
||||
self.env['gamification.karma.tracking'].sudo().create(karma_trackings)
|
||||
|
||||
res._recompute_rank()
|
||||
return res
|
||||
|
||||
def write(self, vals):
|
||||
karma_trackings = []
|
||||
if 'karma' in vals:
|
||||
for user in self:
|
||||
if user.karma != vals['karma']:
|
||||
karma_trackings.append({'user_id': user.id, 'old_value': user.karma, 'new_value': vals['karma']})
|
||||
|
||||
result = super(Users, self).write(vals)
|
||||
|
||||
if karma_trackings:
|
||||
self.env['gamification.karma.tracking'].sudo().create(karma_trackings)
|
||||
if 'karma' in vals:
|
||||
self._recompute_rank()
|
||||
return result
|
||||
@@ -60,6 +73,109 @@ class Users(models.Model):
|
||||
user.karma += karma
|
||||
return True
|
||||
|
||||
def _get_tracking_karma_gain_position(self, user_domain, from_date=None, to_date=None):
|
||||
""" Get absolute position in term of gained karma for users. First a ranking
|
||||
of all users is done given a user_domain; then the position of each user
|
||||
belonging to the current record set is extracted.
|
||||
|
||||
Example: in website profile, search users with name containing Norbert. Their
|
||||
positions should not be 1 to 4 (assuming 4 results), but their actual position
|
||||
in the karma gain ranking (with example user_domain being karma > 1,
|
||||
website published True).
|
||||
|
||||
:param user_domain: general domain (i.e. active, karma > 1, website, ...)
|
||||
to compute the absolute position of the current record set
|
||||
:param from_date: compute karma gained after this date (included) or from
|
||||
beginning of time;
|
||||
:param to_date: compute karma gained before this date (included) or until
|
||||
end of time;
|
||||
|
||||
:return list: [{
|
||||
'user_id': user_id (belonging to current record set),
|
||||
'karma_gain_total': integer, karma gained in the given timeframe,
|
||||
'karma_position': integer, ranking position
|
||||
}, {..}] ordered by karma_position desc
|
||||
"""
|
||||
if not self:
|
||||
return []
|
||||
|
||||
where_query = self.env['res.users']._where_calc(user_domain)
|
||||
user_from_clause, user_where_clause, where_clause_params = where_query.get_sql()
|
||||
|
||||
params = []
|
||||
if from_date:
|
||||
date_from_condition = 'AND tracking.tracking_date::timestamp >= timestamp %s'
|
||||
params.append(from_date)
|
||||
if to_date:
|
||||
date_to_condition = 'AND tracking.tracking_date::timestamp <= timestamp %s'
|
||||
params.append(to_date)
|
||||
params.append(tuple(self.ids))
|
||||
|
||||
query = """
|
||||
SELECT final.user_id, final.karma_gain_total, final.karma_position
|
||||
FROM (
|
||||
SELECT intermediate.user_id, intermediate.karma_gain_total, row_number() OVER (ORDER BY intermediate.karma_gain_total DESC) AS karma_position
|
||||
FROM (
|
||||
SELECT "res_users".id as user_id, COALESCE(SUM("tracking".new_value - "tracking".old_value), 0) as karma_gain_total
|
||||
FROM %(user_from_clause)s
|
||||
LEFT JOIN "gamification_karma_tracking" as "tracking"
|
||||
ON "res_users".id = "tracking".user_id AND "res_users"."active" = TRUE
|
||||
WHERE %(user_where_clause)s %(date_from_condition)s %(date_to_condition)s
|
||||
GROUP BY "res_users".id
|
||||
ORDER BY karma_gain_total DESC
|
||||
) intermediate
|
||||
) final
|
||||
WHERE final.user_id IN %%s""" % {
|
||||
'user_from_clause': user_from_clause,
|
||||
'user_where_clause': user_where_clause or (not from_date and not to_date and 'TRUE') or '',
|
||||
'date_from_condition': date_from_condition if from_date else '',
|
||||
'date_to_condition': date_to_condition if to_date else ''
|
||||
}
|
||||
|
||||
self.env.cr.execute(query, tuple(where_clause_params + params))
|
||||
return self.env.cr.dictfetchall()
|
||||
|
||||
def _get_karma_position(self, user_domain):
|
||||
""" Get absolute position in term of total karma for users. First a ranking
|
||||
of all users is done given a user_domain; then the position of each user
|
||||
belonging to the current record set is extracted.
|
||||
|
||||
Example: in website profile, search users with name containing Norbert. Their
|
||||
positions should not be 1 to 4 (assuming 4 results), but their actual position
|
||||
in the total karma ranking (with example user_domain being karma > 1,
|
||||
website published True).
|
||||
|
||||
:param user_domain: general domain (i.e. active, karma > 1, website, ...)
|
||||
to compute the absolute position of the current record set
|
||||
|
||||
:return list: [{
|
||||
'user_id': user_id (belonging to current record set),
|
||||
'karma_position': integer, ranking position
|
||||
}, {..}] ordered by karma_position desc
|
||||
"""
|
||||
if not self:
|
||||
return {}
|
||||
|
||||
where_query = self.env['res.users']._where_calc(user_domain)
|
||||
user_from_clause, user_where_clause, where_clause_params = where_query.get_sql()
|
||||
|
||||
# we search on every user in the DB to get the real positioning (not the one inside the subset)
|
||||
# then, we filter to get only the subset.
|
||||
query = """
|
||||
SELECT sub.user_id, sub.karma_position
|
||||
FROM (
|
||||
SELECT "res_users"."id" as user_id, row_number() OVER (ORDER BY res_users.karma DESC) AS karma_position
|
||||
FROM %(user_from_clause)s
|
||||
WHERE %(user_where_clause)s
|
||||
) sub
|
||||
WHERE sub.user_id IN %%s""" % {
|
||||
'user_from_clause': user_from_clause,
|
||||
'user_where_clause': user_where_clause or 'TRUE',
|
||||
}
|
||||
|
||||
self.env.cr.execute(query, tuple(where_clause_params + [tuple(self.ids)]))
|
||||
return self.env.cr.dictfetchall()
|
||||
|
||||
def _rank_changed(self):
|
||||
"""
|
||||
Method that can be called on a batch of users with the same new rank
|
||||
@@ -68,7 +184,7 @@ class Users(models.Model):
|
||||
if template:
|
||||
for u in self:
|
||||
if u.rank_id.karma_min > 0:
|
||||
template.send_mail(u.id, force_send=len(self) == 1, notif_layout='mail.mail_notification_light')
|
||||
template.send_mail(u.id, force_send=False, notif_layout='mail.mail_notification_light')
|
||||
|
||||
def _recompute_rank(self):
|
||||
"""
|
||||
@@ -161,7 +277,6 @@ class Users(models.Model):
|
||||
'next_rank_id': lower_rank.id,
|
||||
})
|
||||
|
||||
|
||||
def _get_next_rank(self):
|
||||
""" For fresh users with 0 karma that don't have a rank_id and next_rank_id yet
|
||||
this method returns the first karma rank (by karma ascending). This acts as a
|
||||
|
||||
@@ -27,4 +27,6 @@ badge_user_portal,"Badge-user Portal",gamification.model_gamification_badge_user
|
||||
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
|
||||
gamification_karma_rank_access_user_manager,gamification.karma.rank.access.user.manager,gamification.model_gamification_karma_rank,base.group_system,1,1,1,1
|
||||
gamification_karma_tracking_access_all,gamification.karma.tracking.access.all,gamification.model_gamification_karma_tracking,,0,0,0,0
|
||||
gamification_karma_tracking_access_user_manager,gamification.karma.tracking.access.user.manager,gamification.model_gamification_karma_tracking,base.group_system,1,1,1,1
|
||||
|
||||
|
@@ -2,4 +2,4 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import test_challenge
|
||||
from . import test_recompute_rank
|
||||
from . import test_karma_tracking
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import date
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from unittest.mock import patch
|
||||
|
||||
from odoo import exceptions, fields
|
||||
from odoo.tests import common
|
||||
|
||||
|
||||
class TestKarmaTrackingCommon(common.SavepointCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestKarmaTrackingCommon, cls).setUpClass()
|
||||
cls.test_user = cls.env['res.users'].with_context(no_reset_password=True, mail_create_nosubscribe=True).create({
|
||||
'name': 'Test User',
|
||||
'login': 'test',
|
||||
'email': 'test@example.com',
|
||||
'karma': 0,
|
||||
'groups_id': [(4, cls.env.ref('base.group_user').id)]
|
||||
})
|
||||
cls.test_user_2 = cls.env['res.users'].with_context(no_reset_password=True, mail_create_nosubscribe=True).create({
|
||||
'name': 'Test User 2',
|
||||
'login': 'test2',
|
||||
'email': 'test2@example.com',
|
||||
'karma': 0,
|
||||
'groups_id': [(4, cls.env.ref('base.group_user').id)]
|
||||
})
|
||||
cls.env['gamification.karma.tracking'].search([]).unlink()
|
||||
|
||||
cls.test_date = fields.Date.today() + relativedelta(month=4, day=1)
|
||||
|
||||
@classmethod
|
||||
def _create_trackings(cls, user, karma, steps, date, days_delta=1):
|
||||
old_value = user.karma
|
||||
for step in range(steps):
|
||||
new_value = old_value + karma
|
||||
cls.env['gamification.karma.tracking'].create([{
|
||||
'user_id': user.id,
|
||||
'old_value': old_value,
|
||||
'new_value': new_value,
|
||||
'consolidated': False,
|
||||
'tracking_date': fields.Date.to_string(date)
|
||||
}])
|
||||
old_value = new_value
|
||||
date = date + relativedelta(days=days_delta)
|
||||
|
||||
def test_computation_gain(self):
|
||||
self._create_trackings(self.test_user, 20, 2, self.test_date, days_delta=30)
|
||||
self._create_trackings(self.test_user_2, 10, 20, self.test_date, days_delta=2)
|
||||
|
||||
results = (self.test_user | self.test_user_2)._get_tracking_karma_gain_position([])
|
||||
self.assertEqual(results[0]['user_id'], self.test_user_2.id)
|
||||
self.assertEqual(results[0]['karma_gain_total'], 200)
|
||||
self.assertEqual(results[0]['karma_position'], 1)
|
||||
self.assertEqual(results[1]['user_id'], self.test_user.id)
|
||||
self.assertEqual(results[1]['karma_gain_total'], 40)
|
||||
self.assertEqual(results[1]['karma_position'], 2)
|
||||
|
||||
results = (self.test_user | self.test_user_2)._get_tracking_karma_gain_position([], to_date=self.test_date + relativedelta(day=2))
|
||||
self.assertEqual(results[0]['user_id'], self.test_user.id)
|
||||
self.assertEqual(results[0]['karma_gain_total'], 20)
|
||||
self.assertEqual(results[0]['karma_position'], 1)
|
||||
self.assertEqual(results[1]['user_id'], self.test_user_2.id)
|
||||
self.assertEqual(results[1]['karma_gain_total'], 10)
|
||||
self.assertEqual(results[1]['karma_position'], 2)
|
||||
|
||||
results = (self.test_user | self.test_user_2)._get_tracking_karma_gain_position([], from_date=self.test_date + relativedelta(months=1, day=1))
|
||||
self.assertEqual(results[0]['user_id'], self.test_user_2.id)
|
||||
self.assertEqual(results[0]['karma_gain_total'], 50)
|
||||
self.assertEqual(results[0]['karma_position'], 1)
|
||||
self.assertEqual(results[1]['user_id'], self.test_user.id)
|
||||
self.assertEqual(results[1]['karma_gain_total'], 20)
|
||||
self.assertEqual(results[1]['karma_position'], 2)
|
||||
|
||||
results = self.env['res.users']._get_tracking_karma_gain_position([])
|
||||
self.assertEqual(len(results), 0)
|
||||
|
||||
def test_consolidation_cron(self):
|
||||
self.patcher = patch('odoo.addons.gamification.models.gamification_karma_tracking.fields.Date', wraps=fields.Date)
|
||||
self.mock_datetime = self.patcher.start()
|
||||
self.mock_datetime.today.return_value = date(self.test_date.year, self.test_date.month + 1, self.test_date.day)
|
||||
|
||||
self._create_trackings(self.test_user, 20, 2, self.test_date, days_delta=30)
|
||||
self._create_trackings(self.test_user_2, 10, 20, self.test_date, days_delta=2)
|
||||
self.env['gamification.karma.tracking']._consolidate_last_month()
|
||||
consolidated = self.env['gamification.karma.tracking'].search([
|
||||
('user_id', 'in', (self.test_user | self.test_user_2).ids),
|
||||
('consolidated', '=', True),
|
||||
('tracking_date', '=', self.test_date)
|
||||
])
|
||||
self.assertEqual(len(consolidated), 2)
|
||||
unconsolidated = self.env['gamification.karma.tracking'].search([
|
||||
('user_id', 'in', (self.test_user | self.test_user_2).ids),
|
||||
('consolidated', '=', False),
|
||||
])
|
||||
self.assertEqual(len(unconsolidated), 6) # 5 for test user 2, 1 for test user
|
||||
|
||||
self.patcher.stop()
|
||||
|
||||
def test_consolidation_monthly(self):
|
||||
Tracking = self.env['gamification.karma.tracking']
|
||||
base_test_user_karma = self.test_user.karma
|
||||
base_test_user_2_karma = self.test_user_2.karma
|
||||
self._create_trackings(self.test_user, 20, 2, self.test_date, days_delta=30)
|
||||
self._create_trackings(self.test_user_2, 10, 20, self.test_date, days_delta=2)
|
||||
|
||||
Tracking._process_consolidate(self.test_date)
|
||||
consolidated = Tracking.search([
|
||||
('user_id', '=', self.test_user_2.id),
|
||||
('consolidated', '=', True),
|
||||
('tracking_date', '=', self.test_date)
|
||||
])
|
||||
self.assertEqual(len(consolidated), 1)
|
||||
self.assertEqual(consolidated.old_value, base_test_user_2_karma) # 15 2-days span, from 1 to 29 included = 15 steps -> 150 karma
|
||||
self.assertEqual(consolidated.new_value, base_test_user_2_karma + 150) # 15 2-days span, from 1 to 29 included = 15 steps -> 150 karma
|
||||
|
||||
remaining = Tracking.search([
|
||||
('user_id', '=', self.test_user_2.id),
|
||||
('consolidated', '=', False)
|
||||
])
|
||||
self.assertEqual(len(remaining), 5) # 15 steps consolidated, remaining 5
|
||||
self.assertEqual(remaining[0].tracking_date, self.test_date + relativedelta(months=1, day=9)) # ordering: last first
|
||||
self.assertEqual(remaining[-1].tracking_date, self.test_date + relativedelta(months=1, day=1))
|
||||
|
||||
Tracking._process_consolidate(self.test_date + relativedelta(months=1))
|
||||
consolidated = Tracking.search([
|
||||
('user_id', '=', self.test_user_2.id),
|
||||
('consolidated', '=', True),
|
||||
])
|
||||
self.assertEqual(len(consolidated), 2)
|
||||
self.assertEqual(consolidated[0].new_value, base_test_user_2_karma + 200) # 5 remaining 2-days span, from 1 to 9 included = 5 steps -> 50 karma
|
||||
self.assertEqual(consolidated[0].old_value, base_test_user_2_karma + 150) # coming from previous iteration
|
||||
self.assertEqual(consolidated[0].tracking_date, self.test_date + relativedelta(months=1)) # tracking set at beginning of month
|
||||
self.assertEqual(consolidated[-1].new_value, base_test_user_2_karma + 150) # previously created one still present
|
||||
self.assertEqual(consolidated[-1].old_value, base_test_user_2_karma) # previously created one still present
|
||||
|
||||
remaining = Tracking.search([
|
||||
('user_id', '=', self.test_user_2.id),
|
||||
('consolidated', '=', False)
|
||||
])
|
||||
self.assertFalse(remaining)
|
||||
|
||||
# current user not-in-details tests
|
||||
current_user_trackings = Tracking.search([
|
||||
('user_id', '=', self.test_user.id),
|
||||
])
|
||||
self.assertEqual(len(current_user_trackings), 2)
|
||||
self.assertEqual(current_user_trackings[0].new_value, base_test_user_karma + 40)
|
||||
self.assertEqual(current_user_trackings[-1].old_value, base_test_user_karma)
|
||||
|
||||
def test_user_as_erp_manager(self):
|
||||
self.test_user.write({'groups_id': [
|
||||
(4, self.env.ref('base.group_partner_manager').id),
|
||||
(4, self.env.ref('base.group_erp_manager').id)
|
||||
]})
|
||||
user = self.env['res.users'].with_user(self.test_user).create({
|
||||
'name': 'Test Ostérone', 'karma': '32',
|
||||
'login': 'dummy', 'email': 'dummy@example.com',
|
||||
})
|
||||
with self.assertRaises(exceptions.AccessError):
|
||||
user.read(['karma_tracking_ids'])
|
||||
|
||||
user.write({'karma': 60})
|
||||
user.add_karma(10)
|
||||
self.assertEqual(user.karma, 70)
|
||||
trackings = self.env['gamification.karma.tracking'].sudo().search([('user_id', '=', user.id)])
|
||||
self.assertEqual(len(trackings), 3) # create + write + add_karma
|
||||
|
||||
def test_user_tracking(self):
|
||||
self.test_user.write({'groups_id': [
|
||||
(4, self.env.ref('base.group_partner_manager').id),
|
||||
(4, self.env.ref('base.group_system').id)
|
||||
]})
|
||||
user = self.env['res.users'].with_user(self.test_user).create({
|
||||
'name': 'Test Ostérone', 'karma': '32',
|
||||
'login': 'dummy', 'email': 'dummy@example.com',
|
||||
})
|
||||
self.assertEqual(user.karma, 32)
|
||||
self.assertEqual(len(user.karma_tracking_ids), 1)
|
||||
self.assertEqual(user.karma_tracking_ids.old_value, 0)
|
||||
self.assertEqual(user.karma_tracking_ids.new_value, 32)
|
||||
|
||||
user.write({'karma': 60})
|
||||
user.add_karma(10)
|
||||
self.assertEqual(user.karma, 70)
|
||||
self.assertEqual(len(user.karma_tracking_ids), 3)
|
||||
self.assertEqual(user.karma_tracking_ids[2].old_value, 60)
|
||||
self.assertEqual(user.karma_tracking_ids[2].new_value, 70)
|
||||
self.assertEqual(user.karma_tracking_ids[1].old_value, 32)
|
||||
self.assertEqual(user.karma_tracking_ids[1].new_value, 60)
|
||||
self.assertEqual(user.karma_tracking_ids[0].old_value, 0)
|
||||
self.assertEqual(user.karma_tracking_ids[0].new_value, 32)
|
||||
|
||||
|
||||
class TestComputeRankCommon(common.SavepointCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestComputeRankCommon, cls).setUpClass()
|
||||
|
||||
def _patched_send_mail(*args, **kwargs):
|
||||
pass
|
||||
|
||||
patch_email = patch('odoo.addons.mail.models.mail_template.MailTemplate.send_mail', _patched_send_mail)
|
||||
patch_email.start()
|
||||
|
||||
cls.users = cls.env['res.users']
|
||||
for k in range(-5, 1030, 30):
|
||||
cls.users += cls.env['res.users'].with_context(no_reset_password=True, mail_create_nosubscribe=True).create({
|
||||
'name': str(k),
|
||||
'login': "test_recompute_rank_%s" % k,
|
||||
'karma': k,
|
||||
})
|
||||
|
||||
cls.env['gamification.karma.rank'].search([]).unlink()
|
||||
|
||||
cls.rank_1 = cls.env['gamification.karma.rank'].create({
|
||||
'name': 'rank 1',
|
||||
'karma_min': 0,
|
||||
})
|
||||
|
||||
cls.rank_2 = cls.env['gamification.karma.rank'].create({
|
||||
'name': 'rank 2',
|
||||
'karma_min': 250,
|
||||
})
|
||||
|
||||
cls.rank_3 = cls.env['gamification.karma.rank'].create({
|
||||
'name': 'rank 3',
|
||||
'karma_min': 500,
|
||||
})
|
||||
cls.rank_4 = cls.env['gamification.karma.rank'].create({
|
||||
'name': 'rank 4',
|
||||
'karma_min': 1000,
|
||||
})
|
||||
|
||||
patch_email.stop()
|
||||
|
||||
def test_00_initial_compute(self):
|
||||
|
||||
self.assertEqual(len(self.users), 35)
|
||||
|
||||
self.assertEqual(
|
||||
len(self.rank_1.user_ids & self.users),
|
||||
len([u for u in self.users if u.karma >= self.rank_1.karma_min and u.karma < self.rank_2.karma_min])
|
||||
)
|
||||
self.assertEqual(
|
||||
len(self.rank_2.user_ids & self.users),
|
||||
len([u for u in self.users if u.karma >= self.rank_2.karma_min and u.karma < self.rank_3.karma_min])
|
||||
)
|
||||
self.assertEqual(
|
||||
len(self.rank_3.user_ids & self.users),
|
||||
len([u for u in self.users if u.karma >= self.rank_3.karma_min and u.karma < self.rank_4.karma_min])
|
||||
)
|
||||
self.assertEqual(
|
||||
len(self.rank_4.user_ids & self.users),
|
||||
len([u for u in self.users if u.karma >= self.rank_4.karma_min])
|
||||
)
|
||||
|
||||
def test_01_switch_rank(self):
|
||||
|
||||
self.assertEqual(len(self.users), 35)
|
||||
|
||||
self.rank_3.karma_min = 100
|
||||
# rank_1 -> rank_3 -> rank_2 -> rank_4
|
||||
|
||||
self.assertEqual(
|
||||
len(self.rank_1.user_ids & self.users),
|
||||
len([u for u in self.users if u.karma >= self.rank_1.karma_min and u.karma < self.rank_3.karma_min])
|
||||
)
|
||||
self.assertEqual(
|
||||
len(self.rank_3.user_ids & self.users),
|
||||
len([u for u in self.users if u.karma >= self.rank_3.karma_min and u.karma < self.rank_2.karma_min])
|
||||
)
|
||||
self.assertEqual(
|
||||
len(self.rank_2.user_ids & self.users),
|
||||
len([u for u in self.users if u.karma >= self.rank_2.karma_min and u.karma < self.rank_4.karma_min])
|
||||
)
|
||||
self.assertEqual(
|
||||
len(self.rank_4.user_ids & self.users),
|
||||
len([u for u in self.users if u.karma >= self.rank_4.karma_min])
|
||||
)
|
||||
|
||||
def test_02_update_rank_without_switch(self):
|
||||
number_of_users = False
|
||||
|
||||
def _patched_recompute_rank(_self, *args, **kwargs):
|
||||
nonlocal number_of_users
|
||||
number_of_users = len(_self & self.users)
|
||||
|
||||
patch_bulk = patch('odoo.addons.gamification.models.res_users.Users._recompute_rank', _patched_recompute_rank)
|
||||
patch_bulk.start()
|
||||
self.rank_3.karma_min = 700
|
||||
self.assertEqual(number_of_users, 7, "Should just recompute for the 7 users between 500 and 700")
|
||||
patch_bulk.stop()
|
||||
|
||||
def test_03_test_bulk_call(self):
|
||||
self.assertEqual(len(self.users), 35)
|
||||
|
||||
def _patched_check_in_bulk(*args, **kwargs):
|
||||
raise
|
||||
|
||||
patch_bulk = patch('odoo.addons.gamification.models.res_users.Users._recompute_rank_bulk', _patched_check_in_bulk)
|
||||
patch_bulk.start()
|
||||
|
||||
# call on 5 users should not trigger the bulk function
|
||||
self.users[0:5]._recompute_rank()
|
||||
|
||||
# call on 50 users should trigger the bulk function
|
||||
with self.assertRaises(Exception):
|
||||
self.users[0:50]._recompute_rank()
|
||||
|
||||
patch_bulk.stop()
|
||||
@@ -1,128 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.tests import common
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
class TestComputeRankCommon(common.TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestComputeRankCommon, self).setUp()
|
||||
|
||||
def _patched_send_mail(*args, **kwargs):
|
||||
pass
|
||||
|
||||
self.User = self.env['res.users'].with_context(no_reset_password=True, mail_create_nosubscribe=True)
|
||||
self.users = self.User
|
||||
|
||||
patch_email = patch('odoo.addons.mail.models.mail_template.MailTemplate.send_mail', _patched_send_mail)
|
||||
patch_email.start()
|
||||
for k in range(-5, 1030, 30):
|
||||
self.users += self.User.create({
|
||||
'name': str(k),
|
||||
'login': "test_recompute_rank_%s" % k,
|
||||
'karma': k,
|
||||
})
|
||||
|
||||
self.env['gamification.karma.rank'].search([]).unlink()
|
||||
|
||||
self.rank_1 = self.env['gamification.karma.rank'].create({
|
||||
'name': 'rank 1',
|
||||
'karma_min': 0,
|
||||
})
|
||||
|
||||
self.rank_2 = self.env['gamification.karma.rank'].create({
|
||||
'name': 'rank 2',
|
||||
'karma_min': 250,
|
||||
})
|
||||
|
||||
self.rank_3 = self.env['gamification.karma.rank'].create({
|
||||
'name': 'rank 3',
|
||||
'karma_min': 500,
|
||||
})
|
||||
self.rank_4 = self.env['gamification.karma.rank'].create({
|
||||
'name': 'rank 4',
|
||||
'karma_min': 1000,
|
||||
})
|
||||
|
||||
patch_email.stop()
|
||||
|
||||
|
||||
class test_recompute(TestComputeRankCommon):
|
||||
|
||||
def test_00_initial_compute(self):
|
||||
|
||||
self.assertEqual(len(self.users), 35)
|
||||
|
||||
self.assertEqual(
|
||||
len(self.rank_1.user_ids & self.users),
|
||||
len([u for u in self.users if u.karma >= self.rank_1.karma_min and u.karma < self.rank_2.karma_min])
|
||||
)
|
||||
self.assertEqual(
|
||||
len(self.rank_2.user_ids & self.users),
|
||||
len([u for u in self.users if u.karma >= self.rank_2.karma_min and u.karma < self.rank_3.karma_min])
|
||||
)
|
||||
self.assertEqual(
|
||||
len(self.rank_3.user_ids & self.users),
|
||||
len([u for u in self.users if u.karma >= self.rank_3.karma_min and u.karma < self.rank_4.karma_min])
|
||||
)
|
||||
self.assertEqual(
|
||||
len(self.rank_4.user_ids & self.users),
|
||||
len([u for u in self.users if u.karma >= self.rank_4.karma_min])
|
||||
)
|
||||
|
||||
def test_01_switch_rank(self):
|
||||
|
||||
self.assertEqual(len(self.users), 35)
|
||||
|
||||
self.rank_3.karma_min = 100
|
||||
# rank_1 -> rank_3 -> rank_2 -> rank_4
|
||||
|
||||
self.assertEqual(
|
||||
len(self.rank_1.user_ids & self.users),
|
||||
len([u for u in self.users if u.karma >= self.rank_1.karma_min and u.karma < self.rank_3.karma_min])
|
||||
)
|
||||
self.assertEqual(
|
||||
len(self.rank_3.user_ids & self.users),
|
||||
len([u for u in self.users if u.karma >= self.rank_3.karma_min and u.karma < self.rank_2.karma_min])
|
||||
)
|
||||
self.assertEqual(
|
||||
len(self.rank_2.user_ids & self.users),
|
||||
len([u for u in self.users if u.karma >= self.rank_2.karma_min and u.karma < self.rank_4.karma_min])
|
||||
)
|
||||
self.assertEqual(
|
||||
len(self.rank_4.user_ids & self.users),
|
||||
len([u for u in self.users if u.karma >= self.rank_4.karma_min])
|
||||
)
|
||||
|
||||
def test_02_update_rank_without_switch(self):
|
||||
number_of_users = False
|
||||
|
||||
def _patched_recompute_rank(_self, *args, **kwargs):
|
||||
nonlocal number_of_users
|
||||
number_of_users = len(_self & self.users)
|
||||
|
||||
patch_bulk = patch('odoo.addons.gamification.models.res_users.Users._recompute_rank', _patched_recompute_rank)
|
||||
patch_bulk.start()
|
||||
self.rank_3.karma_min = 700
|
||||
self.assertEqual(number_of_users, 7, "Should just recompute for the 7 users between 500 and 700")
|
||||
patch_bulk.stop()
|
||||
|
||||
def test_03_test_bulk_call(self):
|
||||
self.assertEqual(len(self.users), 35)
|
||||
|
||||
def _patched_check_in_bulk(*args, **kwargs):
|
||||
raise
|
||||
|
||||
patch_bulk = patch('odoo.addons.gamification.models.res_users.Users._recompute_rank_bulk', _patched_check_in_bulk)
|
||||
patch_bulk.start()
|
||||
|
||||
# call on 5 users should not trigger the bulk function
|
||||
self.users[0:5]._recompute_rank()
|
||||
|
||||
# call on 50 users should trigger the bulk function
|
||||
with self.assertRaises(Exception):
|
||||
self.users[0:50]._recompute_rank()
|
||||
|
||||
patch_bulk.stop()
|
||||
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo><data>
|
||||
<record id="gamification_karma_tracking_view_search" model="ir.ui.view">
|
||||
<field name="name">gamification.karma.tracking.view.search</field>
|
||||
<field name="model">gamification.karma.tracking</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Trackings">
|
||||
<field name="user_id"/>
|
||||
<field name="tracking_date"/>
|
||||
<filter string="Consolidated" name="filter_consolidated"
|
||||
domain="[('consolidated', '=', True)]"/>
|
||||
<group string="Group By" expand="1">
|
||||
<filter string="User" name="group_by_user_id"
|
||||
context="{'group_by': 'user_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="gamification_karma_tracking_view_tree" model="ir.ui.view">
|
||||
<field name="name">gamification.karma.tracking.view.tree</field>
|
||||
<field name="model">gamification.karma.tracking</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Trackings">
|
||||
<field name="user_id"/>
|
||||
<field name="tracking_date"/>
|
||||
<field name="old_value"/>
|
||||
<field name="new_value"/>
|
||||
<field name="consolidated"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="gamification_karma_tracking_view_form" model="ir.ui.view">
|
||||
<field name="name">gamification.karma.tracking.view.form</field>
|
||||
<field name="model">gamification.karma.tracking</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Tracking">
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="user_id"/>
|
||||
<field name="tracking_date"/>
|
||||
<field name="old_value"/>
|
||||
<field name="new_value"/>
|
||||
<field name="consolidated"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="gamification_karma_tracking_action" model="ir.actions.act_window">
|
||||
<field name="name">Trackings</field>
|
||||
<field name="res_model">gamification.karma.tracking</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="gamification_karma_tracking_menu"
|
||||
parent="gamification_menu"
|
||||
action="gamification_karma_tracking_action"
|
||||
sequence="50"/>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -8,7 +8,10 @@ import werkzeug.urls
|
||||
import werkzeug.wrappers
|
||||
import math
|
||||
|
||||
from odoo import http, modules, tools
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from operator import itemgetter
|
||||
|
||||
from odoo import fields, http, modules, tools
|
||||
from odoo.http import request
|
||||
from odoo.osv import expression
|
||||
|
||||
@@ -205,17 +208,21 @@ class WebsiteProfile(http.Controller):
|
||||
|
||||
@http.route(['/profile/users',
|
||||
'/profile/users/page/<int:page>'], type='http', auth="public", website=True, sitemap=True)
|
||||
def view_all_users_page(self, page=1, **searches):
|
||||
def view_all_users_page(self, page=1, **kwargs):
|
||||
User = request.env['res.users']
|
||||
dom = [('karma', '>', 1), ('website_published', '=', True)]
|
||||
|
||||
# Searches
|
||||
search_term = searches.get('search')
|
||||
search_term = kwargs.get('search')
|
||||
group_by = kwargs.get('group_by', False)
|
||||
render_values = {
|
||||
'search': search_term,
|
||||
'group_by': group_by or 'all',
|
||||
}
|
||||
if search_term:
|
||||
dom = expression.AND([['|', ('name', 'ilike', search_term), ('company_id.name', 'ilike', search_term)], dom])
|
||||
|
||||
user_count = User.sudo().search_count(dom)
|
||||
|
||||
if user_count:
|
||||
page_count = math.ceil(user_count / self._users_per_page)
|
||||
pager = request.website.pager(url="/profile/users", total=user_count, page=page, step=self._users_per_page,
|
||||
@@ -226,43 +233,41 @@ class WebsiteProfile(http.Controller):
|
||||
|
||||
# Get karma position for users (only website_published)
|
||||
position_domain = [('karma', '>', 1), ('website_published', '=', True)]
|
||||
position_map = self._get_users_karma_position(position_domain, users.ids)
|
||||
if group_by:
|
||||
position_map = self._get_user_tracking_karma_gain_position(position_domain, users.ids, group_by)
|
||||
else:
|
||||
position_results = request.env['res.users'].browse(users.ids)._get_karma_position(position_domain)
|
||||
position_map = dict((user_data['user_id'], dict(user_data)) for user_data in position_results)
|
||||
|
||||
max_position = max([user_data['karma_position'] for user_data in position_map.values()], default=1)
|
||||
for user in user_values:
|
||||
user['position'] = position_map.get(user['id'], 0)
|
||||
user_data = position_map.get(user['id'], dict())
|
||||
user['position'] = user_data.get('karma_position', max_position + 1)
|
||||
user['karma_gain'] = user_data.get('karma_gain_total', 0)
|
||||
user_values.sort(key=itemgetter('position'))
|
||||
|
||||
values = {
|
||||
'top3_users': user_values[:3] if not search_term and page == 1 else None,
|
||||
'users': user_values[3:] if not search_term and page == 1 else user_values,
|
||||
'pager': pager
|
||||
}
|
||||
else:
|
||||
values = {'top3_users': [], 'users': [], 'search': search_term, 'pager': dict(page_count=0)}
|
||||
user_values = []
|
||||
pager = {'page_count': 0}
|
||||
|
||||
return request.render("website_profile.users_page_main", values)
|
||||
render_values.update({
|
||||
'top3_users': user_values[:3] if not search_term and page == 1 else [],
|
||||
'users': user_values,
|
||||
'pager': pager})
|
||||
return request.render("website_profile.users_page_main", render_values)
|
||||
|
||||
def _get_users_karma_position(self, domain, user_ids):
|
||||
if not user_ids:
|
||||
return {}
|
||||
|
||||
Users = request.env['res.users']
|
||||
where_query = Users._where_calc(domain)
|
||||
from_clause, where_clause, where_clause_params = where_query.get_sql()
|
||||
|
||||
# we search on every user in the DB to get the real positioning (not the one inside the subset)
|
||||
# then, we filter to get only the subset.
|
||||
query = """
|
||||
SELECT sub.id, sub.karma_position
|
||||
FROM (
|
||||
SELECT "res_users"."id", row_number() OVER (ORDER BY res_users.karma DESC) AS karma_position
|
||||
FROM {from_clause}
|
||||
WHERE {where_clause}
|
||||
) sub
|
||||
WHERE sub.id IN %s
|
||||
""".format(from_clause=from_clause, where_clause=where_clause)
|
||||
|
||||
request.env.cr.execute(query, where_clause_params + [tuple(user_ids)])
|
||||
|
||||
return {item['id']: item['karma_position'] for item in request.env.cr.dictfetchall()}
|
||||
def _get_user_tracking_karma_gain_position(self, domain, user_ids, group_by):
|
||||
""" Helper method computing boundaries to give to _get_tracking_karma_gain_position.
|
||||
See that method for more details. """
|
||||
to_date = fields.Date.today()
|
||||
if group_by == 'week':
|
||||
from_date = to_date - relativedelta(weeks=1)
|
||||
elif group_by == 'month':
|
||||
from_date = to_date - relativedelta(months=1)
|
||||
else:
|
||||
from_date = None
|
||||
results = request.env['res.users'].browse(user_ids)._get_tracking_karma_gain_position(domain, from_date=from_date, to_date=to_date)
|
||||
return dict((item['user_id'], dict(item)) for item in results)
|
||||
|
||||
# User and validation
|
||||
# --------------------------------------------------
|
||||
|
||||
@@ -481,7 +481,18 @@
|
||||
<div class="o_wprofile_all_users_header o_wprofile_gradient mb-n5 pb-5">
|
||||
<t t-call="website_profile.user_profile_sub_nav"/>
|
||||
<div class="container">
|
||||
<h1 class="py-4 text-white">All Users</h1>
|
||||
<h1 class="py-4 text-white d-inline-block">All Users</h1>
|
||||
<div class="form-inline py-4 float-right">
|
||||
<strong class="form-group text-white mr-2">Rank by :</strong>
|
||||
<div class="form-group btn-group">
|
||||
<a t-attf-class="btn btn-secondary #{ 'active' if group_by == 'week' else ''}"
|
||||
t-att-href="'/profile/users?' + keep_query('search', group_by='week')">This week</a>
|
||||
<a t-attf-class="btn btn-secondary #{ 'active' if group_by == 'month' else ''}"
|
||||
t-att-href="'/profile/users?' + keep_query('search', group_by='month')">This month</a>
|
||||
<a t-attf-class="btn btn-secondary #{ 'active' if group_by == 'all' else ''}"
|
||||
t-att-href="'/profile/users?' + keep_query('search')">All time</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -489,7 +500,7 @@
|
||||
<template id="users_page_content">
|
||||
<div class="container mb32">
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4" t-foreach="top3_users" t-as="user" t-attf-onclick="location.href='/profile/user/#{user['id']}';">
|
||||
<div class="col-md-4 d-flex flex-grow-1" t-foreach="top3_users" t-as="user" t-attf-onclick="location.href='/profile/user/#{user['id']}';">
|
||||
<t t-call="website_profile.top3_user_card"></t>
|
||||
</div>
|
||||
</div>
|
||||
@@ -498,7 +509,7 @@
|
||||
<t t-call="website_profile.all_user_card"/>
|
||||
</tr>
|
||||
</table>
|
||||
<t t-if='search'>
|
||||
<t t-if='search and not users'>
|
||||
<div class='alert alert-warning'>No user found for <strong><t t-esc="search"/></strong>. Try another search.</div>
|
||||
</t>
|
||||
<div class="form-inline justify-content-center">
|
||||
@@ -508,7 +519,7 @@
|
||||
</template>
|
||||
|
||||
<template id="top3_user_card" name="Top 3 User Card">
|
||||
<div class="card text-center mb-2 border-bottom-0 o_wprofile_pointer">
|
||||
<div class="card w-100 text-center mb-2 border-bottom-0 o_wprofile_pointer">
|
||||
<div class="card-body">
|
||||
<div class="d-inline-block position-relative">
|
||||
<img class="rounded-circle img-fluid"
|
||||
@@ -519,6 +530,9 @@
|
||||
<h3 class="mt-2 mb-0" t-esc="user['name']"></h3>
|
||||
<span class="badge badge-danger font-weight-normal px-2" t-if="not user['website_published']">Unpublished</span>
|
||||
<strong class="text-muted" t-esc="user['rank']"/>
|
||||
<div class="h3 my-2" t-if="user['karma_gain']">
|
||||
<span class="badge badge-pill badge-success px-3 py-2" >+ <t t-esc="user['karma_gain']"/> XP</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mx-0 o_wprofile_top3_card_footer text-nowrap">
|
||||
<div class="col py-3"><b t-esc="user['karma']"/> <span class="text-muted">XP</span></div>
|
||||
@@ -537,6 +551,17 @@
|
||||
<td class="align-middle w-md-75">
|
||||
<span class="font-weight-bold" t-esc="user['name']"/><br/>
|
||||
<span class="text-muted font-weight-bold" t-esc="user['rank']"></span>
|
||||
</td>
|
||||
<td class="align-middle text-nowrap">
|
||||
<t t-if="user['karma_gain']">
|
||||
<span class="badge badge-pill badge-success d-inline">+ <t t-esc="user['karma_gain']"/> XP</span>
|
||||
<span class="text-muted pl-2 pr-3">
|
||||
<t t-if="group_by == 'week'">Weekly </t>
|
||||
<t t-elif="group_by == 'month'">Monthly </t>
|
||||
<t t-else="">All time </t>
|
||||
gain
|
||||
</span>
|
||||
</t>
|
||||
</td>
|
||||
<td t-if="not user['website_published']" class="align-middle font-weight-bold text-right text-nowrap">
|
||||
<span class="badge badge-danger font-weight-normal px-2 py-1 m-1">Unpublished</span>
|
||||
|
||||
Reference in New Issue
Block a user