[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:
@@ -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 > 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 >= 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 > 30 and record.rating_percentage_satisfaction.raw_value < 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 <= 30" title="Rating: Bad" role="img" aria-label="Sad face"/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user