function remove_reviewer_from_active_review
Removes a reviewer from an active review cycle, updating assignment status, adjusting sequential review orders if needed, logging the event, and notifying the removed reviewer.
/tf/active/vicechatdev/CDocs/controllers/review_controller.py
1616 - 1771
complex
Purpose
This function manages the removal of a reviewer from an active document review cycle. It enforces business rules (only active reviews can be modified, completed reviews cannot be removed), validates permissions (MANAGE_REVIEWS required), updates reviewer assignment status to REMOVED, adjusts sequence orders for sequential reviews, logs audit events, sends notifications to the removed reviewer, and updates document sharing permissions. It is used when a reviewer needs to be removed from a review process due to unavailability, conflict of interest, or other administrative reasons.
Source Code
def remove_reviewer_from_active_review(
user: DocUser,
review_uid: str,
reviewer_uid: str,
reason: str
) -> Dict[str, Any]:
"""
Remove a reviewer from an active review cycle.
Args:
user: User removing the reviewer
review_uid: UID of review cycle
reviewer_uid: UID of reviewer to remove
reason: Reason for removal
Returns:
Dictionary with removal details
Raises:
ResourceNotFoundError: If review cycle or reviewer not found
ValidationError: If validation fails
PermissionError: If user doesn't have permission
BusinessRuleError: If removal 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 is still active
if review_cycle.status not in ["PENDING", "IN_PROGRESS"]:
raise BusinessRuleError(f"Cannot remove reviewers from review with status {review_cycle.status}")
# Check if reviewer exists and is a reviewer for this cycle
if not review_cycle.is_reviewer(reviewer_uid):
raise ResourceNotFoundError(f"Reviewer {reviewer_uid} is not assigned to this review cycle")
# Validate reason
if not reason or len(reason.strip()) == 0:
raise ValidationError("Removal reason is required")
# Get reviewer assignment
assignment = review_cycle.get_reviewer_assignment(reviewer_uid)
if not assignment:
raise ResourceNotFoundError(f"Reviewer assignment not found for {reviewer_uid}")
# Check if review is already completed
if assignment.status == "COMPLETED":
raise BusinessRuleError("Cannot remove a reviewer who has already completed their review")
# Get reviewer name for notification
reviewer_name = assignment.reviewer_name
if not reviewer_name:
reviewer = DocUser(uid=reviewer_uid)
reviewer_name = reviewer.name if reviewer else "Unknown"
# Update assignment status
assignment.status = "REMOVED"
assignment.removal_date = datetime.now()
assignment.removal_reason = reason
assignment.save()
# Use the model method to remove the reviewer relationship
success = review_cycle.remove_reviewer(reviewer_uid)
if not success:
logger.warning(f"Failed to remove relationship for reviewer {reviewer_uid}")
# Continue anyway since we've updated the assignment status
# If sequential review, adjust sequence orders of other reviewers
if review_cycle.sequential and assignment.sequence_order:
# Get all other assignments
all_assignments = review_cycle.get_reviewer_assignments()
# Update sequence for any assignments that come after this one
removed_sequence = assignment.sequence_order
for other_assignment in all_assignments:
if (other_assignment.uid != assignment.uid and
other_assignment.sequence_order and
other_assignment.sequence_order > removed_sequence):
# Decrement sequence order
other_assignment.sequence_order -= 1
other_assignment.save()
# Log removal event
audit_trail.log_review_event(
event_type="REVIEWER_REMOVED",
user=user,
review_uid=review_uid,
details={
"reviewer_uid": reviewer_uid,
"reviewer_name": reviewer_name,
"reason": reason
}
)
# Notify the removed reviewer
document = None
document_version = review_cycle.document_version
if document_version:
document = document_version.document
notifications.send_notification(
notification_type="REVIEWER_REMOVED",
users=[reviewer_uid],
resource_uid=review_uid,
resource_type="ReviewCycle",
message=f"You have been removed as a reviewer",
details={
"review_uid": review_uid,
"document_uid": document.uid if document else None,
"doc_number": document.doc_number if document else "Unknown",
"title": document.title if document else "Unknown Document",
"reason": reason
},
send_email=True,
email_template="reviewer_removed",
email_data={
"app_url": settings.APP_URL,
"doc_uid": document.uid if document else None,
"review_uid": review_uid,
"reason": reason,
"removed_by": user.name
}
)
# 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,
"reviewer_uid": reviewer_uid,
"reviewer_name": reviewer_name,
"reason": reason,
"message": f"Reviewer {reviewer_name} removed successfully"
}
except (ResourceNotFoundError, ValidationError, PermissionError, BusinessRuleError) as e:
# Re-raise known errors
raise
except Exception as e:
logger.error(f"Error removing reviewer from review cycle: {e}")
logger.error(traceback.format_exc())
raise BusinessRuleError(f"Failed to remove reviewer: {e}")
Parameters
| Name | Type | Default | Kind |
|---|---|---|---|
user |
DocUser | - | positional_or_keyword |
review_uid |
str | - | positional_or_keyword |
reviewer_uid |
str | - | positional_or_keyword |
reason |
str | - | positional_or_keyword |
Parameter Details
user: DocUser object representing the user performing the removal action. Must have MANAGE_REVIEWS permission. Used for permission checks and audit logging.
review_uid: String UID (unique identifier) of the review cycle from which the reviewer should be removed. Must correspond to an existing ReviewCycle with status PENDING or IN_PROGRESS.
reviewer_uid: String UID of the reviewer to be removed. Must be currently assigned as a reviewer to the specified review cycle and not have completed their review.
reason: String explaining why the reviewer is being removed. Required field, cannot be empty or whitespace-only. Used for audit trail and notification to the removed reviewer.
Return Value
Type: Dict[str, Any]
Returns a dictionary with keys: 'success' (bool, always True on successful execution), 'review_uid' (str, the review cycle UID), 'reviewer_uid' (str, the removed reviewer's UID), 'reviewer_name' (str, display name of removed reviewer), 'reason' (str, the removal reason provided), and 'message' (str, confirmation message). 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
import logging
import traceback
from CDocs.config import settings, permissions
from CDocs.models.document import ControlledDocument, DocumentVersion
from CDocs.models.review import ReviewCycle, ReviewerAssignment
from CDocs.models.user_extensions import DocUser
from CDocs.utils import audit_trail, notifications
from CDocs.controllers import PermissionError, ResourceNotFoundError, ValidationError, BusinessRuleError
from CDocs.controllers import log_controller_action
Conditional/Optional Imports
These imports are only needed under specific conditions:
from CDocs.controllers.share_controller import manage_document_permissions
Condition: imported inside function to update document permissions after reviewer removal
Required (conditional)Usage Example
from CDocs.models.user_extensions import DocUser
from CDocs.controllers.review_controller import remove_reviewer_from_active_review
# Get the user performing the action (must have MANAGE_REVIEWS permission)
manager_user = DocUser(uid='user_123')
# Remove a reviewer from an active review cycle
try:
result = remove_reviewer_from_active_review(
user=manager_user,
review_uid='review_456',
reviewer_uid='reviewer_789',
reason='Reviewer is on extended leave and unavailable to complete review'
)
print(f"Success: {result['message']}")
print(f"Removed reviewer: {result['reviewer_name']}")
except PermissionError as e:
print(f"Permission denied: {e}")
except ResourceNotFoundError as e:
print(f"Resource not found: {e}")
except ValidationError as e:
print(f"Validation error: {e}")
except BusinessRuleError as e:
print(f"Business rule violation: {e}")
Best Practices
- Always ensure the user has MANAGE_REVIEWS permission before calling this function
- Provide a meaningful reason for removal as it will be logged in audit trail and sent to the removed reviewer
- Only call this function for review cycles with status PENDING or IN_PROGRESS
- Do not attempt to remove reviewers who have already completed their review (status COMPLETED)
- Handle all four exception types (PermissionError, ResourceNotFoundError, ValidationError, BusinessRuleError) appropriately
- Be aware that this function automatically adjusts sequence orders for sequential reviews
- The function sends email notifications to the removed reviewer, ensure email system is configured
- Document permissions are automatically updated after reviewer removal via manage_document_permissions
- The function is decorated with @log_controller_action for automatic logging
- All changes are logged to the audit trail with event type REVIEWER_REMOVED
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
function remove_approver_from_active_approval 78.8% similar
-
function add_reviewer_to_active_review 78.1% similar
-
function cancel_review_cycle 76.9% similar
-
function remove_approver_from_active_approval_v1 73.6% similar
-
function complete_review 73.0% similar