function create_document_v2
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.
/tf/active/vicechatdev/CDocs/controllers/document_controller.py
678 - 887
complex
Purpose
This function manages the complete lifecycle of creating a new document version in a document management system. It handles version numbering (incrementing minor version), uploads files to FileCloud storage, creates Neo4j database nodes and relationships, manages document permissions, and logs audit events. It supports both custom and standard folder structures for file organization.
Source Code
def create_document_version(
user: DocUser,
document_uid: str,
file_content: bytes,
file_name: str,
comment: str = ""
) -> Dict[str, Any]:
"""
Create a new version of a document, storing file in FileCloud.
Args:
user: User creating the version
document_uid: UID of the document
file_content: Binary content of the file
file_name: Name of the file
comment: Comment for this version
Returns:
Dictionary with version information
"""
logger.info(f"Creating new version for document {document_uid}")
try:
# Check if document exists
from CDocs.models.document import ControlledDocument
logger.info(f"Loading document {document_uid}")
document = ControlledDocument(uid=document_uid)
if not document or not document.title: # Check if document actually loaded
logger.error(f"Document {document_uid} not found or failed to load")
raise ResourceNotFoundError(f"Document {document_uid} not found")
logger.info(f"Document loaded: {document.title}")
# Extract file extension
file_ext = file_name.split('.')[-1].lower() if '.' in file_name else ''
logger.info(f"File extension: {file_ext}")
# Process file content if needed (extract text, etc.)
extracted_text = ""
# Generate next version number
current_version = document.current_version
current_revision = current_version.version_number if current_version else '0.0'
logger.info(f"Current revision: {current_revision}")
try:
major, minor = current_revision.split('.')
next_revision = f"{major}.{int(minor) + 1}"
except (ValueError, AttributeError):
next_revision = "0.1" # Default if parsing fails
logger.info(f"Next revision: {next_revision}")
# Create version UID
version_uid = str(uuid.uuid4())
logger.info(f"Version UID: {version_uid}")
# Determine file path based on custom path or standard structure
# Check if document has a custom path
custom_path = getattr(document, 'custom_path', None)
if custom_path:
# Use custom path structure
document_folder = f"{custom_path}/{document.doc_number}"
filecloudpath = f"{document_folder}/{document.doc_number}_v{next_revision}.{file_ext}"
else:
# Use standard folder structure
department_folder = f"/{settings.FILECLOUD_ROOT_FOLDER}/{document.department}"
doc_type_folder = f"{department_folder}/{document.doc_type}"
document_folder = f"{doc_type_folder}/{document.doc_number}"
filecloudpath = f"{document_folder}/{document.doc_number}_v{next_revision}.{file_ext}"
logger.info(f"FileCloud path: {filecloudpath}")
# Create version properties
version_properties = {
'UID': version_uid,
'created_by_uid': user.uid,
'created_by_name': user.username if hasattr(user, 'username') else 'Unknown',
'created_date': datetime.now().isoformat(),
'version_number': next_revision,
'file_name': file_name,
'file_size': len(file_content) if file_content else 0,
'file_type': file_ext,
'comment': comment,
'fileCloudPath': filecloudpath,
'storage_type': 'FILECLOUD' # Indicate storage type
}
# Add status
version_properties['status'] = 'ACTIVE'
# Sanitize properties before creating node
from CDocs.db.db_operations import _sanitize_properties
sanitized_properties = _sanitize_properties(version_properties)
# Create version node in Neo4j
version_node = db.create_node(
NodeLabels.DOCUMENT_VERSION,
sanitized_properties
)
if not version_node:
logger.error("Failed to create version node")
return None
# Create relationship between document and version
relation_created = db.create_relationship(
document_uid,
version_uid,
RelTypes.HAS_VERSION
)
if not relation_created:
logger.error("Failed to create relationship between document and version")
return None
# Update document revision
doc_update_result = db.run_query(
"""
MATCH (d:ControlledDocument {UID: $doc_uid})
SET d.revision = $revision,
d.modifiedDate = $modified_date
RETURN d
""",
{
"doc_uid": document_uid,
"revision": next_revision,
"modified_date": datetime.now().isoformat()
}
)
# Set as current version if update successful
if doc_update_result:
# Don't use direct property setting - use the document model method
document_obj = ControlledDocument(uid=document_uid)
document_obj.set_current_version(version_uid)
# Now upload the file to FileCloud using the proper controller function
if file_content:
try:
logger.info(f"Uploading file to FileCloud using filecloud_controller")
# Create document object for FileCloud upload
doc_obj = ControlledDocument(uid=document_uid)
# Upload to FileCloud using the proper controller function
filecloud_result = upload_document_to_filecloud(
user=user,
document=doc_obj,
file_content=file_content,
version_comment=comment,
metadata=None
)
if filecloud_result and filecloud_result.get('success'):
logger.info(f"Successfully uploaded to FileCloud")
# Update sharing permissions for document
try:
from CDocs.controllers.share_controller import manage_document_permissions
permission_result = manage_document_permissions(doc_obj)
logger.info(f"Document permissions updated: {permission_result}")
except Exception as perm_error:
logger.warning(f"Error updating document permissions: {perm_error}")
else:
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, just log the error
except Exception as fc_error:
logger.error(f"Error uploading to FileCloud: {fc_error}")
# Don't fail the entire operation, just log the error
else:
logger.warning("No file content provided for FileCloud upload")
# Create audit trail entry
try:
# Use the standard audit_trail.log_document_lifecycle_event function
audit_trail.log_document_lifecycle_event(
event_type="VERSION_CREATED",
user=user,
document_uid=document_uid,
details={
"version_number": next_revision,
"comment": comment,
"file_name": file_name,
"file_size": len(file_content) if file_content else 0,
"file_type": file_ext
}
)
except Exception as audit_err:
logger.warning(f"Error creating audit entry: {audit_err}")
# Ensure folder structure exists before returning
# try:
# from CDocs.controllers.filecloud_controller import ensure_document_folders
# ensure_document_folders(document_uid)
# except Exception as folder_err:
# logger.warning(f"Warning: Could not ensure document folders: {folder_err}")
# Return version properties
return version_properties
except Exception as e:
logger.error(f"Error creating document version: {e}")
import traceback
logger.error(traceback.format_exc())
raise BusinessRuleError(f"Failed to create document version: {e}")
Parameters
| Name | Type | Default | Kind |
|---|---|---|---|
user |
DocUser | - | positional_or_keyword |
document_uid |
str | - | positional_or_keyword |
file_content |
bytes | - | positional_or_keyword |
file_name |
str | - | positional_or_keyword |
comment |
str | '' | positional_or_keyword |
Parameter Details
user: DocUser object representing the authenticated user creating the version. Must have CREATE_VERSION permission. Used for audit trails and permission management.
document_uid: String UUID identifying the existing controlled document. Must correspond to an existing ControlledDocument in the Neo4j database.
file_content: Binary data (bytes) containing the actual file content to be stored. Can be empty/None but will skip FileCloud upload if not provided.
file_name: String name of the file including extension (e.g., 'document.pdf'). Used to extract file type and construct storage paths.
comment: Optional string comment describing this version. Defaults to empty string. Stored in version metadata and audit trail.
Return Value
Type: Dict[str, Any]
Returns a dictionary containing version information with keys: 'UID' (version UUID), 'created_by_uid', 'created_by_name', 'created_date' (ISO format), 'version_number' (e.g., '1.2'), 'file_name', 'file_size' (bytes), 'file_type' (extension), 'comment', 'fileCloudPath' (storage path), 'storage_type' ('FILECLOUD'), and 'status' ('ACTIVE'). Returns None if version node or relationship creation fails. Raises BusinessRuleError on exceptions.
Dependencies
logginguuiddatetimeCDocs.dbCDocs.config.settingsCDocs.models.document.ControlledDocumentCDocs.models.user_extensions.DocUserCDocs.utils.audit_trailCDocs.db.schema_manager.NodeLabelsCDocs.db.schema_manager.RelTypesCDocs.db.db_operations._sanitize_propertiesCDocs.controllers.filecloud_controller.upload_document_to_filecloudCDocs.controllers.share_controller.manage_document_permissionsCDocs.controllers.ResourceNotFoundErrorCDocs.controllers.BusinessRuleErrortraceback
Required Imports
import logging
import uuid
from datetime import datetime
from typing import Dict, Any
from CDocs import db
from CDocs.config import settings
from CDocs.models.document import ControlledDocument
from CDocs.models.user_extensions import DocUser
from CDocs.utils import audit_trail
from CDocs.db.schema_manager import NodeLabels, RelTypes
from CDocs.db.db_operations import _sanitize_properties
from CDocs.controllers.filecloud_controller import upload_document_to_filecloud
from CDocs.controllers import ResourceNotFoundError, BusinessRuleError
Conditional/Optional Imports
These imports are only needed under specific conditions:
from CDocs.controllers.share_controller import manage_document_permissions
Condition: only if file_content is provided and FileCloud upload succeeds - used to update document sharing permissions
Optionalimport traceback
Condition: used for detailed error logging when exceptions occur
Required (conditional)Usage Example
from CDocs.models.user_extensions import DocUser
from CDocs.controllers.document_controller import create_document_version
# Assume user and document_uid are already available
user = DocUser(uid='user-123')
document_uid = 'doc-456'
# Read file content
with open('updated_document.pdf', 'rb') as f:
file_content = f.read()
# Create new version
version_info = create_document_version(
user=user,
document_uid=document_uid,
file_content=file_content,
file_name='updated_document.pdf',
comment='Updated section 3 with new compliance requirements'
)
if version_info:
print(f"Created version {version_info['version_number']}")
print(f"Version UID: {version_info['UID']}")
print(f"FileCloud path: {version_info['fileCloudPath']}")
else:
print('Failed to create version')
Best Practices
- Ensure the document exists before calling this function - it will raise ResourceNotFoundError if document_uid is invalid
- The function requires CREATE_VERSION permission enforced by decorator - ensure user has proper permissions
- File content can be None/empty but FileCloud upload will be skipped - version metadata will still be created
- Version numbering follows major.minor format (e.g., 1.2) and automatically increments minor version
- The function is wrapped in a transaction decorator - database changes will be rolled back on failure
- FileCloud upload failures are logged but don't fail the entire operation - check logs for upload issues
- Document permissions are automatically updated after successful FileCloud upload
- Audit trail entries are created automatically - failures are logged as warnings but don't stop execution
- Custom paths take precedence over standard folder structure (department/doc_type/doc_number)
- File extensions are extracted from file_name and used for storage path construction
- The function sanitizes properties before creating Neo4j nodes to prevent injection issues
- Always handle BusinessRuleError exceptions when calling this function
- The returned dictionary contains all version metadata needed for UI display and further operations
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
function create_document_v1 95.9% similar
-
function upload_document_to_filecloud 81.3% similar
-
function download_document_version 75.4% similar
-
function download_document_version_v1 75.1% similar
-
function get_document_metadata_from_filecloud 73.2% similar