function upload_document_to_filecloud
Uploads a document version to FileCloud storage system with metadata, handling file creation, folder structure, and audit logging.
/tf/active/vicechatdev/CDocs/controllers/filecloud_controller.py
320 - 463
complex
Purpose
This function manages the complete workflow of uploading controlled documents to FileCloud, including document validation, folder structure creation, file upload, metadata attachment, and audit trail logging. It supports both new uploads and version updates, automatically handling metadata conversion and temporary file management. The function is designed for document management systems requiring version control and permission-based access.
Source Code
def upload_document_to_filecloud(
user: DocUser,
document: Union[ControlledDocument, str],
file_content: bytes,
file_path: Optional[str] = None,
version_comment: Optional[str] = None,
metadata: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""
Upload a document version to FileCloud.
Args:
user: User performing the upload
document: ControlledDocument instance or document UID
file_content: Binary content of the file
version_comment: Optional comment for version history
metadata: Optional metadata to attach to the file
Returns:
Dictionary with upload results
Raises:
ResourceNotFoundError: If document not found
PermissionError: If user doesn't have permission
FileCloudError: If upload fails
"""
# Get document instance if UID provided
if isinstance(document, str):
doc = ControlledDocument(uid=document)
if not doc:
raise ResourceNotFoundError(f"Document not found: {document}")
else:
doc = document
# Get current version to determine file details
version = doc.current_version
if not version:
raise ResourceNotFoundError(f"No current version found for document {doc.uid}")
# Ensure folder structure exists
ensure_document_folders(doc)
# Determine file path in FileCloud
if not file_path:
file_path = get_filecloud_document_path(doc)
folder_path, filename = os.path.split(file_path)
logger.info(f"Uploading file to FileCloud: {file_path}")
# Prepare metadata if not provided
if metadata is None:
metadata = {
"cdoc_uid": doc.uid,
"doc_number": doc.doc_number,
"doc_uid": version.uid,
"version_number": version.version_number, # Convert to string
"doc_type": doc.doc_type,
"department": doc.department,
"is_cdoc": "true", # Use string instead of boolean
"status": doc.status,
"owner": doc.owner.name if doc.owner else "Unknown"
}
else:
# Convert any non-string values to strings
for key, value in metadata.items():
if value is None:
metadata[key] = ""
elif not isinstance(value, str):
if isinstance(value, bool):
metadata[key] = "true" if value else "false"
else:
metadata[key] = str(value)
# Create a temporary file for upload
with tempfile.NamedTemporaryFile(suffix='.'+version.file_type, delete=False) as temp_file:
temp_path = temp_file.name
temp_file.write(file_content)
try:
client = get_filecloud_client()
# Upload the file
result = client.upload_file(
local_file_path=temp_path,
remote_path=folder_path,
filename=filename,
overwrite=True
)
if not result.get('success', False):
logger.error(f"Failed to upload file to FileCloud: {result.get('message', 'Unknown error')}")
raise FileCloudError(f"Failed to upload file: {result.get('message', 'Unknown error')}")
# Add metadata to file
catalog = MetadataCatalog(client, debug=True) # Enable debug mode
catalog.initialize()
# # First add the metadata set to the file
# set_result = catalog.add_set_to_file_object(file_path, set_name="CDocs")
# if not set_result.get('success', False):
# logger.warning(f"Failed to add metadata set to file: {set_result.get('message', 'Unknown error')}")
# # Continue despite this error - the set might already be attached
# # Wait briefly after adding the set
# time.sleep(1)
# Then save attribute values
metadata_result = catalog.save_attribute_values_by_name(file_path, "CDocs", metadata)
if not metadata_result.get('success', False):
logger.warning(f"Failed to set metadata on file: {metadata_result.get('message', 'Unknown error')}")
logger.debug(f"Metadata that failed: {metadata}")
logger.debug(f"Raw response: {metadata_result.get('raw_response')}")
# Log audit event
audit_trail.log_version_event(
event_type="VERSION_UPDATED" if version_comment else "VERSION_CREATED",
user=user,
version_uid=version.uid,
details={
"file_path": file_path,
"comment": version_comment,
"metadata": metadata
}
)
return {
"success": True,
"document_uid": doc.uid,
"version_uid": version.uid,
"file_path": file_path,
"metadata": metadata
}
except FileCloudError:
raise
except Exception as e:
logger.error(f"Error uploading document to FileCloud: {e}")
raise FileCloudError(f"Error uploading document: {e}")
finally:
# Clean up temporary file
try:
os.unlink(temp_path)
except:
pass
Parameters
| Name | Type | Default | Kind |
|---|---|---|---|
user |
DocUser | - | positional_or_keyword |
document |
Union[ControlledDocument, str] | - | positional_or_keyword |
file_content |
bytes | - | positional_or_keyword |
file_path |
Optional[str] | None | positional_or_keyword |
version_comment |
Optional[str] | None | positional_or_keyword |
metadata |
Optional[Dict[str, Any]] | None | positional_or_keyword |
Parameter Details
user: DocUser instance representing the authenticated user performing the upload. Used for permission checks and audit logging.
document: Either a ControlledDocument instance or a string UID of the document. If a UID string is provided, the function will retrieve the document instance. Must reference an existing document with a current version.
file_content: Binary content (bytes) of the file to be uploaded. This is the actual file data that will be written to FileCloud.
file_path: Optional string specifying the exact path in FileCloud where the file should be stored. If None, the path is automatically determined using get_filecloud_document_path(). Should include folder path and filename.
version_comment: Optional string comment describing this version upload. Used for version history tracking and determines whether the audit event is logged as VERSION_UPDATED or VERSION_CREATED.
metadata: Optional dictionary containing custom metadata key-value pairs to attach to the file in FileCloud. If None, default metadata is generated from document properties (uid, doc_number, version_number, doc_type, department, status, owner). All values are converted to strings, with booleans converted to 'true'/'false'.
Return Value
Type: Dict[str, Any]
Returns a dictionary with keys: 'success' (bool, always True if no exception), 'document_uid' (str, UID of the document), 'version_uid' (str, UID of the document version), 'file_path' (str, full path where file was uploaded in FileCloud), 'metadata' (dict, the metadata that was attached to the file). If the function fails, it raises an exception rather than returning a failure status.
Dependencies
loggingosjsonuuidtempfiletypingtimeCDocs
Required Imports
import logging
import os
import tempfile
import time
from typing import Dict, List, Any, Optional, Union
from CDocs.models.document import ControlledDocument, DocumentVersion
from CDocs.models.user_extensions import DocUser
from CDocs.utils import audit_trail
from CDocs.controllers import require_permission, log_controller_action, ResourceNotFoundError
from CDocs.utils.metadata_catalog import MetadataCatalog
from CDocs.utils.FC_api import FileCloudAPI
Usage Example
# Upload a new document version
from CDocs.models.user_extensions import DocUser
from CDocs.models.document import ControlledDocument
# Get user and document
user = DocUser.query.get(user_id)
document = ControlledDocument.query.filter_by(uid='DOC-12345').first()
# Read file content
with open('path/to/document.pdf', 'rb') as f:
file_content = f.read()
# Upload with custom metadata
result = upload_document_to_filecloud(
user=user,
document=document,
file_content=file_content,
version_comment='Updated with latest revisions',
metadata={
'custom_field': 'custom_value',
'reviewed_by': 'John Doe'
}
)
print(f"Uploaded to: {result['file_path']}")
print(f"Version UID: {result['version_uid']}")
# Or upload using document UID string
result = upload_document_to_filecloud(
user=user,
document='DOC-12345',
file_content=file_content
)
Best Practices
- Ensure the user has both 'UPLOAD_DOCUMENT' and 'EDIT_DOCUMENT' permissions before calling this function (enforced by decorator)
- The function automatically cleans up temporary files, but ensure proper exception handling in calling code
- Metadata values are automatically converted to strings; booleans become 'true'/'false' strings
- The document must have a current_version set before upload, otherwise ResourceNotFoundError is raised
- File paths in FileCloud are automatically determined if not provided, following the system's folder structure conventions
- The function uses overwrite=True when uploading, so existing files at the same path will be replaced
- Audit events are automatically logged for all uploads; use version_comment to distinguish between updates and new versions
- If metadata attachment fails, the function logs a warning but continues (file is still uploaded)
- Always pass file_content as bytes, not as a file object or string
- The function is decorated with log_controller_action for automatic action logging
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
function create_document_v2 81.3% similar
-
function update_document_metadata_in_filecloud 81.1% similar
-
function create_document_v1 79.5% similar
-
function get_document_metadata_from_filecloud 78.3% similar
-
function import_document_from_filecloud 77.6% similar