🔍 Code Extractor

function add_review_comment

Maturity: 82

Adds a comment to a document review cycle with support for threaded comments, different comment types, and location-based annotations.

File:
/tf/active/vicechatdev/CDocs/controllers/review_controller.py
Lines:
465 - 653
Complexity:
complex

Purpose

This function enables reviewers to add comments during a document review process. It handles permission checks, validates comment data, creates the comment record, updates review cycle status, sends notifications to relevant parties (parent comment authors or document owners), and logs audit events. It supports threaded comments, page-specific annotations, and different comment types (GENERAL, QUESTION, ACTION_REQUIRED, etc.).

Source Code

def add_review_comment(
    user: DocUser,
    review_uid: str,
    comment_text: str,
    comment_type: str = "GENERAL",
    page_number: Optional[int] = None,
    location_info: Optional[Dict[str, Any]] = None,
    parent_comment_uid: Optional[str] = None
) -> Dict[str, Any]:
    """
    Add a comment to a review cycle.
    
    Args:
        user: User adding the comment
        review_uid: UID of review cycle
        comment_text: Text of the comment
        comment_type: Type of comment (GENERAL, QUESTION, SUGGESTION, etc.)
        page_number: Optional page number for document comments
        location_info: Optional location information for document comments
        parent_comment_uid: Optional parent comment UID for threaded comments
        
    Returns:
        Dictionary with created comment details
        
    Raises:
        ResourceNotFoundError: If review cycle not found
        ValidationError: If validation fails
        PermissionError: If user doesn't have permission
        BusinessRuleError: If a business rule is violated
    """
    try:
        # Direct permission check at the beginning
        logger.info(f"Checking if user {user.uid} has ADD_REVIEW_COMMENT permission")
        if not permissions.user_has_permission(user, ["ADD_REVIEW_COMMENT"]):
            logger.warning(f"Permission denied: User {user.uid} attempted to add a comment without ADD_REVIEW_COMMENT permission")
            raise PermissionError("You do not have permission to addd comments")
            
        logger.info(f"User {user.uid} has permission to create and initiate review cycle")
        # Get review cycle instance
        review_cycle = ReviewCycle(uid=review_uid)
        if not review_cycle:
            raise ResourceNotFoundError(f"Review cycle not found: {review_uid}")
            
        # Check if review status allows comments
        if review_cycle.status not in ["PENDING", "IN_PROGRESS"]:
            raise BusinessRuleError(f"Cannot add comments to review with status {review_cycle.status}")
            
        # Check if this user is a reviewer for this cycle
        if not review_cycle.is_reviewer(user.uid) and not permissions.user_has_permission(user, "MANAGE_REVIEWS"):
            raise PermissionError("User is not authorized to add comments to this review")
            
        # Validate comment text
        if not comment_text or len(comment_text.strip()) == 0:
            raise ValidationError("Comment text cannot be empty")
            
        # Validate comment type
        if not comment_type in settings.REVIEW_COMMENT_TYPES:
            raise ValidationError(f"Invalid comment type: {comment_type}")
            
        # Check parent comment if provided
        parent_comment = None
        if parent_comment_uid:
            parent_comment = ReviewComment(uid=parent_comment_uid)
            if not parent_comment or parent_comment.review_cycle_uid != review_uid:
                raise ResourceNotFoundError(f"Parent comment not found: {parent_comment_uid}")
                
        # Create comment instance
         # Use the proper create method for ReviewComment instead of direct instantiation
        comment = ReviewComment.create(
            review_cycle_uid=review_uid,
            commenter=user,
            text=comment_text,
            requires_resolution=comment_type in ["QUESTION", "ACTION_REQUIRED"],
            properties={
                "comment_type": comment_type,
                "page_number": page_number,
                "location_info": location_info,
                "parent_comment_uid": parent_comment_uid,
                "status": "OPEN",
                "user_name": user.name,  # Include for backward compatibility
                "user_uid": user.uid      # Include for backward compatibility
            }
        )
        
        if not comment:
            raise BusinessRuleError("Failed to create review comment")
        
        # If review cycle is in PENDING status, update to IN_PROGRESS
        if review_cycle.status == "PENDING":
            review_cycle.status = "IN_PROGRESS"
            review_cycle.save()
            
        # Update reviewer assignment status if this is the first activity
        assignment = review_cycle.get_reviewer_assignment(user.uid)
        if assignment and assignment.status in ["PENDING", "ACTIVE"] and not assignment.first_activity_date:
            assignment.first_activity_date = datetime.now()
            assignment.status = "ACTIVE"
            assignment.save()
            
        # Log comment event
        audit_trail.log_review_event(
            event_type="REVIEW_COMMENT_ADDED",
            user=user,
            review_uid=review_uid,
            details={
                "comment_uid": comment.uid,
                "comment_type": comment_type,
                "page_number": page_number,
                "parent_comment_uid": parent_comment_uid
            }
        )
        
        # Notify about comment if it's a reply or directed to document owner
        if parent_comment_uid or comment_type in ["QUESTION", "ACTION_REQUIRED"]:
            # Get document and owner
            document = ControlledDocument(uid=review_cycle.document_uid)
            
            # If it's a reply, notify the parent comment author
            if parent_comment and parent_comment.user_uid != user.uid:
               notifications.send_notification(
                notification_type="REVIEW_COMMENT_ADDED",
                users=[parent_comment.user_uid],
                resource_uid=review_cycle.uid,
                resource_type="ReviewCycle",
                message=f"New reply to your comment on {document.doc_number} - {document.title}",
                details={
                    "doc_uid": document.uid,  # Add doc_uid for URL generation
                    "doc_number": document.doc_number,
                    "title": document.title,
                    "review_uid": review_uid,
                    "comment_uid": comment.uid,
                    "comment_text": comment_text[:100] + "..." if len(comment_text) > 100 else comment_text,
                    "commenter_name": user.name,
                    "department": document.department,
                    "doc_type": document.doc_type,
                    "version_number": document.current_version.version_number if document.current_version else "N/A"
                },
                send_email=True,
                email_template="review_comment_reply",
                email_data={
                    "app_url": settings.APP_URL,
                    "doc_uid": document.uid,
                    "review_uid": review_uid,
                    "comment_uid": comment.uid
                }
            )
                
            # If it's a question or action required, notify document owner
            elif comment_type in ["QUESTION", "ACTION_REQUIRED"] and document and document.owner_uid != user.uid:
                notifications.send_notification(
                    notification_type="REVIEW_COMMENT_ADDED",
                    users=[document.owner_uid],
                    resource_uid=review_cycle.uid,
                    resource_type="ReviewCycle",
                    message=f"Action required on document {document.doc_number} - {document.title}",
                    details={
                    "doc_uid": document.uid,  # Add doc_uid for URL generation
                    "doc_number": document.doc_number,
                    "title": document.title,
                    "review_uid": review_uid,
                    "comment_uid": comment.uid,
                    "comment_text": comment_text[:100] + "..." if len(comment_text) > 100 else comment_text,
                    "commenter_name": user.name,
                    "department": document.department,
                    "doc_type": document.doc_type,
                    "version_number": document.current_version.version_number if document.current_version else "N/A"
                    },
                    send_email=True,
                    email_template="review_action_required",
                    email_data={
                        "app_url": settings.APP_URL,
                        "doc_uid": document.uid,
                        "review_uid": review_uid,
                        "comment_uid": comment.uid
                    }
                )
        
        return {
            "success": True,
            "comment": comment.to_dict(),
            "message": "Comment added successfully"
        }
        
    except (ResourceNotFoundError, ValidationError, PermissionError, BusinessRuleError) as e:
        # Re-raise known errors
        raise
    except Exception as e:
        logger.error(f"Error adding review comment: {e}")
        raise BusinessRuleError(f"Failed to add review comment: {e}")

