function update_approval_comment
Updates an approval comment's text and/or status with permission checks, audit logging, and notifications for resolved comments.
/tf/active/vicechatdev/CDocs/controllers/approval_controller.py
634 - 789
complex
Purpose
This function manages the modification of approval comments within an active approval cycle. It enforces business rules such as only allowing updates during active approval cycles, restricting text changes to comment authors, and allowing document owners to change comment status. When comments are resolved, it logs the resolution details and notifies relevant users. The function is part of a document approval workflow system.
Source Code
def update_approval_comment(
user: DocUser,
comment_uid: str,
comment_text: Optional[str] = None,
status: Optional[str] = None
) -> Dict[str, Any]:
"""
Update a approval comment.
Args:
user: User updating the comment
comment_uid: UID of comment to update
comment_text: Optional new comment text
status: Optional new status (OPEN, RESOLVED, etc.)
Returns:
Dictionary with updated comment details
Raises:
ResourceNotFoundError: If comment 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 EDIT_APPROVE_COMMENT permission")
if not permissions.user_has_permission(user, ["EDIT_APPROVE_COMMENT"]):
logger.warning(f"Permission denied: User {user.uid} attempted to edit a comment without EDIT_APPROVE_COMMENT permission")
raise PermissionError("You do not have permission to edit comments")
logger.info(f"User {user.uid} has permission to create and initiate approval cycle")
# Get comment instance
comment = ApprovalComment(uid=comment_uid)
if not comment:
raise ResourceNotFoundError(f"Comment not found: {comment_uid}")
# Get approval cycle
approval_cycle = ApprovalCycle(uid=comment.approval_cycle_uid)
if not approval_cycle:
raise ResourceNotFoundError(f"Approval cycle not found for comment: {comment_uid}")
# Check if approval is still active
if approval_cycle.status not in ["PENDING", "IN_PROGRESS"]:
raise BusinessRuleError(f"Cannot update comments for approval with status {approval_cycle.status}")
# Check if user can edit this comment
can_edit = False
# User can edit their own comments
if comment.user_uid == user.uid:
can_edit = True
# Document owner can change status (but not text)
elif approval_cycle.document_uid:
document = ControlledDocument(uid=approval_cycle.document_uid)
if document and document.owner_uid == user.uid and comment_text is None:
can_edit = True
# Admins can edit any comment
elif permissions.user_has_permission(user, "MANAGE_APPROVALS"):
can_edit = True
if not can_edit:
raise PermissionError("User is not authorized to update this comment")
# Track changes
changes = {}
# Update comment text if provided
if comment_text is not None and comment_text != comment.comment_text:
# Only the author can change the text
if comment.user_uid != user.uid and not permissions.user_has_permission(user, "MANAGE_APPROVALS"):
raise PermissionError("Only the comment author can modify comment text")
old_text = comment.comment_text
comment.comment_text = comment_text
changes["comment_text"] = {"old": old_text, "new": comment_text}
# Update status if provided
if status is not None and status != comment.status:
# Validate status
if status not in settings.APPROVE_COMMENT_STATUSES:
raise ValidationError(f"Invalid comment status: {status}")
old_status = comment.status
comment.status = status
# Add resolution info if being resolved
if status == "RESOLVED" and old_status != "RESOLVED":
comment.resolved_by_uid = user.uid
comment.resolved_by_name = user.name
comment.resolved_date = datetime.now()
changes["status"] = {"old": old_status, "new": status}
# No changes
if not changes:
return {
"success": True,
"comment": comment.to_dict(),
"message": "No changes made to comment"
}
# Update modified timestamp
comment.modified_date = datetime.now()
# Save comment to database
comment.save()
# Log comment update event
audit_trail.log_approval_event(
event_type="APPROVE_COMMENT_UPDATED",
user=user,
approval_uid=approval_cycle.uid,
details={
"comment_uid": comment.uid,
"changes": changes
}
)
# Send notifications if status changed
if "status" in changes and changes["status"]["new"] == "RESOLVED":
# Notify original comment author if different from resolver
if comment.user_uid != user.uid:
document = ControlledDocument(uid=approval_cycle.document_uid)
notifications.send_notification(
notification_type="APPROVE_COMMENT_RESOLVED",
users=[comment.user_uid],
resource_uid=approval_cycle.uid,
resource_type="ApprovalCycle",
message=f"Your comment on {document.doc_number if document else 'document'} has been resolved",
details={
"approval_uid": approval_cycle.uid,
"comment_uid": comment.uid,
"resolved_by": user.name,
"doc_number": document.doc_number if document else "",
"title": document.title if document else ""
},
send_email=True,
email_template="approval_comment_resolved"
)
return {
"success": True,
"comment": comment.to_dict(),
"changes": changes,
"message": "Comment updated successfully"
}
except (ResourceNotFoundError, ValidationError, PermissionError, BusinessRuleError) as e:
# Re-raise known errors
raise
except Exception as e:
logger.error(f"Error updating approval comment: {e}")
raise BusinessRuleError(f"Failed to update approval comment: {e}")
Parameters
| Name | Type | Default | Kind |
|---|---|---|---|
user |
DocUser | - | positional_or_keyword |
comment_uid |
str | - | positional_or_keyword |
comment_text |
Optional[str] | None | positional_or_keyword |
status |
Optional[str] | None | positional_or_keyword |
Parameter Details
user: DocUser object representing the user attempting to update the comment. Must have EDIT_APPROVE_COMMENT permission. Used for permission checks and audit logging.
comment_uid: String UID (unique identifier) of the approval comment to update. Must correspond to an existing ApprovalComment record in the database.
comment_text: Optional string containing new text for the comment. Can only be modified by the original comment author or users with MANAGE_APPROVALS permission. Pass None to leave unchanged.
status: Optional string representing the new status for the comment. Must be one of the values defined in settings.APPROVE_COMMENT_STATUSES (e.g., 'OPEN', 'RESOLVED'). Pass None to leave unchanged.
Return Value
Type: Dict[str, Any]
Returns a dictionary with keys: 'success' (boolean indicating operation success), 'comment' (dictionary representation of the updated ApprovalComment object), 'changes' (dictionary tracking old and new values for modified fields), and 'message' (string describing the outcome). If no changes were made, the changes key may be absent and message indicates no modifications.
Dependencies
logginguuidostypingdatetimetracebackCDocs.dbCDocs.config.settingsCDocs.config.permissionsCDocs.models.documentCDocs.models.approvalCDocs.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.approval import ApprovalComment, ApprovalCycle
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.approval_controller import update_approval_comment
# Get the user performing the update
user = DocUser(uid='user123')
# Update comment text only
result = update_approval_comment(
user=user,
comment_uid='comment456',
comment_text='Updated comment with additional details'
)
# Update comment status to resolved
result = update_approval_comment(
user=user,
comment_uid='comment456',
status='RESOLVED'
)
# Update both text and status
result = update_approval_comment(
user=user,
comment_uid='comment456',
comment_text='Final comment text',
status='RESOLVED'
)
if result['success']:
print(f"Comment updated: {result['message']}")
print(f"Changes made: {result.get('changes', {})}")
updated_comment = result['comment']
Best Practices
- Always ensure the user has EDIT_APPROVE_COMMENT permission before calling this function
- Only comment authors can modify comment text; document owners can only change status
- Comments can only be updated when the approval cycle status is PENDING or IN_PROGRESS
- When resolving a comment, the function automatically records resolver information and timestamp
- The function sends email notifications to comment authors when their comments are resolved by others
- All changes are tracked in the audit trail with detailed change information
- Handle all four exception types: ResourceNotFoundError, ValidationError, PermissionError, and BusinessRuleError
- Pass None for parameters you don't want to change rather than omitting them
- The function is decorated with @log_controller_action for automatic action logging
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
function update_approval_comment_v1 92.7% similar
-
function update_review_comment 91.3% similar
-
function add_approval_comment 81.3% similar
-
function add_approval_comment_v1 76.6% similar
-
function add_review_comment 72.1% similar