# -*- 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/', 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//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//', 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 )