Parameters

Name Type Default Kind
user DocUser - positional_or_keyword
review_uid str - positional_or_keyword
comment_text str - positional_or_keyword
comment_type str 'GENERAL' positional_or_keyword
page_number Optional[int] None positional_or_keyword
location_info Optional[Dict[str, Any]] None positional_or_keyword
parent_comment_uid Optional[str] None positional_or_keyword

Parameter Details

user: DocUser object representing the user adding the comment. Must have ADD_REVIEW_COMMENT permission and be assigned as a reviewer for the review cycle.

review_uid: String UID (unique identifier) of the review cycle to which the comment is being added. Must reference an existing ReviewCycle in PENDING or IN_PROGRESS status.

comment_text: String containing the text content of the comment. Cannot be empty or whitespace-only. Will be truncated to 100 characters in notification messages.

comment_type: String indicating the type of comment. Must be one of the values in settings.REVIEW_COMMENT_TYPES (e.g., 'GENERAL', 'QUESTION', 'SUGGESTION', 'ACTION_REQUIRED'). Defaults to 'GENERAL'. QUESTION and ACTION_REQUIRED types trigger notifications and set requires_resolution flag.

page_number: Optional integer specifying the page number in the document where the comment applies. Used for location-based annotations. Can be None for general comments.

location_info: Optional dictionary containing additional location information for document comments (e.g., coordinates, highlighted text, annotations). Structure depends on document viewer implementation.

