245 lines
10 KiB
Python
245 lines
10 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
|
|
|
|
import logging
|
|
from datetime import date
|
|
|
|
from flectra import api, fields, models, _, exceptions
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
class BadgeUser(models.Model):
|
|
"""User having received a badge"""
|
|
|
|
_name = 'gamification.badge.user'
|
|
_description = 'Gamification user badge'
|
|
_order = "create_date desc"
|
|
_rec_name = "badge_name"
|
|
|
|
user_id = fields.Many2one('res.users', string="User", required=True, ondelete="cascade", index=True)
|
|
sender_id = fields.Many2one('res.users', string="Sender", help="The user who has send the badge")
|
|
badge_id = fields.Many2one('gamification.badge', string='Badge', required=True, ondelete="cascade", index=True)
|
|
challenge_id = fields.Many2one('gamification.challenge', string='Challenge originating', help="If this badge was rewarded through a challenge")
|
|
comment = fields.Text('Comment')
|
|
badge_name = fields.Char(related='badge_id.name', string="Badge Name")
|
|
create_date = fields.Datetime('Created', readonly=True)
|
|
create_uid = fields.Many2one('res.users', string='Creator', readonly=True)
|
|
|
|
def _send_badge(self):
|
|
"""Send a notification to a user for receiving a badge
|
|
|
|
Does not verify constrains on badge granting.
|
|
The users are added to the owner_ids (create badge_user if needed)
|
|
The stats counters are incremented
|
|
:param ids: list(int) of badge users that will receive the badge
|
|
"""
|
|
template = self.env.ref('gamification.email_template_badge_received')
|
|
|
|
for badge_user in self:
|
|
self.env['mail.thread'].message_post_with_template(
|
|
template.id,
|
|
model=badge_user._name,
|
|
res_id=badge_user.id,
|
|
composition_mode='mass_mail',
|
|
partner_ids=badge_user.user_id.partner_id.ids,
|
|
)
|
|
|
|
return True
|
|
|
|
@api.model
|
|
def create(self, vals):
|
|
self.env['gamification.badge'].browse(vals['badge_id']).check_granting()
|
|
return super(BadgeUser, self).create(vals)
|
|
|
|
|
|
class GamificationBadge(models.Model):
|
|
"""Badge object that users can send and receive"""
|
|
|
|
CAN_GRANT = 1
|
|
NOBODY_CAN_GRANT = 2
|
|
USER_NOT_VIP = 3
|
|
BADGE_REQUIRED = 4
|
|
TOO_MANY = 5
|
|
|
|
_name = 'gamification.badge'
|
|
_description = 'Gamification badge'
|
|
_inherit = ['mail.thread']
|
|
|
|
name = fields.Char('Badge', required=True, translate=True)
|
|
active = fields.Boolean('Active', default=True)
|
|
description = fields.Text('Description', translate=True)
|
|
image = fields.Binary("Image", attachment=True, help="This field holds the image used for the badge, limited to 256x256")
|
|
|
|
rule_auth = fields.Selection([
|
|
('everyone', 'Everyone'),
|
|
('users', 'A selected list of users'),
|
|
('having', 'People having some badges'),
|
|
('nobody', 'No one, assigned through challenges'),
|
|
], default='everyone',
|
|
string="Allowance to Grant", help="Who can grant this badge", required=True)
|
|
rule_auth_user_ids = fields.Many2many(
|
|
'res.users', 'rel_badge_auth_users',
|
|
string='Authorized Users',
|
|
help="Only these people can give this badge")
|
|
rule_auth_badge_ids = fields.Many2many(
|
|
'gamification.badge', 'gamification_badge_rule_badge_rel', 'badge1_id', 'badge2_id',
|
|
string='Required Badges',
|
|
help="Only the people having these badges can give this badge")
|
|
|
|
rule_max = fields.Boolean('Monthly Limited Sending', help="Check to set a monthly limit per person of sending this badge")
|
|
rule_max_number = fields.Integer('Limitation Number', help="The maximum number of time this badge can be sent per month per person.")
|
|
challenge_ids = fields.One2many('gamification.challenge', 'reward_id', string="Reward of Challenges")
|
|
|
|
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',
|
|
string='Owners', help='The list of instances of this badge granted to users')
|
|
|
|
stat_count = fields.Integer("Total", compute='_get_owners_info', help="The number of time this badge has been received.")
|
|
stat_count_distinct = fields.Integer("Number of users", compute='_get_owners_info', help="The number of time this badge has been received by unique users.")
|
|
unique_owner_ids = fields.Many2many(
|
|
'res.users', string="Unique Owners", compute='_get_owners_info',
|
|
help="The list of unique users having received this badge.")
|
|
|
|
stat_this_month = fields.Integer(
|
|
"Monthly total", compute='_get_badge_user_stats',
|
|
help="The number of time this badge has been received this month.")
|
|
stat_my = fields.Integer(
|
|
"My Total", compute='_get_badge_user_stats',
|
|
help="The number of time the current user has received this badge.")
|
|
stat_my_this_month = fields.Integer(
|
|
"My Monthly Total", compute='_get_badge_user_stats',
|
|
help="The number of time the current user has received this badge this month.")
|
|
stat_my_monthly_sending = fields.Integer(
|
|
'My Monthly Sending Total',
|
|
compute='_get_badge_user_stats',
|
|
help="The number of time the current user has sent this badge this month.")
|
|
|
|
remaining_sending = fields.Integer(
|
|
"Remaining Sending Allowed", compute='_remaining_sending_calc',
|
|
help="If a maximum is set")
|
|
|
|
@api.depends('owner_ids')
|
|
def _get_owners_info(self):
|
|
"""Return:
|
|
the list of unique res.users ids having received this badge
|
|
the total number of time this badge was granted
|
|
the total number of users this badge was granted to
|
|
"""
|
|
self.env.cr.execute("""
|
|
SELECT badge_id, count(user_id) as stat_count,
|
|
count(distinct(user_id)) as stat_count_distinct,
|
|
array_agg(distinct(user_id)) as unique_owner_ids
|
|
FROM gamification_badge_user
|
|
WHERE badge_id in %s
|
|
GROUP BY badge_id
|
|
""", [tuple(self.ids)])
|
|
|
|
defaults = {
|
|
'stat_count': 0,
|
|
'stat_count_distinct': 0,
|
|
'unique_owner_ids': [],
|
|
}
|
|
mapping = {
|
|
badge_id: {
|
|
'stat_count': count,
|
|
'stat_count_distinct': distinct_count,
|
|
'unique_owner_ids': owner_ids,
|
|
}
|
|
for (badge_id, count, distinct_count, owner_ids) in self.env.cr._obj
|
|
}
|
|
for badge in self:
|
|
badge.update(mapping.get(badge.id, defaults))
|
|
|
|
@api.depends('owner_ids.badge_id', 'owner_ids.create_date', 'owner_ids.user_id')
|
|
def _get_badge_user_stats(self):
|
|
"""Return stats related to badge users"""
|
|
first_month_day = fields.Date.to_string(date.today().replace(day=1))
|
|
|
|
for badge in self:
|
|
owners = badge.owner_ids
|
|
badge.stats_my = sum(o.user_id == self.env.user for o in owners)
|
|
badge.stats_this_month = sum(o.create_date >= first_month_day for o in owners)
|
|
badge.stats_my_this_month = sum(
|
|
o.user_id == self.env.user and o.create_date >= first_month_day
|
|
for o in owners
|
|
)
|
|
badge.stats_my_monthly_sending = sum(
|
|
o.create_uid == self.env.user and o.create_date >= first_month_day
|
|
for o in owners
|
|
)
|
|
|
|
@api.depends(
|
|
'rule_auth',
|
|
'rule_auth_user_ids',
|
|
'rule_auth_badge_ids',
|
|
'rule_max',
|
|
'rule_max_number',
|
|
'stat_my_monthly_sending',
|
|
)
|
|
def _remaining_sending_calc(self):
|
|
"""Computes the number of badges remaining the user can send
|
|
|
|
0 if not allowed or no remaining
|
|
integer if limited sending
|
|
-1 if infinite (should not be displayed)
|
|
"""
|
|
for badge in self:
|
|
if badge._can_grant_badge() != self.CAN_GRANT:
|
|
# if the user cannot grant this badge at all, result is 0
|
|
badge.remaining_sending = 0
|
|
elif not badge.rule_max:
|
|
# if there is no limitation, -1 is returned which means 'infinite'
|
|
badge.remaining_sending = -1
|
|
else:
|
|
badge.remaining_sending = badge.rule_max_number - badge.stat_my_monthly_sending
|
|
|
|
def check_granting(self):
|
|
"""Check the user 'uid' can grant the badge 'badge_id' and raise the appropriate exception
|
|
if not
|
|
|
|
Do not check for SUPERUSER_ID
|
|
"""
|
|
status_code = self._can_grant_badge()
|
|
if status_code == self.CAN_GRANT:
|
|
return True
|
|
elif status_code == self.NOBODY_CAN_GRANT:
|
|
raise exceptions.UserError(_('This badge can not be sent by users.'))
|
|
elif status_code == self.USER_NOT_VIP:
|
|
raise exceptions.UserError(_('You are not in the user allowed list.'))
|
|
elif status_code == self.BADGE_REQUIRED:
|
|
raise exceptions.UserError(_('You do not have the required badges.'))
|
|
elif status_code == self.TOO_MANY:
|
|
raise exceptions.UserError(_('You have already sent this badge too many time this month.'))
|
|
else:
|
|
_logger.error("Unknown badge status code: %s" % status_code)
|
|
return False
|
|
|
|
def _can_grant_badge(self):
|
|
"""Check if a user can grant a badge to another user
|
|
|
|
:param uid: the id of the res.users trying to send the badge
|
|
:param badge_id: the granted badge id
|
|
:return: integer representing the permission.
|
|
"""
|
|
if self.env.user._is_admin():
|
|
return self.CAN_GRANT
|
|
|
|
if self.rule_auth == 'nobody':
|
|
return self.NOBODY_CAN_GRANT
|
|
elif self.rule_auth == 'users' and self.env.user not in self.rule_auth_user_ids:
|
|
return self.USER_NOT_VIP
|
|
elif self.rule_auth == 'having':
|
|
all_user_badges = self.env['gamification.badge.user'].search([('user_id', '=', self.env.uid)])
|
|
if self.rule_auth_badge_ids - all_user_badges:
|
|
return self.BADGE_REQUIRED
|
|
|
|
if self.rule_max and self.stat_my_monthly_sending >= self.rule_max_number:
|
|
return self.TOO_MANY
|
|
|
|
# badge.rule_auth == 'everyone' -> no check
|
|
return self.CAN_GRANT
|