function add_review_comment
Adds a comment to a document review cycle with support for threaded comments, different comment types, and location-based annotations.
/tf/active/vicechatdev/CDocs/controllers/review_controller.py
465 - 653
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
logginguuidostypingdatetimetracebackCDocs.dbCDocs.config.settingsCDocs.config.permissionsCDocs.models.documentCDocs.models.reviewCDocs.models.user_extensionsCDocs.utils.audit_trailCDocs.utils.notificationsCDocs.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
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
function add_approval_comment 85.0% similar
-
function update_review_comment 80.7% similar
-
function add_approval_comment_v1 80.3% similar
-
function add_reviewer_to_active_review 74.3% similar
-
function update_approval_comment 72.1% similar