parent_comment_uid: Optional string UID of a parent comment for creating threaded/nested comments. If provided, must reference an existing ReviewComment in the same review cycle. Triggers notification to parent comment author.

Return Value

Type: Dict[str, Any]

Returns a dictionary with three keys: 'success' (boolean, always True on successful execution), 'comment' (dictionary representation of the created ReviewComment object including uid, text, timestamps, user info, and properties), and 'message' (string confirmation message 'Comment added successfully'). On error, raises one of the documented exceptions instead of returning.

Dependencies

  • logging
  • uuid
  • os
  • typing
  • datetime
  • traceback
  • CDocs.db
  • CDocs.config.settings
  • CDocs.config.permissions
  • CDocs.models.document
  • CDocs.models.review
  • CDocs.models.user_extensions
  • CDocs.utils.audit_trail
  • CDocs.utils.notifications
  • CDocs.controllers

Required Imports

from typing import Dict, Any, Optional
from datetime import datetime
from CDocs.models.user_extensions import DocUser
from CDocs.models.review import ReviewCycle, ReviewComment
from CDocs.models.document import ControlledDocument
from CDocs.config import settings, permissions
from CDocs.utils import audit_trail, notifications
from CDocs.controllers import ResourceNotFoundError, ValidationError, PermissionError, BusinessRuleError
import logging

Usage Example

from CDocs.models.user_extensions import DocUser
from CDocs.controllers.review_controller import add_review_comment

# Get the user and review cycle
user = DocUser(uid='user123')
review_uid = 'review456'

# Add a general comment
result = add_review_comment(
    user=user,
    review_uid=review_uid,
    comment_text='This section needs clarification on the process flow.',
    comment_type='GENERAL',
    page_number=5
)

print(f"Comment added: {result['comment']['uid']}")

# Add a question that requires resolution
question_result = add_review_comment(
    user=user,
    review_uid=review_uid,
    comment_text='What is the approval timeline for this procedure?',
    comment_type='QUESTION',
    page_number=3,
    location_info={'x': 100, 'y': 200, 'width': 300, 'height': 50}
)

# Add a threaded reply to an existing comment
reply_result = add_review_comment(
    user=user,
    review_uid=review_uid,
    comment_text='I agree with this suggestion.',
    comment_type='GENERAL',
    parent_comment_uid=question_result['comment']['uid']
)

Best Practices

  • Always ensure the user has ADD_REVIEW_COMMENT permission before calling this function
  • Verify the review cycle is in PENDING or IN_PROGRESS status before attempting to add comments
  • Use appropriate comment_type values to trigger correct notification behavior (QUESTION and ACTION_REQUIRED notify document owner)
  • When creating threaded comments, ensure parent_comment_uid references a comment in the same review cycle
  • Handle all four exception types (ResourceNotFoundError, ValidationError, PermissionError, BusinessRuleError) in calling code
  • The function automatically transitions review status from PENDING to IN_PROGRESS on first comment
  • Comment text is truncated to 100 characters in notifications but stored in full in the database
  • The function updates reviewer assignment status to ACTIVE on first activity
  • Use location_info and page_number for document-specific annotations to enable precise feedback
  • The function is decorated with @log_controller_action for automatic logging

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function add_approval_comment 85.0% similar

    Adds a comment to an approval cycle for a controlled document, with support for threaded comments, different comment types, and automatic notifications to relevant stakeholders.

    From: /tf/active/vicechatdev/CDocs/controllers/approval_controller.py
  • function update_review_comment 80.7% similar

    Updates an existing review comment's text and/or status with permission checks, audit logging, and notifications for resolved comments.

    From: /tf/active/vicechatdev/CDocs/controllers/review_controller.py
  • function add_approval_comment_v1 80.3% similar

    Adds a comment to an approval cycle with optional location information, page references, and reply threading. Validates user permissions, logs audit trails, and sends notifications to other approvers for issue-type comments.

    From: /tf/active/vicechatdev/CDocs/controllers/approval_controller_bis.py
  • function add_reviewer_to_active_review 74.3% similar

    Adds a reviewer to an active review cycle with optional sequence ordering and instructions, handling permissions, notifications, and audit logging.

    From: /tf/active/vicechatdev/CDocs/controllers/review_controller.py
  • function update_approval_comment 72.1% similar

    Updates an approval comment's text and/or status with permission checks, audit logging, and notifications for resolved comments.

    From: /tf/active/vicechatdev/CDocs/controllers/approval_controller.py
← Back to Browse