292 lines
10 KiB
Python
292 lines
10 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
from odoo import http
|
|
from odoo.http import request
|
|
from odoo.exceptions import ValidationError
|
|
import logging
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class RatingController(http.Controller):
|
|
"""Controller for handling 5-star rating submissions from web and email"""
|
|
|
|
@http.route('/rating/<string:token>',
|
|
type='http', auth='public', website=True, methods=['GET'])
|
|
def rating_form(self, token, **kwargs):
|
|
"""
|
|
Display the web rating form for a given token
|
|
|
|
Args:
|
|
token: Unique rating token
|
|
**kwargs: Additional parameters
|
|
|
|
Returns:
|
|
Rendered rating form page or error page
|
|
"""
|
|
try:
|
|
# Find the rating record by token
|
|
rating = request.env['rating.rating'].sudo().search([
|
|
('access_token', '=', token)
|
|
], limit=1)
|
|
|
|
if not rating:
|
|
_logger.warning('Rating not found for token: %s', token)
|
|
return self._render_error_page(
|
|
'Invalid Link',
|
|
'This rating link is invalid or has expired. '
|
|
'Please contact support if you need assistance.'
|
|
)
|
|
|
|
# Get ticket information if available
|
|
ticket_name = ''
|
|
if rating.res_model == 'helpdesk.ticket' and rating.res_id:
|
|
ticket = request.env['helpdesk.ticket'].sudo().browse(rating.res_id)
|
|
if ticket.exists():
|
|
ticket_name = ticket.name or f'Ticket #{ticket.id}'
|
|
|
|
values = {
|
|
'token': token,
|
|
'rating': rating,
|
|
'ticket_name': ticket_name,
|
|
'page_title': 'Rate Your Experience',
|
|
}
|
|
|
|
return request.render(
|
|
'helpdesk_rating_five_stars.rating_form_page',
|
|
values
|
|
)
|
|
|
|
except Exception as e:
|
|
_logger.exception('Error displaying rating form')
|
|
return self._render_error_page(
|
|
'System Error',
|
|
'An unexpected error occurred. Please try again later.'
|
|
)
|
|
|
|
@http.route('/rating/<string:token>/submit',
|
|
type='http', auth='public', website=True, methods=['POST'], csrf=True)
|
|
def submit_rating_form(self, token, rating_value, feedback=None, **kwargs):
|
|
"""
|
|
Handle rating submission from the web form
|
|
|
|
Args:
|
|
token: Unique rating token
|
|
rating_value: Star rating (1-5)
|
|
feedback: Optional feedback text
|
|
**kwargs: Additional parameters
|
|
|
|
Returns:
|
|
Rendered thank you page or error page
|
|
"""
|
|
try:
|
|
# Convert rating_value to int
|
|
try:
|
|
rating_value = int(rating_value)
|
|
except (ValueError, TypeError):
|
|
_logger.warning('Invalid rating value format: %s', rating_value)
|
|
return self._render_error_page(
|
|
'Invalid Rating',
|
|
'Invalid rating value. Please try again.'
|
|
)
|
|
|
|
# Validate rating value range
|
|
if rating_value < 1 or rating_value > 5:
|
|
_logger.warning(
|
|
'Invalid rating value received: %s for token: %s',
|
|
rating_value, token
|
|
)
|
|
return self._render_error_page(
|
|
'Invalid Rating',
|
|
'Rating must be between 1 and 5 stars. Please try again.'
|
|
)
|
|
|
|
# Find the rating record by token
|
|
rating = request.env['rating.rating'].sudo().search([
|
|
('access_token', '=', token)
|
|
], limit=1)
|
|
|
|
if not rating:
|
|
_logger.warning('Rating not found for token: %s', token)
|
|
return self._render_error_page(
|
|
'Invalid Link',
|
|
'This rating link is invalid or has expired. '
|
|
'Please contact support if you need assistance.'
|
|
)
|
|
|
|
# Detect duplicate rating attempt (Requirement 7.2)
|
|
is_update = rating.consumed and rating.rating > 0
|
|
|
|
# Update the rating value and feedback
|
|
try:
|
|
write_vals = {
|
|
'rating': float(rating_value),
|
|
'consumed': True,
|
|
}
|
|
if feedback:
|
|
write_vals['feedback'] = feedback
|
|
|
|
rating.write(write_vals)
|
|
|
|
if is_update:
|
|
_logger.info(
|
|
'Rating updated (duplicate): token=%s, old_value=%s, new_value=%s, resource=%s',
|
|
token, rating.rating, rating_value, rating.res_model
|
|
)
|
|
else:
|
|
_logger.info(
|
|
'Rating created: token=%s, value=%s, resource=%s',
|
|
token, rating_value, rating.res_model
|
|
)
|
|
except ValidationError as e:
|
|
_logger.error(
|
|
'Validation error while saving rating: %s',
|
|
str(e)
|
|
)
|
|
return self._render_error_page(
|
|
'Validation Error',
|
|
str(e)
|
|
)
|
|
|
|
# Redirect to confirmation page with update flag
|
|
return self._render_confirmation_page(rating, rating_value, is_update=is_update)
|
|
|
|
except Exception as e:
|
|
_logger.exception('Unexpected error during rating submission')
|
|
return self._render_error_page(
|
|
'System Error',
|
|
'An unexpected error occurred. Please try again later.'
|
|
)
|
|
|
|
@http.route('/rating/<string:token>/<int:rating_value>',
|
|
type='http', auth='public', website=True, methods=['GET', 'POST'])
|
|
def submit_rating(self, token, rating_value, **kwargs):
|
|
"""
|
|
Handle rating submission from email links or web form
|
|
|
|
Args:
|
|
token: Unique rating token
|
|
rating_value: Star rating (1-5)
|
|
**kwargs: Additional parameters
|
|
|
|
Returns:
|
|
Rendered thank you page or error page
|
|
"""
|
|
try:
|
|
# Validate rating value range
|
|
if rating_value < 1 or rating_value > 5:
|
|
_logger.warning(
|
|
'Invalid rating value received: %s for token: %s',
|
|
rating_value, token
|
|
)
|
|
return self._render_error_page(
|
|
'Invalid Rating',
|
|
'Rating must be between 1 and 5 stars. Please try again.'
|
|
)
|
|
|
|
# Find the rating record by token
|
|
rating = request.env['rating.rating'].sudo().search([
|
|
('access_token', '=', token)
|
|
], limit=1)
|
|
|
|
if not rating:
|
|
_logger.warning('Rating not found for token: %s', token)
|
|
return self._render_error_page(
|
|
'Invalid Link',
|
|
'This rating link is invalid or has expired. '
|
|
'Please contact support if you need assistance.'
|
|
)
|
|
|
|
# Detect duplicate rating attempt (Requirement 7.2)
|
|
# Check if rating is already consumed and has a value
|
|
is_update = rating.consumed and rating.rating > 0
|
|
|
|
# Update the rating value
|
|
try:
|
|
rating.write({
|
|
'rating': float(rating_value),
|
|
'consumed': True,
|
|
})
|
|
|
|
if is_update:
|
|
_logger.info(
|
|
'Rating updated (duplicate): token=%s, old_value=%s, new_value=%s, resource=%s',
|
|
token, rating.rating, rating_value, rating.res_model
|
|
)
|
|
else:
|
|
_logger.info(
|
|
'Rating created: token=%s, value=%s, resource=%s',
|
|
token, rating_value, rating.res_model
|
|
)
|
|
except ValidationError as e:
|
|
_logger.error(
|
|
'Validation error while saving rating: %s',
|
|
str(e)
|
|
)
|
|
return self._render_error_page(
|
|
'Validation Error',
|
|
str(e)
|
|
)
|
|
|
|
# Redirect to confirmation page with update flag
|
|
return self._render_confirmation_page(rating, rating_value, is_update=is_update)
|
|
|
|
except Exception as e:
|
|
_logger.exception('Unexpected error during rating submission')
|
|
return self._render_error_page(
|
|
'System Error',
|
|
'An unexpected error occurred. Please try again later.'
|
|
)
|
|
|
|
def _render_confirmation_page(self, rating, rating_value, is_update=False):
|
|
"""
|
|
Render the confirmation page after successful rating submission
|
|
|
|
Args:
|
|
rating: The rating record
|
|
rating_value: The submitted rating value
|
|
is_update: Whether this is an update to an existing rating
|
|
|
|
Returns:
|
|
Rendered confirmation page
|
|
"""
|
|
# Generate star HTML for display
|
|
filled_star = '★'
|
|
empty_star = '☆'
|
|
stars_html = (filled_star * rating_value) + (empty_star * (5 - rating_value))
|
|
|
|
values = {
|
|
'rating': rating,
|
|
'rating_value': rating_value,
|
|
'stars_html': stars_html,
|
|
'is_update': is_update,
|
|
'page_title': 'Thank You for Your Feedback',
|
|
}
|
|
|
|
return request.render(
|
|
'helpdesk_rating_five_stars.rating_confirmation_page',
|
|
values
|
|
)
|
|
|
|
def _render_error_page(self, error_title, error_message):
|
|
"""
|
|
Render an error page with the given title and message
|
|
|
|
Args:
|
|
error_title: Title of the error
|
|
error_message: Detailed error message
|
|
|
|
Returns:
|
|
Rendered error page
|
|
"""
|
|
values = {
|
|
'error_title': error_title,
|
|
'error_message': error_message,
|
|
'page_title': 'Rating Error',
|
|
}
|
|
|
|
return request.render(
|
|
'helpdesk_rating_five_stars.rating_error_page',
|
|
values
|
|
)
|