[IMP] rating, project, im_livechat: improve handling of ratings

RATING

Rating texts currently have a negative skewness. Indeed apart top rating
all ratings have a negative feeling. In this commit we update them
to match more closely a 1-5 range from dissatisfied to satisfied, ok being
the middle value.

PROJECT

Filter customer rating were taking in account ratings from first e-mail
instead of current satisfaction.

IM LIVECHAT

Livechat did not catch feedbacks without comment.

Task ID-2439720
COM PR odoo/odoo#66992
ENT PR odoo/enterprise#16757
This commit is contained in:
ram
2021-04-26 14:10:20 +00:00
committed by Thibault Delavallée
parent a40b52edf4
commit ddf7195ae3
12 changed files with 71 additions and 27 deletions
@@ -98,7 +98,7 @@ class ImLivechatChannel(models.Model):
:returns : the ir.action 'action_view_rating' with the correct domain
"""
self.ensure_one()
action = self.env['ir.actions.act_window']._for_xml_id('im_livechat.rating_rating_action_view_livechat_rating')
action = self.env['ir.actions.act_window']._for_xml_id('im_livechat.rating_rating_action_livechat')
action['domain'] = [('parent_res_id', '=', self.id), ('parent_res_model', '=', 'im_livechat.channel')]
return action
@@ -183,7 +183,7 @@ class ImLivechatChannel(models.Model):
self.env.cr.execute("""SELECT COUNT(DISTINCT c.id), c.livechat_operator_id
FROM mail_channel c
LEFT OUTER JOIN mail_message m ON c.id = m.res_id AND m.model = 'mail.channel'
WHERE c.channel_type = 'livechat'
WHERE c.channel_type = 'livechat'
AND c.livechat_operator_id in %s
AND m.create_date > ((now() at time zone 'UTC') - interval '30 minutes')
GROUP BY c.livechat_operator_id
@@ -505,11 +505,13 @@ var Feedback = Widget.extend({
rate: this.rating,
reason: reason,
};
this.dp.add(session.rpc('/im_livechat/feedback', args)).then(function () {
this.dp.add(session.rpc('/im_livechat/feedback', args)).then(function (response) {
var emoji = RATING_TO_EMOJI[self.rating] || "??";
var content = _.str.sprintf(_t("Rating: %s"), emoji);
if (reason) {
content += " \n" + reason;
if (!reason) {
var content = _.str.sprintf(_t("Rating: %s"), emoji);
}
else {
var content = "Rating reason: \n" + reason;
}
self.trigger('send_message', { content: content, isFeedback: true });
});
@@ -555,6 +557,7 @@ var Feedback = Widget.extend({
// only display textearea if bad smiley selected
if (this.rating !== 5) {
this._sendFeedback();
this.$('.o_livechat_rating_reason').show();
} else {
this.$('.o_livechat_rating_reason').hide();
@@ -50,7 +50,7 @@
<t t-esc="record.nbr_channel.raw_value"/> Sessions
<div class="float-right">
<t t-if="record.rating_percentage_satisfaction.raw_value &gt; 0">
<a type="action" name="%(rating_rating_action_view_livechat_rating)d" tabindex="10">
<a type="action" name="%(rating_rating_action_livechat)d" tabindex="10">
<i class="fa fa-smile-o text-success" t-if="record.rating_percentage_satisfaction.raw_value &gt;= 70" title="Rating: Great" role="img" aria-label="Happy face"/>
<i class="fa fa-meh-o text-warning" t-if="record.rating_percentage_satisfaction.raw_value &gt; 30 and record.rating_percentage_satisfaction.raw_value &lt; 70" title="Rating: Okay" role="img" aria-label="Neutral face"/>
<i class="fa fa-frown-o text-danger" t-if="record.rating_percentage_satisfaction.raw_value &lt;= 30" title="Rating: Bad" role="img" aria-label="Sad face"/>
+43 -2
View File
@@ -1,6 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="rating_rating_view_form_livechat" model="ir.ui.view">
<field name="name">rating.rating.form.livechat</field>
<field name="model">rating.rating</field>
<field name="inherit_id" ref="rating.rating_rating_view_form"/>
<field name="priority">32</field>
<field name="mode">primary</field>
<field name="arch" type="xml">
<xpath expr="//div[@name='rating_image_container']" position="replace">
<field name="rating_text" string="Rating"/>
</xpath>
</field>
</record>
<record id="rating_rating_view_search_livechat" model="ir.ui.view">
<field name="name">rating.rating.search.livechat</field>
<field name="model">rating.rating</field>
@@ -22,11 +35,11 @@
</field>
</record>
<record id="rating_rating_action_view_livechat_rating" model="ir.actions.act_window">
<record id="rating_rating_action_livechat" model="ir.actions.act_window">
<field name="name">Ratings for livechat channel</field>
<field name="res_model">rating.rating</field>
<field name="view_mode">kanban,tree,graph,pivot,form</field>
<field name="domain">[('parent_res_model','=','im_livechat.channel'), ('consumed','=',True)]</field>
<field name="domain">[('parent_res_model', '=', 'im_livechat.channel'), ('consumed','=',True)]</field>
<field name="search_view_id" ref="rating_rating_view_search_livechat"/>
<field name="help" type="html">
<p class="o_view_nocontent_empty_folder">
@@ -36,6 +49,20 @@
<field name="context">{'search_default_rating_last_7_days': 1}</field>
</record>
<record id="rating_rating_action_livechat_view_kanban" model="ir.actions.act_window.view">
<field name="sequence" eval="1"/>
<field name="view_mode">kanban</field>
<field name="act_window_id" ref="rating_rating_action_livechat"/>
<field name="view_id" ref="rating.rating_rating_view_kanban"/>
</record>
<record id="rating_rating_action_livechat_view_form" model="ir.actions.act_window.view">
<field name="sequence">5</field>
<field name="view_mode">form</field>
<field name="act_window_id" ref="rating_rating_action_livechat"/>
<field name="view_id" ref="rating_rating_view_form_livechat"/>
</record>
<record id="rating_rating_action_livechat_report" model="ir.actions.act_window">
<field name="name">Customer Ratings</field>
<field name="res_model">rating.rating</field>
@@ -49,4 +76,18 @@
</field>
</record>
<record id="rating_rating_action_livechat_report_view_kanban" model="ir.actions.act_window.view">
<field name="sequence">1</field>
<field name="view_mode">kanban</field>
<field name="act_window_id" ref="rating_rating_action_livechat_report"/>
<field name="view_id" ref="rating.rating_rating_view_kanban"/>
</record>
<record id="rating_rating_action_livechat_report_view_form" model="ir.actions.act_window.view">
<field name="sequence">5</field>
<field name="view_mode">form</field>
<field name="act_window_id" ref="rating_rating_action_livechat_report"/>
<field name="view_id" ref="rating_rating_view_form_livechat"/>
</record>
</odoo>
+2 -2
View File
@@ -66,12 +66,12 @@
</td>
<td>
<a href="/rate/${access_token}/3">
<img alt="Not satisfied" src="/rating/static/src/img/rating_3.png" title="Not satisfied"/>
<img alt="Okay" src="/rating/static/src/img/rating_3.png" title="Okay"/>
</a>
</td>
<td>
<a href="/rate/${access_token}/1">
<img alt="Highly Dissatisfied" src="/rating/static/src/img/rating_1.png" title="Highly Dissatisfied"/>
<img alt="Dissatisfied" src="/rating/static/src/img/rating_1.png" title="Dissatisfied"/>
</a>
</td>
</tr>
+1 -1
View File
@@ -247,7 +247,7 @@ class TestProjectFlow(TestProjectCommon):
first_task.invalidate_cache()
self.assertEqual(rating_good.rating_text, 'satisfied')
self.assertEqual(rating_bad.rating_text, 'not_satisfied')
self.assertEqual(rating_bad.rating_text, 'okay')
self.assertEqual(first_task.rating_count, 1, "Task should have only one rating associated, since one is not consumed")
self.assertEqual(rating_good.parent_res_id, self.project_pigs.id)
+2 -2
View File
@@ -943,8 +943,8 @@
<field name="activity_ids" widget="kanban_activity"/>
<b t-if="record.rating_ids.raw_value.length">
<span style="font-weight:bold;" class="fa fa-fw mt4 fa-smile-o text-success" t-if="record.rating_last_value.value == 5" title="Latest Rating: Satisfied" role="img" aria-label="Happy face"/>
<span style="font-weight:bold;" class="fa fa-fw mt4 fa-meh-o text-warning" t-if="record.rating_last_value.value == 3" title="Latest Rating: Not Satisfied" role="img" aria-label="Neutral face"/>
<span style="font-weight:bold;" class="fa fa-fw mt4 fa-frown-o text-danger" t-if="record.rating_last_value.value == 1" title="Latest Rating: Higly Dissatisfied" role="img" aria-label="Sad face"/>
<span style="font-weight:bold;" class="fa fa-fw mt4 fa-meh-o text-warning" t-if="record.rating_last_value.value == 3" title="Latest Rating: Okay" role="img" aria-label="Neutral face"/>
<span style="font-weight:bold;" class="fa fa-fw mt4 fa-frown-o text-danger" t-if="record.rating_last_value.value == 1" title="Latest Rating: Dissatisfied" role="img" aria-label="Sad face"/>
</b>
</div>
<div class="oe_kanban_bottom_right" t-if="!selection_mode">
+1 -1
View File
@@ -83,7 +83,7 @@
</xpath>
<xpath expr="/search" position="inside">
<filter string="Last 30 Days" name="rating_last_30_days" domain="[
('create_date', '>=', (datetime.datetime.combine(context_today() + relativedelta(days=-30), datetime.time(0,0,0)).to_utc()).strftime('%Y-%m-%d %H:%M:%S'))]"
('write_date', '>=', (datetime.datetime.combine(context_today() + relativedelta(days=-30), datetime.time(0,0,0)).to_utc()).strftime('%Y-%m-%d %H:%M:%S'))]"
/>
<separator/>
</xpath>
+3 -3
View File
@@ -39,9 +39,9 @@ class Rating(http.Controller):
if not rating:
return request.not_found()
rate_names = {
5: _("satisfied"),
3: _("not satisfied"),
1: _("highly dissatisfied")
5: _("Satisfied"),
3: _("Okay"),
1: _("Dissatisfied")
}
rating.write({'rating': rate, 'consumed': True})
lang = rating.partner_id.lang or get_lang(request.env).code
+4 -4
View File
@@ -56,8 +56,8 @@ class Rating(models.Model):
rating_image = fields.Binary('Image', compute='_compute_rating_image')
rating_text = fields.Selection([
('satisfied', 'Satisfied'),
('not_satisfied', 'Not satisfied'),
('highly_dissatisfied', 'Highly dissatisfied'),
('okay', 'Okay'),
('dissatisfied', 'Dissatisfied'),
('no_rating', 'No Rating yet')], string='Rating', store=True, compute='_compute_rating_text', readonly=True)
feedback = fields.Text('Comment', help="Reason of the rating")
message_id = fields.Many2one(
@@ -119,9 +119,9 @@ class Rating(models.Model):
if rating.rating >= RATING_LIMIT_SATISFIED:
rating.rating_text = 'satisfied'
elif rating.rating >= RATING_LIMIT_OK:
rating.rating_text = 'not_satisfied'
rating.rating_text = 'okay'
elif rating.rating >= RATING_LIMIT_MIN:
rating.rating_text = 'highly_dissatisfied'
rating.rating_text = 'dissatisfied'
else:
rating.rating_text = 'no_rating'
+3 -3
View File
@@ -6,7 +6,7 @@
<field name="model">rating.rating</field>
<field name="arch" type="xml">
<tree string="Rating" create="false" edit="false" sample="1">
<field name="rating_text" decoration-danger="rating_text == 'highly_dissatisfied'" decoration-warning="rating_text == 'not_satisfied'" decoration-success="rating_text == 'satisfied'" class="font-weight-bold"/>
<field name="rating_text" decoration-danger="rating_text == 'dissatisfied'" decoration-warning="rating_text == 'okay'" decoration-success="rating_text == 'satisfied'" class="font-weight-bold"/>
<field name="feedback"/>
<field name="rated_partner_id"/>
<field name="partner_id"/>
@@ -138,8 +138,8 @@
<field name="res_id"/>
<field name="parent_res_name" filter_domain="[('parent_res_name','ilike',self)]"/>
<filter string="Satisfied" name="rating_happy" domain="[('rating_text', '=', 'satisfied')]"/>
<filter string="Not satisfied" name="rating_okay" domain="[('rating_text', '=', 'not_satisfied')]"/>
<filter string="Highly dissatisfied" name="rating_unhappy" domain="[('rating_text', '=', 'highly_dissatisfied')]"/>
<filter string="Okay" name="rating_okay" domain="[('rating_text', '=', 'okay')]"/>
<filter string="Dissatisfied" name="rating_unhappy" domain="[('rating_text', '=', 'dissatisfied')]"/>
<separator/>
<filter string="My Ratings" name="my_ratings" domain="[('rated_partner_id.user_ids', 'in', [uid])]"/>
<separator/>
+2 -2
View File
@@ -1031,8 +1031,8 @@ links to the rating object. A very basic email template could look like this:
<p>How satsified are you?</p>
<ul>
<li><a href="/rate/${access_token}/5">Satisfied</a></li>
<li><a href="/rate/${access_token}/3">Not satisfied</a></li>
<li><a href="/rate/${access_token}/1">Very unsatisfied</a></li>
<li><a href="/rate/${access_token}/3">Okay</a></li>
<li><a href="/rate/${access_token}/1">Dissatisfied</a></li>
</ul>
]]></field>
</record>