🔍 Code Extractor

function clone_document

Maturity: 85

Clones an existing controlled document, creating a new document with optional content copying, custom properties, and FileCloud integration.

File:
/tf/active/vicechatdev/CDocs/controllers/document_controller.py
Lines:
2670 - 2873
Complexity:
complex

Purpose

This function creates a duplicate of an existing controlled document in a document management system. It handles the complete cloning workflow including: retrieving the source document, creating a new document with inherited or custom properties, optionally copying document content and versions, uploading to FileCloud storage, ensuring proper folder structure, and logging audit trail events. The function supports customization of title, document type, department, and additional properties, while maintaining data integrity through transaction management and comprehensive error handling.

Source Code

def clone_document(
    user: DocUser,
    document_uid: str,
    new_title: Optional[str] = None,
    doc_type: Optional[str] = None,
    department: Optional[str] = None,
    include_content: bool = True,
    clone_as_new_revision: bool = False,
    additional_properties: Optional[Dict[str, Any]] = None,
    custom_doc_number: Optional[str] = None
) -> Dict[str, Any]:
    """
    Clone an existing document as a new document or new revision.
    
    Args:
        user: User cloning the document
        document_uid: UID of the source document
        new_title: Title for the cloned document (defaults to "Copy of [original]")
        doc_type: Document type code for the new document (defaults to same as original)
        department: Department code for the new document (defaults to same as original)
        include_content: Whether to include the content of the original document
        clone_as_new_revision: Whether to create as a new revision of the same document
        additional_properties: Optional additional properties for the new document (e.g., custom_path)
        custom_doc_number: Optional custom document number
        
    Returns:
        Dictionary with cloned document details
    """
    try:
        # Get the source document
        source_document = ControlledDocument(uid=document_uid)
        if not source_document:
            raise ResourceNotFoundError(f"Document {document_uid} not found")
            
        # Get current version
        current_version = source_document.current_version
        if not current_version and include_content:
            raise BusinessRuleError("Document has no current version to clone")
        
        # Use original title if none provided
        if not new_title:
            new_title = f"Copy of {source_document.title}"
        
        # Use original document type and department if none provided
        if not doc_type:
            doc_type = source_document.doc_type
        
        if not department:
            department = source_document.department
            
        # Prepare properties for the new document
        properties = {
            "description": f"Cloned from {source_document.doc_number}: {source_document.title}",
            "status": "DRAFT",
            "revision": "0.0"  # Start with 0.1 for cloned document
        }
        
        # Check if source document has a custom path, and if so, preserve it in the clone
        # unless a different one is explicitly provided
        if hasattr(source_document, 'custom_path') and source_document.custom_path and not (additional_properties and 'custom_path' in additional_properties):
            properties['custom_path'] = source_document.custom_path
        
        # Add any additional properties
        if additional_properties:
            properties.update(additional_properties)
        
        # Create new document with the merged properties
        new_document = ControlledDocument.create(
            title=new_title,
            doc_type=doc_type,
            department=department,
            owner=user,
            doc_number=custom_doc_number,
            properties=properties
        )
        
        if not new_document:
            raise BusinessRuleError("Failed to create new document")
            
        # Copy content if requested
        version_result = None
        if include_content and current_version:
            # Download content from source document
            content_result = download_document_version(
                user=user,
                document_uid=document_uid,
                version_uid=current_version.uid if current_version else None
            )
            
            if not content_result or 'content' not in content_result:
                # Clean up the new document if content download fails
                try:
                    db.delete_node(new_document.uid, cascade=True)
                except:
                    pass
                raise BusinessRuleError("Failed to download source document content")
                
            # Extract file content and metadata
            file_content = content_result['content']
            file_name = content_result.get('file_name', f"{new_document.doc_number}_v0.1.docx")
            
            # Create initial version for new document
            version_result = create_document_version(
                user=user,
                document_uid=new_document.uid,
                file_content=file_content,
                file_name=file_name,
                comment="Initial version created from cloned document"
            )
            
            if not version_result or 'UID' not in version_result:
                # If version creation fails, delete the new document to clean up
                try:
                    db.delete_node(new_document.uid, cascade=True)
                except:
                    pass
                raise BusinessRuleError("Failed to create document version")
            
            # Upload the file to FileCloud - THIS WAS MISSING
            try:
                from CDocs.controllers.filecloud_controller import upload_document_to_filecloud
                
                # Set document metadata for storage
                metadata = {
                    "doc_uid": new_document.uid,
                    "doc_number": new_document.doc_number,
                    "version_uid": version_result.get('UID'),
                    "version_number": "0.1",  # Initial version
                    "title": new_title,
                    "status": "DRAFT"
                }
                
                # Upload to FileCloud
                filecloud_result = upload_document_to_filecloud(
                    user=user,
                    document=new_document,
                    file_content=file_content,
                    version_comment="Initial document version from clone",
                    metadata=metadata
                )
                
                if not filecloud_result or not filecloud_result.get('success'):
                    error_msg = filecloud_result.get('message', 'Unknown error') if filecloud_result else 'Failed to upload to FileCloud'
                    logger.error(f"Failed to upload to FileCloud: {error_msg}")
                    # Don't fail the entire operation, but log the warning
                    logger.warning(f"Document {new_document.doc_number} cloned but FileCloud upload failed: {error_msg}")
                    
            except Exception as fc_error:
                logger.error(f"Error uploading to FileCloud during clone: {fc_error}")
                # Don't fail the entire operation, but log the warning
                logger.warning(f"Document {new_document.doc_number} cloned but FileCloud upload failed: {str(fc_error)}")
            
            # Ensure folders are created in FileCloud
            try:
                from CDocs.controllers.filecloud_controller import ensure_document_folders
                ensure_document_folders(new_document)
            except Exception as folder_err:
                logger.warning(f"Warning: Could not ensure document folders: {folder_err}")
        
        # Log cloning event with custom path info if applicable
        event_details = {
            "source_document_uid": document_uid,
            "source_document_number": source_document.doc_number,
            "include_content": include_content
        }
        
        # Add custom path information to the audit trail if present
        if properties.get('custom_path'):
            event_details["custom_path"] = properties['custom_path']
        
        audit_trail.log_document_lifecycle_event(
            event_type="DOCUMENT_CLONED",
            user=user,
            document_uid=new_document.uid,
            details=event_details
        )
        
        # Return success with new document details
        return {
            "success": True,
            "message": "Document cloned successfully",
            "document_uid": new_document.uid,
            "document_number": new_document.doc_number,
            "document": {
                "uid": new_document.uid,
                "doc_number": new_document.doc_number,
                "title": new_document.title,
                "version_uid": version_result.get('UID') if version_result else None
            }
        }
        
    except (ResourceNotFoundError, ValidationError, PermissionError, BusinessRuleError) as e:
        logger.error(f"Error cloning document: {str(e)}")
        return {
            "success": False,
            "message": str(e)
        }
    except Exception as e:
        logger.error(f"Unexpected error cloning document: {str(e)}")
        logger.error(traceback.format_exc())
        return {
            "success": False,
            "message": f"An unexpected error occurred: {str(e)}"
        }

