[ADD] rating : base rating module contains a mixin and the meta model rating with graph and kanban views
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Odoo, Open Source Management Solution
|
||||
# Copyright (C) 2014-TODAY Odoo SA (<https://www.odoo.com>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
import models
|
||||
import controllers
|
||||
@@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': 'Customer Rating',
|
||||
'version': '1.0',
|
||||
'category': 'Tools',
|
||||
'description': """
|
||||
This module allows a customer to give rating.
|
||||
""",
|
||||
'author': 'Odoo SA',
|
||||
'website': 'http://odoo.com',
|
||||
'depends': [
|
||||
'mail',
|
||||
],
|
||||
'data': [
|
||||
'views/rating_view.xml',
|
||||
'views/rating_template.xml',
|
||||
'security/ir.model.access.csv'
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import main
|
||||
@@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import werkzeug
|
||||
from openerp import http
|
||||
from openerp.http import request
|
||||
|
||||
|
||||
class Rating(http.Controller):
|
||||
|
||||
@http.route('/rating/<string:token>/<int:rate>', type='http', auth="public")
|
||||
def add_rating(self, token, rate, **kwargs):
|
||||
Rating = request.env['rating.rating']
|
||||
rating = Rating.search([('access_token', '=', token)])
|
||||
if rating:
|
||||
is_rated = bool(rating.rating != -1)
|
||||
if not is_rated:
|
||||
request.env['rating.rating'].sudo().apply_rating(rate, token=token)
|
||||
# redirect to the form view if logged person
|
||||
if request.session.uid and not is_rated:
|
||||
record = request.env[rating.res_model].browse(rating.res_id)
|
||||
return werkzeug.utils.redirect('/web#model=%s&id=%s&view_type=form' % (record._name, record.id))
|
||||
return request.render('rating.rating_external_page_view', {'rating': rate, 'is_rated': is_rated})
|
||||
return request.not_found()
|
||||
@@ -0,0 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import rating
|
||||
@@ -0,0 +1,151 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import uuid
|
||||
from openerp import api, fields, models, _
|
||||
|
||||
class Rating(models.Model):
|
||||
|
||||
_name = "rating.rating"
|
||||
_description = "Rating"
|
||||
_order = 'create_date desc'
|
||||
_rec_name = 'res_name'
|
||||
_sql_constraints = [
|
||||
('rating_range', 'check(rating >= -1 and rating <= 10)', 'Rating should be between -1 to 10'),
|
||||
]
|
||||
|
||||
@api.multi
|
||||
@api.depends('res_model', 'res_id')
|
||||
def _compute_res_name(self):
|
||||
for record in self:
|
||||
name = self.env[record.res_model].sudo().browse(record.res_id).name_get()
|
||||
record.res_name = name and name[0][1] or ('%s/%s') % (record.res_model, record.res_id)
|
||||
|
||||
@api.model
|
||||
def new_access_token(self):
|
||||
return uuid.uuid4().hex
|
||||
|
||||
res_name = fields.Char(string='Resource Name', compute='_compute_res_name', store=True, help="The name of the rated ressource.")
|
||||
res_model = fields.Char(string='Document Model', required=True, help="Model name of the rated object", index=True)
|
||||
res_id = fields.Integer(string='Document ID', required=True, help="Identifier of the rated object", index=True)
|
||||
rated_partner_id = fields.Many2one('res.partner', string="Rated Partner", help="Owner of the rated ressource")
|
||||
partner_id = fields.Many2one('res.partner', string='Customer', help="Author of the rating")
|
||||
rating = fields.Float(string="Rating", group_operator="avg", default=-1, help="Rating value")
|
||||
feedback = fields.Text('Feedback reason', help="Reason of the rating")
|
||||
access_token = fields.Char(string='Security Token', default=new_access_token, help="Access token to set the rating of the value")
|
||||
|
||||
@api.model
|
||||
def apply_rating(self, rate, res_model=None, res_id=None, token=None):
|
||||
""" apply a rating for given res_model/res_id or token. If the res_model is a mail.thread
|
||||
object, a message will be posted in the chatter.
|
||||
:param float rating : the rating value to apply
|
||||
:param list res_id : id of the rated object.
|
||||
:param string res_model : name of model.
|
||||
:param string token : access token
|
||||
:return the rating record
|
||||
"""
|
||||
domain = [('access_token', '=', token)] if token else [('res_model', '=', res_model), ('res_id', '=', res_id)]
|
||||
rating = self.search(domain, limit=1)
|
||||
if rating:
|
||||
rating.write({'rating' : rate})
|
||||
if hasattr(self.env[rating.res_model], 'message_post'):
|
||||
record = self.env[rating.res_model].sudo().browse(rating.res_id)
|
||||
record.message_post(
|
||||
body="%s %s <br/><img src='/rating/static/src/img/rating_%s.png' style='width:20px;height:20px'/>"
|
||||
% (rating.sudo().partner_id.name, _('rated it'), rate),
|
||||
author_id=rating.partner_id and rating.partner_id.id or None # None will set the default author in mail_thread.py
|
||||
)
|
||||
return rating
|
||||
|
||||
@api.multi
|
||||
def reset(self):
|
||||
for record in self:
|
||||
record.write({
|
||||
'rating': -1,
|
||||
'access_token': record.new_access_token(),
|
||||
'feedback' : False
|
||||
})
|
||||
|
||||
|
||||
class RatingMixin(models.AbstractModel):
|
||||
|
||||
_name = 'rating.mixin'
|
||||
_description = "Rating Mixin"
|
||||
|
||||
rating_ids = fields.One2many('rating.rating', 'res_id', string='Rating', domain=lambda self: [('res_model', '=', self._name)])
|
||||
|
||||
@api.multi
|
||||
def rating_send_request(self, template, partner_id, rated_partner_id, reuse_rating=True):
|
||||
""" This method create (empty) rating objects for the current recordsets
|
||||
and send this request by mail (given email template) with the given
|
||||
rated_partner_id and partner_id as recipient and sender of the email.
|
||||
:param template : the email template to send. The res_model of the
|
||||
template must be 'rating.rating'.
|
||||
:type template : mail.template
|
||||
:param res_model : model name of the object to rated_partner_id
|
||||
:type res_model : string
|
||||
:param res_id : id the record to rate
|
||||
:type res_id : int
|
||||
:param partner_id : the recipient partner
|
||||
:type partner : res.partner
|
||||
:param rated_partner_id : the sender partner
|
||||
:type rated_partner_id : res.partner
|
||||
:param reuse_rating : if True, the rating of the current objects will
|
||||
be reset. Otherwise a new rating will be create
|
||||
:type reuse_rating : boolean
|
||||
"""
|
||||
if not rated_partner_id.email or not partner_id.email:
|
||||
return False
|
||||
Rating = self.env['rating.rating']
|
||||
res_model = self._name
|
||||
for record in self:
|
||||
res_id = record.id
|
||||
values = {
|
||||
'res_model' : res_model,
|
||||
'res_id' : res_id,
|
||||
'partner_id' : partner_id.id,
|
||||
'rated_partner_id' : rated_partner_id.id
|
||||
}
|
||||
if reuse_rating:
|
||||
# search the existing rating for the given res_model/res_id
|
||||
rating = Rating.search([('res_id', '=', res_id), ('res_model', '=', res_model)], limit=1)
|
||||
if rating: # reset the rating
|
||||
rating.reset()
|
||||
else: # create a new one
|
||||
rating = Rating.create(values)
|
||||
else:
|
||||
rating = Rating.create(values)
|
||||
# send the mail
|
||||
template.send_mail(rating.id, force_send=True)
|
||||
|
||||
@api.multi
|
||||
def rating_get_stats(self):
|
||||
""" get the repatition of rating grade for the given res_ids.
|
||||
:param list res_ids : optional list of ids. If not given, this will take the ids of the current recordset.
|
||||
:param string res_model : optional name of model. If not given, the one of the current recordset will be used.
|
||||
:return dictionnary where the key is the rating value (the note), and the value, the number of object (res_model, res_id) having the value
|
||||
"""
|
||||
data = self.env['rating.rating'].read_group([('res_model', '=', self._name), ('res_id', 'in', self.ids), ('rating', '>=', 0)], ['rating'], ['rating', 'res_id'])
|
||||
# init dict with all posible rate value, except -1 (no value for the rating)
|
||||
res = dict.fromkeys(range(11), 0)
|
||||
res.update((d['rating'], d['rating_count']) for d in data)
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
def rating_get_grades(self):
|
||||
""" get the repatition of rating grade for the given res_ids.
|
||||
:param list res_ids : optional list of ids. If not given, this will take the ids of the current recordset.
|
||||
:param string res_model : optional name of model. If not given, the one of the current recordset will be used.
|
||||
:return dictionnary where the key is the grade (great, okay, bad), and the value, the number of object (res_model, res_id) having the grade
|
||||
the grade are compute as 0-30% : Bad
|
||||
31-69%: Okay
|
||||
70-100%: Great
|
||||
"""
|
||||
data = self.rating_get_stats()
|
||||
res = dict.fromkeys(['great', 'okay', 'bad'], 0)
|
||||
for key in data:
|
||||
if key >= 7:
|
||||
res['great'] += data[key]
|
||||
elif key > 3:
|
||||
res['okay'] += data[key]
|
||||
else:
|
||||
res['bad'] += data[key]
|
||||
return res
|
||||
@@ -0,0 +1,4 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_rating_user,rating.rating.user,rating.model_rating_rating,base.group_user,1,1,1,0
|
||||
access_rating_public,rating.rating.public,rating.model_rating_rating,base.group_public,1,1,1,0
|
||||
access_rating_portal,rating.rating.portal,rating.model_rating_rating,base.group_portal,1,1,1,0
|
||||
|
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<template id="rating_external_page_view">
|
||||
<head>
|
||||
<link rel='stylesheet' href='/web/static/lib/bootstrap/css/bootstrap.css'/>
|
||||
</head>
|
||||
<div class='well' t-if="not is_rated">
|
||||
<h1>Thanks for voting !</h1>
|
||||
<div class="oe_right">
|
||||
<p>Your vote has been taken into account.</p>
|
||||
<p>It was <img t-attf-src='../../../rating/static/src/img/rating_#{rating}.png'/></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class='well' t-if="is_rated">
|
||||
<h1>Sorry you have already voted !</h1>
|
||||
</div>
|
||||
</template>
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="view_rating_rating_kanban">
|
||||
<field name="name">rating.rating.kanban</field>
|
||||
<field name="model">rating.rating</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban create="false" class="oe_background_grey">
|
||||
<field name="rating"/>
|
||||
<field name="res_name"/>
|
||||
<field name="create_date"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="rated_partner_id"/>
|
||||
<field name="feedback"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div style="min-height:100px;width:100px">
|
||||
<div t-attf-title="Name : #{ record.res_name.raw_value } <br>
|
||||
#{ record.partner_id.value ? 'Customer : ' + record.partner_id.value + '<br>' : '' }
|
||||
#{ record.rated_partner_id.value ? 'Responsible : ' + record.rated_partner_id.value+'<br>' : '' }
|
||||
Date : #{ moment(record.create_date.raw_value).format('L') }
|
||||
#{ record.feedback.raw_value ? '<br>'+'Reason : ' + record.feedback.raw_value : '' }"
|
||||
data-html="true" data-placement="bottom">
|
||||
<img t-attf-src="rating/static/src/img/rating_#{record.rating.raw_value}.png" class="oe_kanban_image"/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_rating_rating_graph">
|
||||
<field name="name">rating.rating.graph</field>
|
||||
<field name="model">rating.rating</field>
|
||||
<field name="arch" type="xml">
|
||||
<graph string="Rating Average" type="pivot">
|
||||
<field name="res_name" type="row"/>
|
||||
<field name="rating" type="measure"/>
|
||||
</graph>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_rating_rating_search">
|
||||
<field name="name">rating.rating.search</field>
|
||||
<field name="model">rating.rating</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Ratings">
|
||||
<field name="rated_partner_id"/>
|
||||
<field name="rating"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="res_name"/>
|
||||
<field name="res_id"/>
|
||||
<filter string="Happy" domain="[('rating', '>=', 7)]"/>
|
||||
<filter string="Okay" domain="[('rating', '>', 3), ('rating', '<', 7)]"/>
|
||||
<filter string="Unhappy" domain="[('rating', '>=', 0), ('rating', '<=',3)]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Responsible" name="Responsible" context="{'group_by':'rated_partner_id'}"/>
|
||||
<filter string="Customer" name="customer" context="{'group_by':'partner_id'}"/>
|
||||
<filter string="Rating" name="rating" context="{'group_by':'rating'}"/>
|
||||
<filter string="Resource" name="resource" context="{'group_by':'res_name'}"/>
|
||||
<filter string="Day" context="{'group_by':'create_date:day'}"/>
|
||||
<filter string="Month" context="{'group_by':'create_date:month'}"/>
|
||||
<filter string="Year" context="{'group_by':'create_date:year'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_view_rating" model="ir.actions.act_window">
|
||||
<field name="name">Rating</field>
|
||||
<field name="res_model">rating.rating</field>
|
||||
<field name="view_mode">kanban,graph</field>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
There is no rating for this object at the moment.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
Reference in New Issue
Block a user