🔍 Code Extractor

function remove_reviewer_from_active_review

Maturity: 72

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.

File:
/tf/active/vicechatdev/CDocs/controllers/review_controller.py
Lines:
1616 - 1771
Complexity:
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

  • logging
  • uuid
  • os
  • typing
  • datetime
  • traceback
  • CDocs
  • CDocs.config
  • CDocs.models.document
  • CDocs.models.review
  • CDocs.models.user_extensions
  • CDocs.utils
  • CDocs.controllers
  • CDocs.controllers.document_controller
  • CDocs.controllers.share_controller
  • CDocs.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

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function remove_approver_from_active_approval 78.8% similar

    Removes an approver from an active approval cycle with permission checks, validation, and notification handling.

    From: /tf/active/vicechatdev/CDocs/controllers/approval_controller.py
  • function add_reviewer_to_active_review 78.1% 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 cancel_review_cycle 76.9% similar

    Cancels an active review cycle for a controlled document, updating the review status, notifying reviewers, and reverting the document status if necessary.

    From: /tf/active/vicechatdev/CDocs/controllers/review_controller.py
  • function remove_approver_from_active_approval_v1 73.6% similar

    Removes an approver from an active approval cycle with permission checks, audit logging, and notifications.

    From: /tf/active/vicechatdev/CDocs/controllers/approval_controller_bis.py
  • function complete_review 73.0% similar

    Completes a document review cycle by submitting a reviewer's decision (APPROVED/REJECTED), updating review status, managing sequential review workflows, and triggering notifications.

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