196 lines
8.6 KiB
Python
196 lines
8.6 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||
|
import base64
|
||
|
import uuid
|
||
|
|
||
|
from odoo import api, fields, models
|
||
|
from odoo.addons.rating.models import rating_data
|
||
|
from odoo.tools.misc import file_open
|
||
|
|
||
|
|
||
|
class Rating(models.Model):
|
||
|
_name = "rating.rating"
|
||
|
_description = "Rating"
|
||
|
_order = 'write_date desc, id desc'
|
||
|
_rec_name = 'res_name'
|
||
|
|
||
|
@api.model
|
||
|
def _default_access_token(self):
|
||
|
return uuid.uuid4().hex
|
||
|
|
||
|
@api.model
|
||
|
def _selection_target_model(self):
|
||
|
return [(model.model, model.name) for model in self.env['ir.model'].sudo().search([])]
|
||
|
|
||
|
create_date = fields.Datetime(string="Submitted on")
|
||
|
res_name = fields.Char(string='Resource name', compute='_compute_res_name', store=True)
|
||
|
res_model_id = fields.Many2one('ir.model', 'Related Document Model', index=True, ondelete='cascade')
|
||
|
res_model = fields.Char(string='Document Model', related='res_model_id.model', store=True, index=True, readonly=True)
|
||
|
res_id = fields.Many2oneReference(string='Document', model_field='res_model', required=True, index=True)
|
||
|
resource_ref = fields.Reference(
|
||
|
string='Resource Ref', selection='_selection_target_model',
|
||
|
compute='_compute_resource_ref', readonly=True)
|
||
|
parent_res_name = fields.Char('Parent Document Name', compute='_compute_parent_res_name', store=True)
|
||
|
parent_res_model_id = fields.Many2one('ir.model', 'Parent Related Document Model', index=True, ondelete='cascade')
|
||
|
parent_res_model = fields.Char('Parent Document Model', store=True, related='parent_res_model_id.model', index=True, readonly=False)
|
||
|
parent_res_id = fields.Integer('Parent Document', index=True)
|
||
|
parent_ref = fields.Reference(
|
||
|
string='Parent Ref', selection='_selection_target_model',
|
||
|
compute='_compute_parent_ref', readonly=True)
|
||
|
rated_partner_id = fields.Many2one('res.partner', string="Rated Operator")
|
||
|
rated_partner_name = fields.Char(related="rated_partner_id.name")
|
||
|
partner_id = fields.Many2one('res.partner', string='Customer')
|
||
|
rating = fields.Float(string="Rating Value", group_operator="avg", default=0)
|
||
|
rating_image = fields.Binary('Image', compute='_compute_rating_image')
|
||
|
rating_image_url = fields.Char('Image URL', compute='_compute_rating_image')
|
||
|
rating_text = fields.Selection(rating_data.RATING_TEXT, string='Rating', store=True, compute='_compute_rating_text', readonly=True)
|
||
|
feedback = fields.Text('Comment')
|
||
|
message_id = fields.Many2one(
|
||
|
'mail.message', string="Message",
|
||
|
index=True, ondelete='cascade')
|
||
|
is_internal = fields.Boolean('Visible Internally Only', readonly=False, related='message_id.is_internal', store=True)
|
||
|
access_token = fields.Char('Security Token', default=_default_access_token)
|
||
|
consumed = fields.Boolean(string="Filled Rating")
|
||
|
|
||
|
_sql_constraints = [
|
||
|
('rating_range', 'check(rating >= 0 and rating <= 5)', 'Rating should be between 0 and 5'),
|
||
|
]
|
||
|
|
||
|
@api.depends('res_model', 'res_id')
|
||
|
def _compute_res_name(self):
|
||
|
for rating in self:
|
||
|
name = self.env[rating.res_model].sudo().browse(rating.res_id).display_name
|
||
|
rating.res_name = name or f'{rating.res_model}/{rating.res_id}'
|
||
|
|
||
|
@api.depends('res_model', 'res_id')
|
||
|
def _compute_resource_ref(self):
|
||
|
for rating in self:
|
||
|
if rating.res_model and rating.res_model in self.env:
|
||
|
rating.resource_ref = '%s,%s' % (rating.res_model, rating.res_id or 0)
|
||
|
else:
|
||
|
rating.resource_ref = None
|
||
|
|
||
|
@api.depends('parent_res_model', 'parent_res_id')
|
||
|
def _compute_parent_ref(self):
|
||
|
for rating in self:
|
||
|
if rating.parent_res_model and rating.parent_res_model in self.env:
|
||
|
rating.parent_ref = '%s,%s' % (rating.parent_res_model, rating.parent_res_id or 0)
|
||
|
else:
|
||
|
rating.parent_ref = None
|
||
|
|
||
|
@api.depends('parent_res_model', 'parent_res_id')
|
||
|
def _compute_parent_res_name(self):
|
||
|
for rating in self:
|
||
|
name = False
|
||
|
if rating.parent_res_model and rating.parent_res_id:
|
||
|
name = self.env[rating.parent_res_model].sudo().browse(rating.parent_res_id).display_name
|
||
|
name = name or f'{rating.parent_res_model}/{rating.parent_res_id}'
|
||
|
rating.parent_res_name = name
|
||
|
|
||
|
def _get_rating_image_filename(self):
|
||
|
self.ensure_one()
|
||
|
return 'rating_%s.png' % rating_data._rating_to_threshold(self.rating)
|
||
|
|
||
|
@api.depends('rating')
|
||
|
def _compute_rating_image(self):
|
||
|
self.rating_image_url = False
|
||
|
self.rating_image = False
|
||
|
for rating in self:
|
||
|
image_path = f'rating/static/src/img/{rating._get_rating_image_filename()}'
|
||
|
rating.rating_image_url = f'/{image_path}'
|
||
|
try:
|
||
|
rating.rating_image = base64.b64encode(
|
||
|
file_open(image_path, 'rb', filter_ext=('.png',)).read())
|
||
|
except (IOError, OSError, FileNotFoundError):
|
||
|
rating.rating_image = False
|
||
|
|
||
|
@api.depends('rating')
|
||
|
def _compute_rating_text(self):
|
||
|
for rating in self:
|
||
|
rating.rating_text = rating_data._rating_to_text(rating.rating)
|
||
|
|
||
|
# ------------------------------------------------------------
|
||
|
# CRUD
|
||
|
# ------------------------------------------------------------
|
||
|
|
||
|
@api.model_create_multi
|
||
|
def create(self, vals_list):
|
||
|
for values in vals_list:
|
||
|
if values.get('res_model_id') and values.get('res_id'):
|
||
|
values.update(self._find_parent_data(values))
|
||
|
return super().create(vals_list)
|
||
|
|
||
|
def write(self, values):
|
||
|
if values.get('res_model_id') and values.get('res_id'):
|
||
|
values.update(self._find_parent_data(values))
|
||
|
return super(Rating, self).write(values)
|
||
|
|
||
|
def unlink(self):
|
||
|
# OPW-2181568: Delete the chatter message too
|
||
|
self.env['mail.message'].search([('rating_ids', 'in', self.ids)]).unlink()
|
||
|
return super(Rating, self).unlink()
|
||
|
|
||
|
def _find_parent_data(self, values):
|
||
|
""" Determine the parent res_model/res_id, based on the values to create or write """
|
||
|
current_model_name = self.env['ir.model'].sudo().browse(values['res_model_id']).model
|
||
|
current_record = self.env[current_model_name].browse(values['res_id'])
|
||
|
data = {
|
||
|
'parent_res_model_id': False,
|
||
|
'parent_res_id': False,
|
||
|
}
|
||
|
if hasattr(current_record, '_rating_get_parent_field_name'):
|
||
|
current_record_parent = current_record._rating_get_parent_field_name()
|
||
|
if current_record_parent:
|
||
|
parent_res_model = getattr(current_record, current_record_parent)
|
||
|
data['parent_res_model_id'] = self.env['ir.model']._get(parent_res_model._name).id
|
||
|
data['parent_res_id'] = parent_res_model.id
|
||
|
return data
|
||
|
|
||
|
# ------------------------------------------------------------
|
||
|
# ACTIONS
|
||
|
# ------------------------------------------------------------
|
||
|
|
||
|
def reset(self):
|
||
|
for record in self:
|
||
|
record.write({
|
||
|
'rating': 0,
|
||
|
'access_token': record._default_access_token(),
|
||
|
'feedback': False,
|
||
|
'consumed': False,
|
||
|
})
|
||
|
|
||
|
def action_open_rated_object(self):
|
||
|
self.ensure_one()
|
||
|
return {
|
||
|
'type': 'ir.actions.act_window',
|
||
|
'res_model': self.res_model,
|
||
|
'res_id': self.res_id,
|
||
|
'views': [[False, 'form']]
|
||
|
}
|
||
|
|
||
|
# ------------------------------------------------------------
|
||
|
# TOOLS
|
||
|
# ------------------------------------------------------------
|
||
|
|
||
|
def _classify_by_model(self):
|
||
|
""" To ease batch computation of various ratings related methods they
|
||
|
are classified by model. Ratings not linked to a valid record through
|
||
|
res_model / res_id are ignored.
|
||
|
|
||
|
:return dict: for each model having at least one rating in self, have
|
||
|
a sub-dict containing
|
||
|
* ratings: ratings related to that model;
|
||
|
* record IDs: records linked to the ratings of that model, in same
|
||
|
order;
|
||
|
"""
|
||
|
data_by_model = {}
|
||
|
for rating in self.filtered(lambda act: act.res_model and act.res_id):
|
||
|
if rating.res_model not in data_by_model:
|
||
|
data_by_model[rating.res_model] = {
|
||
|
'ratings': self.env['rating.rating'],
|
||
|
'record_ids': [],
|
||
|
}
|
||
|
data_by_model[rating.res_model]['ratings'] += rating
|
||
|
data_by_model[rating.res_model]['record_ids'].append(rating.res_id)
|
||
|
return data_by_model
|