🔍 Code Extractor

function close_review_cycle

Maturity: 71

Closes a completed review cycle for a controlled document, with optional document status update, permission validation, and stakeholder notifications.

File:
/tf/active/vicechatdev/CDocs/controllers/review_controller.py
Lines:
1044 - 1184
Complexity:
complex

Purpose

This function manages the closure of document review cycles in a controlled document management system. It validates user permissions (MANAGE_REVIEWS or EDIT_DOCUMENT), ensures the review cycle is in COMPLETED status, optionally updates the associated document's status with validation of status transitions, logs audit events, sends notifications to document owners, and updates document sharing permissions. It enforces business rules around who can close reviews (document owner, review initiator, or managers) and ensures proper status transitions according to system settings.

Source Code

def close_review_cycle(
    user: DocUser,
    review_uid: str,
    update_document_status: bool = True,
    target_status: str = "DRAFT"
) -> Dict[str, Any]:
    """
    Close a review cycle and optionally update document status.
    
    Args:
        user: User closing the review cycle
        review_uid: UID of review cycle
        update_document_status: Whether to update document status
        target_status: Target document status if updating
        
    Returns:
        Dictionary with operation details
        
    Raises:
        ResourceNotFoundError: If review cycle not found
        ValidationError: If target status is invalid
        PermissionError: If user doesn't have permission
        BusinessRuleError: If operation is not allowed
    """
    try:
        # Direct permission check at the beginning
        logger.info(f"Checking if user {user.uid} has MANAGE_REVIEWS or EDIT_DOCUMENT permission")
        if not permissions.user_has_permission(user, ["MANAGE_REVIEWS", "EDIT_DOCUMENT"]):
            logger.warning(f"Permission denied: User {user.uid} attempted to close review cycle without required permissions")
            raise PermissionError("You do not have permission to close this review cycle")
            
        logger.info(f"User {user.uid} has permission to close review cycle {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 completed
        if review_cycle.status != "COMPLETED":
            raise BusinessRuleError(f"Cannot close a review cycle with status {review_cycle.status}. Review must be completed first.")
            
        # Get document
        document = None
        document_version = review_cycle.document_version
        if document_version:
            document = document_version.document
        
        if not document:
            raise ResourceNotFoundError("Document not found for this review cycle")
        
        # Check if user has permission to modify this document
        is_document_owner = document.owner_uid == user.uid
        is_review_initiator = review_cycle.initiated_by_uid == user.uid
        has_manage_permission = permissions.user_has_permission(user, "MANAGE_REVIEWS")
        
        if not (is_document_owner or is_review_initiator or has_manage_permission):
            raise PermissionError("Only the document owner, review initiator, or managers can close this review cycle")
        
        # If requested, update document status
        if update_document_status:
            # Validate target status
            if not target_status in settings.DOCUMENT_STATUSES.values():
                raise ValidationError(f"Invalid target status: {target_status}")
                
            # Check if status transition is valid
            if not settings.is_valid_status_transition(document.status, target_status):
                raise BusinessRuleError(f"Invalid status transition from {document.status} to {target_status}")
                
            # Update document status
            from CDocs.controllers.document_controller import update_document
            update_result = update_document(
                user=user,
                document_uid=document.uid,
                status=target_status
            )
            
            if not update_result.get('success', False):
                raise BusinessRuleError(f"Failed to update document status: {update_result.get('message', 'Unknown error')}")
        
        # Log event
        audit_trail.log_review_event(
            event_type="REVIEW_CYCLE_CLOSED",
            user=user,
            review_uid=review_uid,
            details={
                "document_uid": document.uid,
                "document_status_updated": update_document_status,
                "target_status": target_status if update_document_status else None
            }
        )
        
        # Notify document owner if different from current user
        if document.owner_uid and document.owner_uid != user.uid:
            notifications.send_notification(
                notification_type="REVIEW_CYCLE_CLOSED",
                users=[document.owner_uid],
                resource_uid=review_cycle.uid,
                resource_type="ReviewCycle",
                message=f"Review cycle for {document.doc_number} has been closed",
                details={
                    "review_uid": review_uid,
                    "document_uid": document.uid,
                    "closed_by": user.name,
                    "doc_number": document.doc_number,
                    "title": document.title,
                    "status_updated": update_document_status,
                    "new_status": target_status if update_document_status else None
                },
                send_email=True,
                email_template="review_cycle_closed",
                email_data={
                    "app_url": settings.APP_URL,
                    "doc_uid": document.uid,
                    "review_uid": review_uid,
                    "closed_by": user.name,
                    "status_updated": update_document_status,
                    "new_status": target_status if update_document_status else None
                }
            )
        
        # 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,
            "document_uid": document.uid,
            "document_status_updated": update_document_status,
            "new_status": target_status if update_document_status else None,
            "message": f"Review cycle closed successfully"
        }
        
    except (ResourceNotFoundError, ValidationError, PermissionError, BusinessRuleError) as e:
        # Re-raise known errors
        raise
    except Exception as e:
        logger.error(f"Error closing review cycle: {e}")
        raise BusinessRuleError(f"Failed to close review cycle: {e}")

