function clone_document_v1
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.
/tf/active/vicechatdev/document_controller_backup.py
1782 - 1953
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
logginguuidostempfiletypingdatetimeiopanelshutiltracebackCDocs
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
from CDocs.models.user_extensions import DocUser
from CDocs.controllers.document_controller import clone_document
# Get authenticated user
user = DocUser(uid='user-123')
# Clone document as a new independent document with content
result = clone_document(
user=user,
document_uid='doc-456',
new_title='Updated Product Manual',
include_content=True,
clone_as_new_revision=False
)
if result['success']:
cloned_doc = result['document']
print(f"Created new document: {cloned_doc['docNumber']}")
print(f"Cloned from: {result['source_document']['doc_number']}")
# Create a new revision of existing document
revision_result = clone_document(
user=user,
document_uid='doc-456',
include_content=True,
clone_as_new_revision=True
)
if revision_result['success']:
print(f"Created revision: {revision_result['message']}")
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 (enforced by decorator but good to verify in calling code)
- 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 (maintains document number)
- Use clone_as_new_revision=False when creating an independent copy (generates new document number)
- 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' key in the returned dictionary before accessing other fields
- The cloned document is always created in DRAFT status regardless of source document status
- Audit trail events are automatically logged for document lifecycle tracking
- Notifications are automatically sent to relevant users about the new document
- The function requires a properly configured CDocs database with schema and FileCloud integration
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
function clone_document 91.8% similar
-
function create_document_v3 65.0% similar
-
function delete_document 64.7% similar
-
function create_document_v4 64.7% similar
-
function create_document_v2 64.6% similar