🔍 Code Extractor

function update_document_v1

Maturity: 71

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.

File:
/tf/active/vicechatdev/CDocs/controllers/document_controller.py
Lines:
2503 - 2665
Complexity:
complex

Purpose

This function serves as the primary controller for updating document properties in a document management system. It handles complex business logic including: validating status transitions, triggering PDF publishing when moving to published status, resetting training assignments when documents are published or made effective, maintaining audit trails, and notifying stakeholders of changes. It enforces permissions and business rules while coordinating with multiple subsystems (FileCloud, notifications, training, audit).

Source Code

def update_document(
    user: DocUser,
    document_uid: str,
    **kwargs
) -> 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' in kwargs and kwargs['title'] is not None and kwargs['title'] != document.title:
            document.title = kwargs['title']
            changes['title'] = {'old': original_values['title'], 'new': kwargs['title']}
        
        # Update description if provided
        if 'description' in kwargs and kwargs['description'] is not None and kwargs['description'] != document.description:
            document.description = kwargs['description']
            changes['description'] = {'old': original_values['description'], 'new': kwargs['description']}
        
        # Handle status update with special consideration for format transitions
        if 'status' in kwargs and kwargs['status'] is not None and kwargs['status'] != document.status:
            current_status = document.status
            target_status = kwargs['status']
            
            # 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=kwargs.get('comment', 'Status updated to Published')
                    )
                    return result
            if is_editable_status(current_status) and not is_editable_status(target_status):
                # 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': kwargs.get('comment') if kwargs else None
                }
            )
        
        # Update owner if provided
        if 'owner_uid' in kwargs and kwargs['owner_uid'] is not None and kwargs['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": kwargs['owner_uid']}
            )
            
            if not owner_exists or not owner_exists[0].get('exists', False):
                raise ValidationError(f"User not found: {kwargs['owner_uid']}")
                
            # Update owner
            document.owner = kwargs['owner_uid']
            changes['owner_uid'] = {'old': original_values['owner_uid'], 'new': kwargs['owner_uid']}
        
        # Update metadata if provided
        if 'metadata' in kwargs and kwargs['metadata'] is not None:
            metadata = kwargs['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}
        
        # If status is being changed to PUBLISHED or EFFECTIVE, reset training
        if 'status' in kwargs:
            new_status = kwargs['status']
            if new_status in [STATUS_PUBLISHED, STATUS_EFFECTIVE]:
                try:
                    # Reset training for this document
                    document.reset_training_on_publish()
                    
                    # Log training reset
                    audit_trail.log_event(
                        event_type="TRAINING_RESET_ON_PUBLISH",
                        user=user,
                        resource_uid=document_uid,
                        resource_type="ControlledDocument",
                        details={
                            "doc_number": document.doc_number,
                            "new_status": new_status
                        }
                    )
                    
                    # Notify users with training assignments
                    from CDocs.controllers.training_controller import reset_document_training
                    reset_document_training(user, document_uid)
                    
                except Exception as training_err:
                    logger.warning(f"Error resetting training on publish: {training_err}")
                    # Don't fail the document update if training reset fails
    
        # 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
**kwargs - - var_keyword

Parameter Details

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

document_uid: String UID (unique identifier) of the document to update. Must correspond to an existing ControlledDocument in the database.

kwargs: Variable keyword arguments containing optional update fields: 'title' (str) - new document title, 'description' (str) - new document description, 'status' (str) - new document status (must be valid transition), 'owner_uid' (str) - UID of new document owner (must exist), 'metadata' (dict) - metadata to merge with existing metadata, 'comment' (str) - optional comment for status changes

Return Value

Type: Dict[str, Any]

Returns a dictionary with keys: 'success' (bool) - True if update succeeded, 'message' (str) - human-readable success message, 'document_uid' (str) - UID of updated document, 'changes' (dict) - detailed change log where each key is a field name and value is a dict with 'old' and 'new' values (or 'updated': True for metadata). May also return result from publish_document if status transition triggers publishing.

Dependencies

  • logging
  • uuid
  • os
  • tempfile
  • typing
  • datetime
  • io
  • panel
  • shutil
  • traceback
  • json
  • re
  • random

Required Imports

from CDocs import db
from CDocs.config import settings
from CDocs.config import permissions
from CDocs.models.document import ControlledDocument
from CDocs.models.user_extensions import DocUser
from CDocs.utils import notifications
from CDocs.utils import audit_trail
from CDocs.controllers import require_permission
from CDocs.controllers import log_controller_action
from CDocs.controllers import transaction
from CDocs.controllers import PermissionError
from CDocs.controllers import ResourceNotFoundError
from CDocs.controllers import ValidationError
from CDocs.controllers import BusinessRuleError
from CDocs.models.document_status import is_editable_status
from CDocs.models.document_status import is_published_status
from CDocs.models.document_status import STATUS_PUBLISHED
from CDocs.models.document_status import STATUS_EFFECTIVE
import logging

Conditional/Optional Imports

These imports are only needed under specific conditions:

from CDocs.controllers.training_controller import reset_document_training

Condition: only when status changes to PUBLISHED or EFFECTIVE and training reset is triggered

Required (conditional)
from CDocs.controllers.document_controller import publish_document

Condition: only when status transition requires publishing (transitioning to STATUS_PUBLISHED)

Required (conditional)

Usage Example

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

# Get authenticated user
user = DocUser(uid='user123')

# Update document title and description
result = update_document(
    user=user,
    document_uid='doc-456',
    title='Updated Document Title',
    description='New description for the document',
    comment='Updated metadata'
)

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

# Update document status (triggers special handling)
result = update_document(
    user=user,
    document_uid='doc-456',
    status='PUBLISHED',
    comment='Publishing document for release'
)

# Update owner and metadata
result = update_document(
    user=user,
    document_uid='doc-456',
    owner_uid='newowner789',
    metadata={'department': 'Engineering', 'project': 'Alpha'}
)

Best Practices

  • Always wrap calls in try-except blocks to handle ResourceNotFoundError, ValidationError, PermissionError, and BusinessRuleError
  • Ensure user has EDIT_DOCUMENT permission before calling (decorator enforces this)
  • Only pass kwargs for fields you want to update; omitted fields remain unchanged
  • When changing status, verify the transition is valid for your document's current state
  • Use the 'comment' kwarg when changing status to provide audit context
  • Be aware that status changes to PUBLISHED trigger publish_document which may have side effects (PDF generation, FileCloud upload)
  • Status changes to PUBLISHED or EFFECTIVE reset training assignments for all users
  • Metadata updates are merged with existing metadata, not replaced
  • The function runs within a transaction decorator, so all changes are atomic
  • Check the 'changes' dict in the return value to see what actually changed
  • Owner changes validate that the new owner exists in the system
  • Training reset failures are logged but don't fail the document update

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function update_document 96.3% similar

    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.

    From: /tf/active/vicechatdev/document_controller_backup.py
  • function update_status_after_approval 75.5% 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 71.8% 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 publish_document_v1 70.9% similar

    Publishes a controlled document by incrementing its version to the next major revision, converting it to a signed PDF, and updating its status to PUBLISHED.

    From: /tf/active/vicechatdev/CDocs/controllers/document_controller.py
  • function create_document_v3 70.6% 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
← Back to Browse