Files
odoo_source/addons/gamification/models/gamification_karma_tracking.py
T
Thomas Josseandstd-odoo 0ffa2d64ba [IMP] gamification, website_forum, website_slides: karma tracking improvement
Purpose
=======
Some users found a way to farm karma points, so we want to be able to
track the source of the karma gain / loss.

Specifications
==============
Now, we added a reference field `origin_ref` which store the record
responsible for the karma gain / loss (e.g. a slide we just completed).

In addition to this origin, we also have a new field to store the
reason (e.g. "Slide completed") so we know exactly what happened and
how the user gains his karma.

Before, the `old_value` of the karma tracking has to be set manually,
but now it's done automatically based on the value of the previous
tracking of the same user. That way, it will simplify other part of
the code.

Add the karma reason in the website modules. Adapt those modules due
to the changes in gamification.

Task-2234179

closes odoo/odoo#76430

Related: odoo/enterprise#23702
Related: odoo/upgrade#3299
Signed-off-by: Warnon Aurélien (awa) <awa@odoo.com>
Co-authored-by: std-odoo <std@odoo.com>
2023-03-06 17:47:09 +01:00

136 lines
5.4 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from dateutil.relativedelta import relativedelta
from odoo import _, api, fields, models
from odoo.tools import date_utils
class KarmaTracking(models.Model):
_name = 'gamification.karma.tracking'
_description = 'Track Karma Changes'
_rec_name = 'user_id'
_order = 'tracking_date desc, id desc'
def _get_origin_selection_values(self):
return [('res.users', _('User'))]
user_id = fields.Many2one('res.users', 'User', index=True, required=True, ondelete='cascade')
old_value = fields.Integer('Old Karma Value', readonly=True)
new_value = fields.Integer('New Karma Value', required=True)
gain = fields.Integer('Gain', compute='_compute_gain', readonly=False)
consolidated = fields.Boolean('Consolidated')
tracking_date = fields.Datetime(default=fields.Datetime.now, readonly=True, index=True)
reason = fields.Text(default=lambda self: _('Add Manually'), string='Description')
origin_ref = fields.Reference(
string='Source',
selection=lambda self: self._get_origin_selection_values(),
default=lambda self: f'res.users,{self.env.user.id}',
)
origin_ref_model_name = fields.Selection(
string='Source Type', selection=lambda self: self._get_origin_selection_values(),
compute='_compute_origin_ref_model_name', store=True)
@api.depends('old_value', 'new_value')
def _compute_gain(self):
for karma in self:
karma.gain = karma.new_value - (karma.old_value or 0)
@api.depends('origin_ref')
def _compute_origin_ref_model_name(self):
for karma in self:
if not karma.origin_ref:
karma.origin_ref_model_name = False
continue
karma.origin_ref_model_name = karma.origin_ref._name
@api.model_create_multi
def create(self, values_list):
# fill missing old value with current user karma
users = self.env['res.users'].browse([
values['user_id']
for values in values_list
if 'old_value' not in values and values.get('user_id')
])
karma_per_users = {user.id: user.karma for user in users}
for values in values_list:
if 'old_value' not in values and values.get('user_id'):
values['old_value'] = karma_per_users[values['user_id']]
if 'gain' in values and 'old_value' in values:
values['new_value'] = values['old_value'] + values['gain']
del values['gain']
return super().create(values_list)
@api.model
def _consolidate_cron(self):
"""Consolidate the trackings 2 months ago. Used by a cron to cleanup tracking records."""
from_date = date_utils.start_of(fields.Datetime.today(), 'month') - relativedelta(months=2)
return self._process_consolidate(from_date)
def _process_consolidate(self, from_date, end_date=None):
"""Consolidate the karma trackings.
The consolidation keeps, for each user, the oldest "old_value" and the most recent
"new_value", creates a new karma tracking with those values and removes all karma
trackings between those dates. The origin / reason is changed on the consolidated
records, so this information is lost in the process.
"""
self.env['gamification.karma.tracking'].flush_model()
if not end_date:
end_date = date_utils.end_of(date_utils.end_of(from_date, 'month'), 'day')
select_query = """
WITH old_tracking AS (
SELECT DISTINCT ON (user_id) user_id, old_value, tracking_date
FROM gamification_karma_tracking
WHERE tracking_date BETWEEN %(from_date)s
AND %(end_date)s
AND consolidated IS NOT TRUE
ORDER BY user_id, tracking_date ASC, id ASC
)
INSERT INTO gamification_karma_tracking (
user_id,
old_value,
new_value,
tracking_date,
origin_ref,
consolidated,
reason)
SELECT DISTINCT ON (nt.user_id)
nt.user_id,
ot.old_value AS old_value,
nt.new_value AS new_value,
ot.tracking_date AS from_tracking_date,
%(origin_ref)s AS origin_ref,
TRUE,
%(reason)s
FROM gamification_karma_tracking AS nt
JOIN old_tracking AS ot
ON ot.user_id = nt.user_id
WHERE nt.tracking_date BETWEEN %(from_date)s
AND %(end_date)s
AND nt.consolidated IS NOT TRUE
ORDER BY nt.user_id, nt.tracking_date DESC, id DESC
"""
self.env.cr.execute(select_query, {
'from_date': from_date,
'end_date': end_date,
'origin_ref': f'res.users,{self.env.user.id}',
'reason': _('Consolidation from %s to %s', from_date.date(), end_date.date()),
})
self.search([
('tracking_date', '>=', from_date),
('tracking_date', '<=', end_date),
('consolidated', '!=', True)]
).with_context(skip_karma_computation=True).unlink()
return True