🔍 Code Extractor

function update_document

Maturity: 81

Updates properties of a controlled document including title, description, status, owner, and metadata, with special handling for status transitions that require format conversions or publishing workflows.

File:
/tf/active/vicechatdev/document_controller_backup.py
Lines:
1640 - 1775
Complexity:
complex

Purpose

This function serves as the primary interface for modifying document properties in a controlled document management system. It enforces business rules around status transitions, particularly when moving from editable to published states, ensures proper audit logging, validates ownership changes, and delegates to specialized publishing workflows when necessary. It's designed to maintain document integrity and compliance with document lifecycle management requirements.

Source Code

def update_document(
    user: DocUser,
    document_uid: str,
    title: Optional[str] = None,
    description: Optional[str] = None,
    status: Optional[str] = None,
    owner_uid: Optional[str] = None,
    metadata: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
    """
    Update a document's properties, handling format transitions when status changes.
    
    Args:
        user: User making the update
        document_uid: UID of document to update
        title: New document title (optional)
        description: New document description (optional)
        status: New document status (optional)
        owner_uid: New document owner UID (optional)
        metadata: New document metadata (optional)
        
    Returns:
        Dictionary with update status
    """
    try:
        # Get document instance
        document = ControlledDocument(uid=document_uid)
        if not document:
            raise ResourceNotFoundError(f"Document not found: {document_uid}")
        
        # Store original values for audit
        original_values = {
            'title': document.title,
            'description': document.description,
            'status': document.status,
            'owner_uid': document.owner_uid
        }
        
        # Track what changed
        changes = {}
        
        # Update title if provided
        if title is not None and title != document.title:
            document.title = title
            changes['title'] = {'old': original_values['title'], 'new': title}
        
        # Update description if provided
        if description is not None and description != document.description:
            document.description = description
            changes['description'] = {'old': original_values['description'], 'new': description}
        
        # Handle status update with special consideration for format transitions
        if status is not None and status != document.status:
            current_status = document.status
            target_status = status.lower()
            
            # Check if the transition is valid
            if not document.can_transition_to(target_status):
                raise BusinessRuleError(f"Invalid status transition from {current_status} to {target_status}")
            
            # Check if we're transitioning from editable to non-editable status
            if is_editable_status(current_status) and is_published_status(target_status):
                # Status requires PDF publishing
                if target_status == STATUS_PUBLISHED:
                    # Call publish_document instead
                    result = publish_document(
                        user=user,
                        document_uid=document_uid,
                        publish_comment=metadata.get('comment', 'Status updated to Published') if metadata else None
                    )
                    return result
                else:
                    # For other non-editable statuses, check if PDF exists
                    current_version = document.current_version
                    if current_version and not current_version.pdf_file_path:
                        raise BusinessRuleError(f"Document must be published before changing to {target_status}")
            
            # For transitions that don't require special handling, update status normally
            document.status = target_status
            changes['status'] = {'old': current_status, 'new': target_status}
            
            # Log status change
            audit_trail.log_document_lifecycle_event(
                event_type="DOCUMENT_STATUS_CHANGED",
                user=user,
                document_uid=document_uid,
                details={
                    'old_status': current_status,
                    'new_status': target_status,
                    'comment': metadata.get('comment') if metadata else None
                }
            )
        
        # Update owner if provided
        if owner_uid is not None and owner_uid != document.owner_uid:
            # Check if new owner exists
            owner_exists = db.run_query(
                "MATCH (u:User {UID: $uid}) RETURN COUNT(u) > 0 as exists",
                {"uid": owner_uid}
            )
            
            if not owner_exists or not owner_exists[0].get('exists', False):
                raise ValidationError(f"User not found: {owner_uid}")
                
            # Update owner
            document.owner = owner_uid
            changes['owner_uid'] = {'old': original_values['owner_uid'], 'new': owner_uid}
        
        # Update metadata if provided
        if metadata:
            # Merge with existing metadata if any
            existing_metadata = getattr(document, 'metadata', {}) or {}
            updated_metadata = {**existing_metadata, **metadata}
            
            # Update in database
            db.update_node(document_uid, {'metadata': updated_metadata})
            changes['metadata'] = {'updated': True}
        
        # Notify about document update if significant changes were made
        if changes:
            notifications.notify_document_update(document, "DOCUMENT_UPDATED")
        
        # Return success response with changes
        return {
            "success": True,
            "message": "Document updated successfully",
            "document_uid": document_uid,
            "changes": changes
        }
        
    except (ResourceNotFoundError, ValidationError, PermissionError, BusinessRuleError) as e:
        # Re-raise known errors
        raise
    except Exception as e:
        logger.error(f"Error updating document: {e}")
        raise BusinessRuleError(f"Failed to update document: {e}")

Parameters

Name Type Default Kind
user DocUser - positional_or_keyword
document_uid str - positional_or_keyword
title Optional[str] None positional_or_keyword
description Optional[str] None positional_or_keyword
status Optional[str] None positional_or_keyword
owner_uid Optional[str] None positional_or_keyword
metadata Optional[Dict[str, Any]] None positional_or_keyword

Parameter Details

user: DocUser object representing the authenticated user performing the update. Used for permission checks, audit logging, and notification purposes.

document_uid: Unique identifier (string) of the document to be updated. Must correspond to an existing ControlledDocument in the system.

title: Optional new title for the document (string). If provided and different from current title, the document title will be updated. Pass None to leave unchanged.

description: Optional new description for the document (string). If provided and different from current description, the document description will be updated. Pass None to leave unchanged.

status: Optional new status for the document (string). Must be a valid status value and transition. Special handling applies when transitioning from editable to published states. Valid values include: 'draft', 'in_review', 'in_approval', 'approved', 'published', 'effective', 'archived', 'obsolete'.

owner_uid: Optional UID (string) of the new document owner. Must correspond to an existing User in the system. Ownership transfer is validated before being applied.

metadata: Optional dictionary containing additional metadata to merge with existing document metadata. Can include custom fields like 'comment' which is used in status change logging.

Return Value

Type: Dict[str, Any]

Returns a dictionary with keys: 'success' (boolean indicating operation success), 'message' (string describing the outcome), 'document_uid' (string echoing the updated document's UID), and 'changes' (dictionary detailing what changed, with each key being a field name and value being a dict with 'old' and 'new' values, or 'updated': True for metadata). On error, raises one of: ResourceNotFoundError, ValidationError, PermissionError, or BusinessRuleError.

Dependencies

  • logging
  • uuid
  • os
  • tempfile
  • typing
  • datetime
  • io
  • panel
  • shutil
  • traceback
  • CDocs
  • config

Required Imports

from typing import Dict, List, Any, Optional, Union, BinaryIO, Tuple
from datetime import datetime, timedelta
from CDocs import db
from CDocs.config import settings, permissions
from CDocs.models.document import ControlledDocument, DocumentVersion
from CDocs.models.user_extensions import DocUser
from CDocs.utils import document_processor, notifications, audit_trail
from CDocs.db.schema_manager import NodeLabels, RelTypes
from CDocs.controllers import require_permission, log_controller_action, transaction, PermissionError, ResourceNotFoundError, ValidationError, BusinessRuleError, IntegrationError
from CDocs.models.document_status import is_editable_status, is_published_status, STATUS_DRAFT, STATUS_IN_REVIEW, STATUS_IN_APPROVAL, STATUS_APPROVED, STATUS_PUBLISHED, STATUS_EFFECTIVE, STATUS_ARCHIVED, STATUS_OBSOLETE, get_next_status
from CDocs.controllers.document_controller import publish_document
import logging

Conditional/Optional Imports

These imports are only needed under specific conditions:

from CDocs.controllers.filecloud_controller import upload_document_to_filecloud, download_document_from_filecloud, get_document_metadata_from_filecloud, get_filecloud_client, get_filecloud_document_path

Condition: only if FileCloud integration is enabled and status transitions require PDF generation or file operations

Optional
from CDocs.utils.document_converter import ControlledDocumentConverter

Condition: only if document format conversion is needed during status transitions

Optional
from CDocs.utils.filecloud_integration import FileCloudIntegration

Condition: only if using FileCloud for document storage

Optional
from CDocs.models.review import ReviewCycle

Condition: only if review workflow features are used

Optional
from CDocs.models.approval import Approval

Condition: only if approval workflow features are used

Optional

Usage Example

from CDocs.models.user_extensions import DocUser
from CDocs.controllers.document_controller import update_document

# Assume user and document already exist
user = DocUser(uid='user123')
document_uid = 'doc456'

# Update document title and description
result = update_document(
    user=user,
    document_uid=document_uid,
    title='Updated Document Title',
    description='This is the updated description',
    metadata={'comment': 'Updated for clarity'}
)

if result['success']:
    print(f"Document updated: {result['changes']}")

# Update document status (will trigger special handling if transitioning to published)
result = update_document(
    user=user,
    document_uid=document_uid,
    status='published',
    metadata={'comment': 'Ready for publication'}
)

# Transfer document ownership
result = update_document(
    user=user,
    document_uid=document_uid,
    owner_uid='newowner789',
    metadata={'comment': 'Ownership transferred'}
)

# Handle errors
try:
    result = update_document(
        user=user,
        document_uid='nonexistent',
        title='New Title'
    )
except ResourceNotFoundError as e:
    print(f"Document not found: {e}")
except BusinessRuleError as e:
    print(f"Business rule violation: {e}")

Best Practices

  • Always wrap calls in try-except blocks to handle ResourceNotFoundError, ValidationError, PermissionError, and BusinessRuleError exceptions
  • Ensure the user has EDIT_DOCUMENT permission for the specified document before calling (decorator enforces this)
  • When updating status to 'published', the function automatically delegates to publish_document() which handles PDF generation
  • Use the metadata parameter to include comments that will be logged in the audit trail, especially for status changes
  • Only provide parameters you want to update; passing None leaves fields unchanged
  • Verify that owner_uid corresponds to an existing user before attempting ownership transfer
  • Be aware that status transitions are validated against business rules; not all transitions are allowed
  • The function operates within a transaction context, so all changes are atomic
  • Check the 'changes' key in the return value to see exactly what was modified
  • For status transitions from editable to non-editable states, ensure a PDF version exists or use publish_document directly
  • The function sends notifications automatically when significant changes are made
  • All updates are logged to the audit trail for compliance and tracking purposes

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function update_document_v1 96.3% similar

    Updates a controlled document's properties (title, description, status, owner, metadata) with validation, audit logging, and special handling for status transitions that require PDF publishing or training resets.

    From: /tf/active/vicechatdev/CDocs/controllers/document_controller.py
  • function update_status_after_approval 76.6% similar

    Updates a controlled document's status after an approval workflow completes, determining the next status based on the approval decision and logging the change to the audit trail.

    From: /tf/active/vicechatdev/CDocs/controllers/document_controller.py
  • function update_document_metadata_in_filecloud 72.1% similar

    Updates metadata for a document stored in FileCloud, merging new metadata with existing values and logging the update in an audit trail.

    From: /tf/active/vicechatdev/CDocs/controllers/filecloud_controller.py
  • function create_document_v3 71.2% similar

    Creates a new controlled document in a document management system with specified properties, type, department, and status.

    From: /tf/active/vicechatdev/document_controller_backup.py
  • function create_document_v4 69.4% similar

    Creates a new controlled document in the CDocs system with specified metadata, content, and properties, returning a status dictionary with the created document details.

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