Parameters

Name Type Default Kind
user DocUser - positional_or_keyword
review_uid str - positional_or_keyword
update_document_status bool True positional_or_keyword
target_status str 'DRAFT' positional_or_keyword

Parameter Details

user: DocUser object representing the authenticated user attempting to close the review cycle. Must have MANAGE_REVIEWS or EDIT_DOCUMENT permission, or be the document owner or review initiator.

review_uid: String containing the unique identifier (UID) of the review cycle to be closed. Must reference an existing ReviewCycle with status 'COMPLETED'.

update_document_status: Boolean flag (default True) indicating whether to update the associated document's status after closing the review. When True, the document status will be changed to target_status.

target_status: String (default 'DRAFT') specifying the desired document status if update_document_status is True. Must be a valid status from settings.DOCUMENT_STATUSES and represent a valid transition from the current document status.

Return Value

Type: Dict[str, Any]

Returns a dictionary with keys: 'success' (bool, always True on successful execution), 'review_uid' (str, the closed review cycle UID), 'document_uid' (str, associated document UID), 'document_status_updated' (bool, whether status was updated), 'new_status' (str or None, the new document status if updated), and 'message' (str, success message). On error, raises one of the documented exceptions instead of returning.

Dependencies

  • logging
  • uuid
  • os
  • typing
  • datetime
  • traceback
  • CDocs.db
  • CDocs.config.settings
  • CDocs.config.permissions
  • CDocs.models.document
  • CDocs.models.review
  • CDocs.models.user_extensions
  • CDocs.utils.audit_trail
  • CDocs.utils.notifications
  • CDocs.controllers
  • CDocs.controllers.document_controller
  • CDocs.controllers.share_controller

Required Imports

from typing import Dict, Any
from CDocs.models.user_extensions import DocUser
from CDocs.models.review import ReviewCycle
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
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 update_document_status is True

Required (conditional)
from CDocs.controllers.share_controller import manage_document_permissions

Condition: always executed at end of function to update document permissions

Required (conditional)

Usage Example

from CDocs.models.user_extensions import DocUser
from CDocs.controllers.review_controller import close_review_cycle

# Get authenticated user
user = DocUser.query.get(current_user_uid)

# Close review cycle without updating document status
result = close_review_cycle(
    user=user,
    review_uid='rev-12345-abcde',
    update_document_status=False
)

# Close review cycle and update document to APPROVED status
result = close_review_cycle(
    user=user,
    review_uid='rev-12345-abcde',
    update_document_status=True,
    target_status='APPROVED'
)

if result['success']:
    print(f"Review closed. Document status: {result['new_status']}")
    print(f"Document UID: {result['document_uid']}")

Best Practices

  • Always ensure the review cycle has status 'COMPLETED' before attempting to close it
  • Validate that the target_status is appropriate for your document workflow before calling with update_document_status=True
  • Handle all four exception types (ResourceNotFoundError, ValidationError, PermissionError, BusinessRuleError) in calling code
  • The function automatically sends email notifications to document owners, so ensure email system is properly configured
  • User must have MANAGE_REVIEWS or EDIT_DOCUMENT permission, OR be the document owner or review initiator
  • The function updates document sharing permissions automatically after closure, which may affect user access
  • All operations are logged to audit trail for compliance tracking
  • Status transitions are validated against system settings - ensure settings.DOCUMENT_STATUSES and is_valid_status_transition() are properly configured
  • The decorator log_controller_action('close_review_cycle') provides additional logging, ensure it's properly configured

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function close_approval_cycle 88.1% similar

    Closes a completed approval cycle and optionally updates the associated document's status, with permission checks, audit logging, and notifications.

    From: /tf/active/vicechatdev/CDocs/controllers/approval_controller.py
  • function cancel_review_cycle 79.7% 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 complete_review 79.3% 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
  • function close_approval_cycle_v1 77.1% similar

    Administratively closes an approval cycle by setting a final decision (APPROVED or REJECTED), updating the associated document status, and notifying relevant stakeholders.

    From: /tf/active/vicechatdev/CDocs/controllers/approval_controller_bis.py
  • function update_reviewer_permissions 71.2% similar

    Updates file access permissions for reviewers associated with a review cycle by delegating to the main document permission management function.

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