function update_document_v1
Updates a controlled document's properties (title, description, status, owner, metadata) with validation, audit logging, and special handling for status transitions that require PDF publishing or training resets.
/tf/active/vicechatdev/CDocs/controllers/document_controller.py
2503 - 2665
complex
Purpose
This function serves as the primary controller for updating document properties in a document management system. It handles complex business logic including: validating status transitions, triggering PDF publishing when moving to published status, resetting training assignments when documents are published or made effective, maintaining audit trails, and notifying stakeholders of changes. It enforces permissions and business rules while coordinating with multiple subsystems (FileCloud, notifications, training, audit).
Source Code
def update_document(
user: DocUser,
document_uid: str,
**kwargs
) -> Dict[str, Any]:
"""
Update a document's properties, handling format transitions when status changes.
Args:
user: User making the update
document_uid: UID of document to update
title: New document title (optional)
description: New document description (optional)
status: New document status (optional)
owner_uid: New document owner UID (optional)
metadata: New document metadata (optional)
Returns:
Dictionary with update status
"""
try:
# Get document instance
document = ControlledDocument(uid=document_uid)
if not document:
raise ResourceNotFoundError(f"Document not found: {document_uid}")
# Store original values for audit
original_values = {
'title': document.title,
'description': document.description,
'status': document.status,
'owner_uid': document.owner_uid
}
# Track what changed
changes = {}
# Update title if provided
if 'title' in kwargs and kwargs['title'] is not None and kwargs['title'] != document.title:
document.title = kwargs['title']
changes['title'] = {'old': original_values['title'], 'new': kwargs['title']}
# Update description if provided
if 'description' in kwargs and kwargs['description'] is not None and kwargs['description'] != document.description:
document.description = kwargs['description']
changes['description'] = {'old': original_values['description'], 'new': kwargs['description']}
# Handle status update with special consideration for format transitions
if 'status' in kwargs and kwargs['status'] is not None and kwargs['status'] != document.status:
current_status = document.status
target_status = kwargs['status']
# Check if the transition is valid
if not document.can_transition_to(target_status):
raise BusinessRuleError(f"Invalid status transition from {current_status} to {target_status}")
# Check if we're transitioning from editable to non-editable status
if is_editable_status(current_status) and is_published_status(target_status):
# Status requires PDF publishing
if target_status == STATUS_PUBLISHED:
# Call publish_document instead
result = publish_document(
user=user,
document_uid=document_uid,
publish_comment=kwargs.get('comment', 'Status updated to Published')
)
return result
if is_editable_status(current_status) and not is_editable_status(target_status):
# For other non-editable statuses, check if PDF exists
current_version = document.current_version
if current_version and not current_version.pdf_file_path:
raise BusinessRuleError(f"Document must be published before changing to {target_status}")
# For transitions that don't require special handling, update status normally
document.status = target_status
changes['status'] = {'old': current_status, 'new': target_status}
# Log status change
audit_trail.log_document_lifecycle_event(
event_type="DOCUMENT_STATUS_CHANGED",
user=user,
document_uid=document_uid,
details={
'old_status': current_status,
'new_status': target_status,
'comment': kwargs.get('comment') if kwargs else None
}
)
# Update owner if provided
if 'owner_uid' in kwargs and kwargs['owner_uid'] is not None and kwargs['owner_uid'] != document.owner_uid:
# Check if new owner exists
owner_exists = db.run_query(
"MATCH (u:User {UID: $uid}) RETURN COUNT(u) > 0 as exists",
{"uid": kwargs['owner_uid']}
)
if not owner_exists or not owner_exists[0].get('exists', False):
raise ValidationError(f"User not found: {kwargs['owner_uid']}")
# Update owner
document.owner = kwargs['owner_uid']
changes['owner_uid'] = {'old': original_values['owner_uid'], 'new': kwargs['owner_uid']}
# Update metadata if provided
if 'metadata' in kwargs and kwargs['metadata'] is not None:
metadata = kwargs['metadata']
# Merge with existing metadata if any
existing_metadata = getattr(document, 'metadata', {}) or {}
updated_metadata = {**existing_metadata, **metadata}
# Update in database
db.update_node(document_uid, {'metadata': updated_metadata})
changes['metadata'] = {'updated': True}
# If status is being changed to PUBLISHED or EFFECTIVE, reset training
if 'status' in kwargs:
new_status = kwargs['status']
if new_status in [STATUS_PUBLISHED, STATUS_EFFECTIVE]:
try:
# Reset training for this document
document.reset_training_on_publish()
# Log training reset
audit_trail.log_event(
event_type="TRAINING_RESET_ON_PUBLISH",
user=user,
resource_uid=document_uid,
resource_type="ControlledDocument",
details={
"doc_number": document.doc_number,
"new_status": new_status
}
)
# Notify users with training assignments
from CDocs.controllers.training_controller import reset_document_training
reset_document_training(user, document_uid)
except Exception as training_err:
logger.warning(f"Error resetting training on publish: {training_err}")
# Don't fail the document update if training reset fails
# Notify about document update if significant changes were made
if changes:
notifications.notify_document_update(document, "DOCUMENT_UPDATED")
# Return success response with changes
return {
"success": True,
"message": "Document updated successfully",
"document_uid": document_uid,
"changes": changes
}
except (ResourceNotFoundError, ValidationError, PermissionError, BusinessRuleError) as e:
# Re-raise known errors
raise
except Exception as e:
logger.error(f"Error updating document: {e}")
raise BusinessRuleError(f"Failed to update document: {e}")
Parameters
| Name | Type | Default | Kind |
|---|---|---|---|
user |
DocUser | - | positional_or_keyword |
document_uid |
str | - | positional_or_keyword |
**kwargs |
- | - | var_keyword |
Parameter Details
user: DocUser object representing the authenticated user making the update. Used for permission checks, audit logging, and notification context.
document_uid: String UID (unique identifier) of the document to update. Must correspond to an existing ControlledDocument in the database.
kwargs: Variable keyword arguments containing optional update fields: 'title' (str) - new document title, 'description' (str) - new document description, 'status' (str) - new document status (must be valid transition), 'owner_uid' (str) - UID of new document owner (must exist), 'metadata' (dict) - metadata to merge with existing metadata, 'comment' (str) - optional comment for status changes
Return Value
Type: Dict[str, Any]
Returns a dictionary with keys: 'success' (bool) - True if update succeeded, 'message' (str) - human-readable success message, 'document_uid' (str) - UID of updated document, 'changes' (dict) - detailed change log where each key is a field name and value is a dict with 'old' and 'new' values (or 'updated': True for metadata). May also return result from publish_document if status transition triggers publishing.
Dependencies
logginguuidostempfiletypingdatetimeiopanelshutiltracebackjsonrerandom
Required Imports
from CDocs import db
from CDocs.config import settings
from CDocs.config import permissions
from CDocs.models.document import ControlledDocument
from CDocs.models.user_extensions import DocUser
from CDocs.utils import notifications
from CDocs.utils import audit_trail
from CDocs.controllers import require_permission
from CDocs.controllers import log_controller_action
from CDocs.controllers import transaction
from CDocs.controllers import PermissionError
from CDocs.controllers import ResourceNotFoundError
from CDocs.controllers import ValidationError
from CDocs.controllers import BusinessRuleError
from CDocs.models.document_status import is_editable_status
from CDocs.models.document_status import is_published_status
from CDocs.models.document_status import STATUS_PUBLISHED
from CDocs.models.document_status import STATUS_EFFECTIVE
import logging
Conditional/Optional Imports
These imports are only needed under specific conditions:
from CDocs.controllers.training_controller import reset_document_training
Condition: only when status changes to PUBLISHED or EFFECTIVE and training reset is triggered
Required (conditional)from CDocs.controllers.document_controller import publish_document
Condition: only when status transition requires publishing (transitioning to STATUS_PUBLISHED)
Required (conditional)Usage Example
from CDocs.models.user_extensions import DocUser
from CDocs.controllers.document_controller import update_document
# Get authenticated user
user = DocUser(uid='user123')
# Update document title and description
result = update_document(
user=user,
document_uid='doc-456',
title='Updated Document Title',
description='New description for the document',
comment='Updated metadata'
)
if result['success']:
print(f"Document updated: {result['changes']}")
# Update document status (triggers special handling)
result = update_document(
user=user,
document_uid='doc-456',
status='PUBLISHED',
comment='Publishing document for release'
)
# Update owner and metadata
result = update_document(
user=user,
document_uid='doc-456',
owner_uid='newowner789',
metadata={'department': 'Engineering', 'project': 'Alpha'}
)
Best Practices
- Always wrap calls in try-except blocks to handle ResourceNotFoundError, ValidationError, PermissionError, and BusinessRuleError
- Ensure user has EDIT_DOCUMENT permission before calling (decorator enforces this)
- Only pass kwargs for fields you want to update; omitted fields remain unchanged
- When changing status, verify the transition is valid for your document's current state
- Use the 'comment' kwarg when changing status to provide audit context
- Be aware that status changes to PUBLISHED trigger publish_document which may have side effects (PDF generation, FileCloud upload)
- Status changes to PUBLISHED or EFFECTIVE reset training assignments for all users
- Metadata updates are merged with existing metadata, not replaced
- The function runs within a transaction decorator, so all changes are atomic
- Check the 'changes' dict in the return value to see what actually changed
- Owner changes validate that the new owner exists in the system
- Training reset failures are logged but don't fail the document update
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
function update_document 96.3% similar
-
function update_status_after_approval 75.5% similar
-
function update_document_metadata_in_filecloud 71.8% similar
-
function publish_document_v1 70.9% similar
-
function create_document_v3 70.6% similar