Parameters

Name Type Default Kind
user DocUser - positional_or_keyword
document_uid str - positional_or_keyword
new_title Optional[str] None positional_or_keyword
doc_type Optional[str] None positional_or_keyword
department Optional[str] None positional_or_keyword
include_content bool True positional_or_keyword
clone_as_new_revision bool False positional_or_keyword
additional_properties Optional[Dict[str, Any]] None positional_or_keyword
custom_doc_number Optional[str] None positional_or_keyword

Parameter Details

user: DocUser object representing the authenticated user performing the clone operation. Must have CREATE_DOCUMENT permission.

document_uid: Unique identifier (UID) string of the source document to be cloned. Must reference an existing document.

new_title: Optional string for the cloned document's title. If None, defaults to 'Copy of [original title]'.

doc_type: Optional document type code string for the new document. If None, inherits from the source document.

department: Optional department code string for the new document. If None, inherits from the source document.

include_content: Boolean flag (default True) indicating whether to copy the actual file content from the source document's current version.

clone_as_new_revision: Boolean flag (default False) for creating the clone as a new revision of the same document. Note: This parameter is defined but not currently implemented in the function logic.

additional_properties: Optional dictionary of custom properties to add to the new document (e.g., {'custom_path': '/some/path', 'metadata_key': 'value'}).

