diff --git a/addons/project/views/project_views.xml b/addons/project/views/project_views.xml
index 0bb19d55a8a..46e8a77ffc3 100644
--- a/addons/project/views/project_views.xml
+++ b/addons/project/views/project_views.xml
@@ -535,6 +535,11 @@
+
+
+
+
+
diff --git a/addons/rating/models/rating_mixin.py b/addons/rating/models/rating_mixin.py
index ff5f55d1583..f6ef3ce06b5 100644
--- a/addons/rating/models/rating_mixin.py
+++ b/addons/rating/models/rating_mixin.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import operator
+from collections import defaultdict
from datetime import timedelta
from odoo import api, fields, models, tools
@@ -32,11 +33,14 @@ class RatingParentMixin(models.AbstractModel):
compute="_compute_rating_percentage_satisfaction", compute_sudo=True,
store=False, help="Percentage of happy ratings")
rating_count = fields.Integer(string='# Ratings', compute="_compute_rating_percentage_satisfaction", compute_sudo=True)
+ rating_avg = fields.Float('Average Rating', groups='base.group_user',
+ compute='_compute_rating_percentage_satisfaction', compute_sudo=True, search='_search_rating_avg')
+ rating_last_value = fields.Float('Rating Last Value', groups='base.group_user', related='rating_ids.rating')
@api.depends('rating_ids.rating', 'rating_ids.consumed')
def _compute_rating_percentage_satisfaction(self):
# build domain and fetch data
- domain = [('parent_res_model', '=', self._name), ('parent_res_id', 'in', self.ids), ('rating', '>=', 1), ('consumed', '=', True)]
+ domain = [('parent_res_model', '=', self._name), ('parent_res_id', 'in', self.ids), ('rating', '>=', RATING_LIMIT_MIN), ('consumed', '=', True)]
if self._rating_satisfaction_days:
domain += [('write_date', '>=', fields.Datetime.to_string(fields.datetime.now() - timedelta(days=self._rating_satisfaction_days)))]
data = self.env['rating.rating'].read_group(domain, ['parent_res_id', 'rating'], ['parent_res_id', 'rating'], lazy=False)
@@ -44,6 +48,7 @@ class RatingParentMixin(models.AbstractModel):
# get repartition of grades per parent id
default_grades = {'great': 0, 'okay': 0, 'bad': 0}
grades_per_parent = dict((parent_id, dict(default_grades)) for parent_id in self.ids) # map: {parent_id: {'great': 0, 'bad': 0, 'ok': 0}}
+ rating_scores_per_parent = defaultdict(int) # contains the total of the rating values per record
for item in data:
parent_id = item['parent_res_id']
rating = item['rating']
@@ -53,12 +58,30 @@ class RatingParentMixin(models.AbstractModel):
grades_per_parent[parent_id]['okay'] += item['__count']
else:
grades_per_parent[parent_id]['bad'] += item['__count']
+ rating_scores_per_parent[parent_id] += rating * item['__count']
# compute percentage per parent
for record in self:
repartition = grades_per_parent.get(record.id, default_grades)
- record.rating_count = sum(repartition.values())
- record.rating_percentage_satisfaction = repartition['great'] * 100 / sum(repartition.values()) if sum(repartition.values()) else -1
+ rating_count = sum(repartition.values())
+ record.rating_count = rating_count
+ record.rating_percentage_satisfaction = repartition['great'] * 100 / rating_count if rating_count else -1
+ record.rating_avg = rating_scores_per_parent[record.id] / rating_count if rating_count else 0
+
+ def _search_rating_avg(self, operator, value):
+ if operator not in OPERATOR_MAPPING:
+ raise NotImplementedError('This operator %s is not supported in this search method.' % operator)
+ domain = [('parent_res_model', '=', self._name), ('consumed', '=', True), ('rating', '>=', RATING_LIMIT_MIN)]
+ if self._rating_satisfaction_days:
+ min_date = fields.datetime.now() - timedelta(days=self._rating_satisfaction_days)
+ domain = expression.AND([domain, [('write_date', '>=', fields.Datetime.to_string(min_date))]])
+ rating_read_group = self.env['rating.rating'].sudo().read_group(domain, ['parent_res_id', 'rating_avg:avg(rating)'], ['parent_res_id'])
+ parent_res_ids = [
+ res['parent_res_id']
+ for res in rating_read_group
+ if OPERATOR_MAPPING[operator](float_compare(res['rating_avg'], value, 2), 0)
+ ]
+ return [('id', 'in', parent_res_ids)]
class RatingMixin(models.AbstractModel):