function cancel_review_cycle
Cancels an active review cycle for a controlled document, updating the review status, notifying reviewers, and reverting the document status if necessary.
/tf/active/vicechatdev/CDocs/controllers/review_controller.py
1187 - 1344
complex
Purpose
This function manages the cancellation workflow for document review cycles in a controlled document management system. It validates user permissions, checks if the review can be canceled based on its current status, updates all related entities (review cycle, reviewer assignments, document status), logs audit events, and sends notifications to affected reviewers. It ensures business rules are enforced (only PENDING or IN_PROGRESS reviews can be canceled) and maintains data integrity across the document lifecycle.
Source Code
def cancel_review_cycle(
user: DocUser,
review_uid: str,
reason: str
) -> Dict[str, Any]:
"""
Cancel an active review cycle.
Args:
user: User canceling the review
review_uid: UID of review cycle to cancel
reason: Reason for cancellation
Returns:
Dictionary with cancellation details
Raises:
ResourceNotFoundError: If review cycle not found
ValidationError: If validation fails
PermissionError: If user doesn't have permission
BusinessRuleError: If cancellation is not allowed
"""
try:
# Direct permission check at the beginning
logger.info(f"Checking if user {user.uid} has MANAGE_REVIEWS permission")
if not permissions.user_has_permission(user, "MANAGE_REVIEWS"):
logger.warning(f"Permission denied: User {user.uid} attempted to manage reviews without MANAGE_REVIEWS permission")
raise PermissionError("You do not have permission to manage reviews")
logger.info(f"User {user.uid} has permission to manage reviews {review_uid}")
# 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 can be canceled
if review_cycle.status not in ["PENDING", "IN_PROGRESS"]:
raise BusinessRuleError(f"Cannot cancel review with status {review_cycle.status}")
# Check if user has permission
if not permissions.user_has_permission(user, "MANAGE_REVIEWS"):
# Check if user is the document owner or review initiator
document = ControlledDocument(uid=review_cycle.document_uid)
if not (document and document.owner_uid == user.uid) and review_cycle.initiated_by_uid != user.uid:
raise PermissionError("User is not authorized to cancel this review cycle")
# Validate reason
if not reason or len(reason.strip()) == 0:
raise ValidationError("Cancellation reason is required")
# Update review cycle
review_cycle.status = "CANCELED"
review_cycle.cancellation_date = datetime.now()
review_cycle.cancellation_reason = reason
review_cycle.save()
# Update all active reviewer assignments to canceled
assignments = review_cycle.get_reviewer_assignments()
for assignment in assignments:
if assignment.status in ["PENDING", "ACTIVE"]:
assignment.status = "CANCELED"
assignment.save()
# Check if document status should be updated
document = ControlledDocument(uid=review_cycle.document_uid)
if document and document.status == "IN_REVIEW":
# Update the document status back to DRAFT
document.status = "DRAFT"
document.save()
# Add document status update to the event details for audit trail
audit_trail.log_event(
event_type="DOCUMENT_STATUS_CHANGED",
user=user,
resource_uid=document.uid,
resource_type="ControlledDocument",
details={
"old_status": "IN_REVIEW",
"new_status": "DRAFT",
"reason": f"Review cycle {review_uid} was canceled"
}
)
# Log cancellation event
audit_trail.log_review_event(
event_type="REVIEW_CYCLE_CANCELED",
user=user,
review_uid=review_uid,
details={
"reason": reason
}
)
# Get document
document = ControlledDocument(uid=review_cycle.document_uid)
# Notify reviewers
for reviewer_uid in review_cycle.reviewer_uids:
notifications.send_notification(
notification_type="REVIEW_CANCELED",
users=[reviewer_uid],
resource_uid=review_cycle.uid,
resource_type="ReviewCycle",
message=f"Review for {document.doc_number if document else 'document'} has been canceled",
details={
"review_uid": review_uid,
"document_uid": document.uid if document else None,
"doc_number": document.doc_number if document else None,
"title": document.title if document else None,
"reason": reason
},
send_email=True,
email_template="review_canceled",
email_data={
"app_url": settings.APP_URL,
"doc_uid": document.uid if document else None,
"review_uid": review_uid,
"reason": reason,
"canceled_by": user.name
}
)
# Check if document status should be updated
if document and document.status == "IN_REVIEW":
# Check if there are any other active reviews
other_active_reviews = ReviewCycle.get_reviews_for_document(
document_uid=document.uid,
status_filter=["PENDING", "IN_PROGRESS"]
)
# If no other active reviews, update document status back to DRAFT
if not other_active_reviews:
from CDocs.controllers.document_controller import update_document
update_document(
user=user,
document_uid=document.uid,
status="DRAFT"
)
# Update sharing permissions for document based on new review cycle
from CDocs.controllers.share_controller import manage_document_permissions
document = ControlledDocument(uid=document.uid)
permission_result = manage_document_permissions(document)
return {
"success": True,
"review_uid": review_uid,
"reason": reason,
"message": "Review cycle canceled successfully"
}
except (ResourceNotFoundError, ValidationError, PermissionError, BusinessRuleError) as e:
# Re-raise known errors
raise
except Exception as e:
logger.error(f"Error canceling review cycle: {e}")
raise BusinessRuleError(f"Failed to cancel review cycle: {e}")
Parameters
| Name | Type | Default | Kind |
|---|---|---|---|
user |
DocUser | - | positional_or_keyword |
review_uid |
str | - | positional_or_keyword |
reason |
str | - | positional_or_keyword |
Parameter Details
user: DocUser object representing the user attempting to cancel the review. Must have MANAGE_REVIEWS permission, or be the document owner or review initiator. Used for permission checks, audit logging, and notification attribution.
review_uid: String containing the unique identifier (UID) of the review cycle to be canceled. Must correspond to an existing ReviewCycle record in the database.
reason: String explaining why the review is being canceled. Required field that cannot be empty or whitespace-only. Used for audit trail and included in notifications to reviewers.
Return Value
Type: Dict[str, Any]
Returns a dictionary with keys: 'success' (boolean, always True on successful execution), 'review_uid' (string, the UID of the canceled review), 'reason' (string, the cancellation reason provided), and 'message' (string, confirmation message 'Review cycle canceled successfully'). On error, raises one of the documented exceptions instead of returning.
Dependencies
logginguuidostypingdatetimetracebackCDocsCDocs.configCDocs.models.documentCDocs.models.reviewCDocs.models.user_extensionsCDocs.utilsCDocs.controllersCDocs.controllers.document_controllerCDocs.controllers.share_controllerCDocs.db
Required Imports
from typing import Dict, Any
from datetime import datetime
from CDocs.models.user_extensions import DocUser
from CDocs.models.review import ReviewCycle, ReviewerAssignment
from CDocs.models.document import ControlledDocument
from CDocs.config import permissions, settings
from CDocs.utils import audit_trail, notifications
from CDocs.controllers import PermissionError, ResourceNotFoundError, ValidationError, BusinessRuleError, log_controller_action
from CDocs.controllers.document_controller import update_document
from CDocs.controllers.share_controller import manage_document_permissions
import logging
Conditional/Optional Imports
These imports are only needed under specific conditions:
from CDocs.controllers.document_controller import update_document
Condition: only when document status needs to be reverted to DRAFT after canceling the last active review
Required (conditional)from CDocs.controllers.share_controller import manage_document_permissions
Condition: called at the end to update document sharing permissions based on the new review state
Required (conditional)Usage Example
from CDocs.models.user_extensions import DocUser
from CDocs.controllers.review_controller import cancel_review_cycle
from CDocs.controllers import PermissionError, ResourceNotFoundError, ValidationError, BusinessRuleError
# Assume user and review_uid are already obtained
user = DocUser(uid='user-123')
review_uid = 'review-456'
cancellation_reason = 'Document requirements changed, review no longer needed'
try:
result = cancel_review_cycle(
user=user,
review_uid=review_uid,
reason=cancellation_reason
)
print(f"Success: {result['message']}")
print(f"Canceled review: {result['review_uid']}")
except PermissionError as e:
print(f"Permission denied: {e}")
except ResourceNotFoundError as e:
print(f"Review not found: {e}")
except ValidationError as e:
print(f"Invalid input: {e}")
except BusinessRuleError as e:
print(f"Cannot cancel: {e}")
Best Practices
- Always provide a meaningful cancellation reason as it's logged in the audit trail and sent to reviewers
- Ensure the user has MANAGE_REVIEWS permission or is the document owner/review initiator before calling
- Only reviews with status PENDING or IN_PROGRESS can be canceled; check status before attempting cancellation
- Handle all four exception types (ResourceNotFoundError, ValidationError, PermissionError, BusinessRuleError) in calling code
- The function automatically handles document status reversion, reviewer notifications, and permission updates
- Be aware that canceling a review may trigger document status changes from IN_REVIEW back to DRAFT
- The function is decorated with @log_controller_action('cancel_review_cycle') for automatic action logging
- All reviewer assignments associated with the review are automatically set to CANCELED status
- Email notifications are sent to all reviewers with details about the cancellation
- The function checks for other active reviews before reverting document status to avoid premature status changes
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
function cancel_approval_cycle 85.9% similar
-
function close_review_cycle 79.7% similar
-
function cancel_approval_cycle_v1 79.5% similar
-
function remove_reviewer_from_active_review 76.9% similar
-
function complete_review 73.3% similar