🔍 Code Extractor

function clone_document

Maturity: 80

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.

File:
/tf/active/vicechatdev/document_controller_backup.py
Lines:
1782 - 1953
Complexity:
complex

Purpose

This function provides document cloning capabilities in a controlled document management system. It supports two modes: creating a completely new document with a new document number (standard clone) or creating a new revision of the same document (revision clone). It handles permission checks, metadata copying, content duplication, audit logging, and notifications. The function is transactional and requires CREATE_DOCUMENT permission.

Source Code

def clone_document(
    user: DocUser,
    document_uid: str,
    new_title: Optional[str] = None,
    include_content: bool = True,
    clone_as_new_revision: bool = False
) -> Dict[str, Any]:
    """
    Clone an existing document to create a new document.
    
    Args:
        user: User creating the clone
        document_uid: UID of document to clone
        new_title: Optional new title (defaults to "Copy of [original title]")
        include_content: Whether to clone the content as well
        clone_as_new_revision: Whether to create a new revision of the same document
        
    Returns:
        Dictionary with cloned document details
        
    Raises:
        ResourceNotFoundError: If document not found
        PermissionError: If user doesn't have permission
        BusinessRuleError: If cloning fails
    """
    try:
        # Get document instance
        source_doc = ControlledDocument(uid=document_uid)
        if not source_doc:
            raise ResourceNotFoundError(f"Document not found: {document_uid}")
            
        # Get source document metadata
        doc_type = source_doc.doc_type
        department = source_doc.department
        
        # Check if user has permission for this department
        if not permissions.user_has_department_permission(user, department):
            raise PermissionError(f"User does not have permission to create documents in department: {department}")
            
        # Generate title if not provided
        if not new_title:
            if clone_as_new_revision:
                new_title = source_doc.title
            else:
                new_title = f"Copy of {source_doc.title}"
                
        # Clone document
        if clone_as_new_revision:
            # Create a new revision of the same document
            # Keep the same document number, but increment major version
            cloned_doc = source_doc
            
            # Update metadata for new revision
            cloned_doc.modified_date = datetime.now()
            cloned_doc.status = "DRAFT"
            
            # Save changes
            cloned_doc.save()
            
            # Log revision event
            audit_trail.log_document_lifecycle_event(
                event_type="DOCUMENT_REVISION_CREATED",
                user=user,
                document_uid=cloned_doc.uid,
                details={
                    "previous_status": source_doc.status,
                    "new_status": "DRAFT"
                }
            )
            
        else:
            # Create a completely new document
            # Generate new document number
            doc_number = generate_document_number(doc_type, department)
            
            # Find the CDocs root node
            cdocs_result = db.run_query("MATCH (c:CDocs) RETURN c.UID as uid LIMIT 1")
            cdocs_uid = cdocs_result[0]['uid'] if cdocs_result and 'uid' in cdocs_result[0] else None

            # Clone properties
            clone_properties = {
                'UID': str(uuid.uuid4()),
                'docNumber': doc_number,
                'title': new_title,
                'docType': doc_type,
                'department': department,
                'status': "DRAFT",
                'description': source_doc.description,
                'ownerUID': user.uid,
                'ownerName': user.name,
                'creatorUID': user.uid,
                'creatorName': user.name,
                'createdDate': datetime.now(),
                'modifiedDate': datetime.now(),
                'metadata': source_doc.metadata.copy() if source_doc.metadata else {}
            }

            # Update metadata for clone
            clone_properties['metadata'] = clone_properties['metadata'] or {}
            clone_properties['metadata']["cloned_from"] = document_uid
            clone_properties['metadata']["source_doc_number"] = source_doc.doc_number

            # Relationships to create
            relationships = []
            if cdocs_uid:
                relationships.append({
                    'to_node_uid': cdocs_uid,
                    'relationship_type': RelTypes.HAS_DOCUMENT,
                    'direction': "INCOMING"  # CDocs -> Document
                })

            # Create the cloned document node with relationships
            cloned_data = db.create_node_and_ensure_relationships(
                label=NodeLabels.CONTROLLED_DOCUMENT,
                properties=clone_properties,
                relationships=relationships
            )

            if not cloned_data:
                raise BusinessRuleError("Failed to create cloned document in database")

            # Create the document object
            cloned_doc = ControlledDocument(cloned_data['UID'])
            
        # Clone content if requested
        cloned_version = None
        if include_content and source_doc.current_version:
            # Get source version
            source_version = source_doc.current_version
            
            # Download source content
            file_content = download_document_from_filecloud(
                user=user,
                document_uid=document_uid
            )
            
            if isinstance(file_content, bytes):
                # Create new version
                version_number = "1.0" if not clone_as_new_revision else None
                cloned_version = create_document_version(
                    user=user,
                    document_uid=cloned_doc.uid,
                    file_content=file_content,
                    file_name=source_version.file_name,
                    version_number=version_number,
                    comment="Cloned from document " + source_doc.doc_number,
                    make_current=True
                )
        
        # Send notification
        notifications.notify_document_update(cloned_doc, "DOCUMENT_CREATED")
        
        return {
            "success": True,
            "document": cloned_doc.to_dict(),
            "is_new_revision": clone_as_new_revision,
            "source_document": {
                "uid": source_doc.uid,
                "doc_number": source_doc.doc_number,
                "title": source_doc.title
            },
            "version": cloned_version.to_dict() if cloned_version else None,
            "message": f"Document {source_doc.doc_number} cloned successfully" if not clone_as_new_revision else
                      f"New revision of document {source_doc.doc_number} created successfully"
        }
        
    except (ResourceNotFoundError, PermissionError, BusinessRuleError) as e:
        # Re-raise known errors
        raise
    except Exception as e:
        logger.error(f"Error cloning document {document_uid}: {e}")
        raise BusinessRuleError(f"Failed to clone document: {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
include_content bool True positional_or_keyword
clone_as_new_revision bool False positional_or_keyword

Parameter Details

user: DocUser object representing the authenticated user performing the clone operation. Used for permission checks, audit logging, and setting ownership of the cloned document.

document_uid: String containing the unique identifier (UID) of the source document to be cloned. Must reference an existing ControlledDocument in the database.

new_title: Optional string for the title of the cloned document. If None, defaults to 'Copy of [original title]' for new documents or keeps the original title for revisions.

include_content: Boolean flag (default True) indicating whether to clone the document's file content. If True, downloads the source document's current version and creates a new version for the clone.

clone_as_new_revision: Boolean flag (default False) that determines cloning behavior. If True, creates a new revision of the same document (same doc number, incremented version). If False, creates a completely new document with a new document number.

Return Value

Type: Dict[str, Any]

Returns a dictionary with keys: 'success' (bool indicating operation success), 'document' (dict representation of the cloned ControlledDocument), 'is_new_revision' (bool indicating if this is a revision), 'source_document' (dict with uid, doc_number, and title of source), 'version' (dict representation of cloned DocumentVersion if content was included, else None), and 'message' (string describing the operation result).

Dependencies

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

Required Imports

from typing import Dict, Any, Optional
from datetime import datetime
from CDocs import db
from CDocs.config import settings, permissions
from CDocs.models.document import ControlledDocument
from CDocs.models.user_extensions import DocUser
from CDocs.utils import notifications, audit_trail
from CDocs.db.schema_manager import NodeLabels, RelTypes
from CDocs.controllers import require_permission, log_controller_action, transaction
from CDocs.controllers import PermissionError, ResourceNotFoundError, BusinessRuleError
from CDocs.controllers.filecloud_controller import download_document_from_filecloud
import uuid
import logging

Conditional/Optional Imports

These imports are only needed under specific conditions:

from CDocs.controllers.document_controller import create_document_version

Condition: Required when include_content=True to create a new version with cloned content

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

Condition: Required when clone_as_new_revision=False to generate a new document number

Required (conditional)

Usage Example

# Standard clone - creates new document
from CDocs.models.user_extensions import DocUser
from CDocs.controllers.document_controller import clone_document

user = DocUser(uid='user123')
source_doc_uid = 'doc-uuid-12345'

# Clone with new title and content
result = clone_document(
    user=user,
    document_uid=source_doc_uid,
    new_title='My Cloned Document',
    include_content=True,
    clone_as_new_revision=False
)

print(f"Cloned document UID: {result['document']['UID']}")
print(f"New doc number: {result['document']['docNumber']}")
print(f"Message: {result['message']}")

# Create new revision of existing document
revision_result = clone_document(
    user=user,
    document_uid=source_doc_uid,
    include_content=True,
    clone_as_new_revision=True
)

print(f"New revision created: {revision_result['is_new_revision']}")
print(f"Version: {revision_result['version']['version_number']}")

Best Practices

  • Always wrap calls in try-except blocks to handle ResourceNotFoundError, PermissionError, and BusinessRuleError exceptions
  • Ensure the user has CREATE_DOCUMENT permission before calling (though decorator enforces this)
  • Verify the user has department permissions for the source document's department
  • Use clone_as_new_revision=True when creating a new version of the same document, False for independent copies
  • Set include_content=False if you only need to clone metadata without file content (faster operation)
  • The function is transactional - all operations will rollback on failure
  • Check the 'success' field in the returned dictionary before proceeding with dependent operations
  • The cloned document is always created in DRAFT status regardless of source document status
  • When cloning as new revision, the document number remains the same but version increments
  • The function logs audit events automatically - no need for additional logging
  • Notifications are sent automatically to relevant stakeholders
  • Metadata from source document is copied and augmented with cloning information (cloned_from, source_doc_number)

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function delete_document 64.8% similar

    Deletes a controlled document from the system with permission checks, status validation, and audit logging.

    From: /tf/active/vicechatdev/document_controller_backup.py
  • function create_document_v2 64.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 get_document 64.4% similar

    Retrieves comprehensive details of a controlled document by its UID, with optional inclusion of version history, review cycles, and approval cycles.

    From: /tf/active/vicechatdev/document_controller_backup.py
  • function create_document 63.6% similar

    Creates a new controlled document in a document management system with versioning, audit trails, and optional initial content.

    From: /tf/active/vicechatdev/document_controller_backup.py
  • function update_document 63.2% 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
← Back to Browse