custom_doc_number: Optional string to specify a custom document number instead of auto-generating one.

Return Value

Type: Dict[str, Any]

Returns a dictionary with keys: 'success' (bool indicating operation success), 'message' (str with status/error message), 'document_uid' (str UID of cloned document if successful), 'document_number' (str document number if successful), and 'document' (dict with 'uid', 'doc_number', 'title', 'version_uid' if successful). On failure, only 'success' (False) and 'message' (error description) are returned.

Dependencies

  • logging
  • uuid
  • os
  • tempfile
  • typing
  • datetime
  • io
  • panel
  • shutil
  • traceback
  • json
  • re
  • random
  • CDocs
  • CDocs.db
  • CDocs.config
  • CDocs.models
  • CDocs.utils
  • CDocs.controllers
  • document_auditor

Required Imports

import logging
import traceback
from typing import Dict, Any, Optional
from CDocs import db
from CDocs.models.document import ControlledDocument
from CDocs.models.user_extensions import DocUser
from CDocs.utils import audit_trail
from CDocs.controllers import require_permission, log_controller_action, transaction
from CDocs.controllers import ResourceNotFoundError, ValidationError, PermissionError, BusinessRuleError

Conditional/Optional Imports

These imports are only needed under specific conditions:

from CDocs.controllers.filecloud_controller import upload_document_to_filecloud

Condition: only when include_content=True and content needs to be uploaded to FileCloud

Required (conditional)
from CDocs.controllers.filecloud_controller import ensure_document_folders

Condition: only when include_content=True to ensure FileCloud folder structure exists

Required (conditional)
from CDocs.controllers.document_controller import download_document_version, create_document_version

Condition: only when include_content=True to download source content and create new version

Required (conditional)

Usage Example

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

# Get authenticated user
user = DocUser.get_by_username('john.doe')

# Clone document with default settings (copies content)
result = clone_document(
    user=user,
    document_uid='doc-12345-abcde'
)

if result['success']:
    print(f"Document cloned: {result['document_number']}")
    print(f"New UID: {result['document_uid']}")
else:
    print(f"Clone failed: {result['message']}")

# Clone with custom properties
result = clone_document(
    user=user,
    document_uid='doc-12345-abcde',
    new_title='Updated Procedure Manual',
    doc_type='PROCEDURE',
    department='ENGINEERING',
    include_content=True,
    additional_properties={'custom_path': '/engineering/procedures'},
    custom_doc_number='ENG-PROC-001'
)

# Clone without content (metadata only)
result = clone_document(
    user=user,
    document_uid='doc-12345-abcde',
    new_title='Template Copy',
    include_content=False
)

Best Practices

  • Always check the 'success' field in the returned dictionary before accessing other fields
  • Ensure the user has CREATE_DOCUMENT permission before calling this function (enforced by decorator)
  • The function is wrapped in a transaction, so failures will rollback database changes automatically
  • When include_content=True, ensure sufficient storage space is available in FileCloud
  • The clone_as_new_revision parameter is currently not implemented; use include_content and additional_properties instead
  • FileCloud upload failures are logged but don't fail the entire operation; check logs for warnings
  • Custom paths from source documents are preserved unless explicitly overridden in additional_properties
  • The cloned document always starts with status='DRAFT' and revision='0.0'
  • Audit trail events are automatically logged with source document information
  • If content download or version creation fails, the function attempts to clean up the partially created document
  • Use custom_doc_number carefully to avoid conflicts with existing document numbers

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function clone_document_v1 91.8% similar

    Clones an existing controlled document to create either a new independent document or a new revision of the same document, optionally including the document's content.

    From: /tf/active/vicechatdev/document_controller_backup.py
  • function create_document_v2 69.2% similar

    Creates a new version of a controlled document by generating version metadata, storing the file in FileCloud, updating the document's revision number, and creating an audit trail entry.

    From: /tf/active/vicechatdev/CDocs/controllers/document_controller.py
  • function create_document_v3 66.7% 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_v1 66.6% similar

    Creates a new version of an existing document in a document management system, storing the file in FileCloud and tracking version metadata in Neo4j graph database.

    From: /tf/active/vicechatdev/document_controller_backup.py
  • function create_document_v4 64.6% 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