class ControlledDocument
Model representing a controlled document.
/tf/active/vicechatdev/CDocs/models/document.py
700 - 1639
moderate
Purpose
Model representing a controlled document.
Source Code
class ControlledDocument(BaseModel):
"""Model representing a controlled document."""
def __init__(self, data: Optional[Dict[str, Any]] = None, uid: Optional[str] = None):
"""
Initialize a controlled document.
Args:
data: Dictionary of document properties
uid: Document UID to load from database (if data not provided)
"""
if data is None and uid is not None:
# Fetch document data from database
data = db.get_node_by_uid(uid)
super().__init__(data or {})
self._versions_cache = None
@classmethod
def create(cls, title, doc_type, department, owner, doc_number=None, properties=None):
"""
Create a new controlled document.
Args:
title: Document title
doc_type: Document type name or code
department: Department name or code
owner: Owner User object or UID
properties: Additional properties
Returns:
New ControlledDocument instance or None if creation failed
"""
try:
# Convert doc_type and department to codes if they're full names
doc_type_code = settings.get_document_type_code(doc_type)
dept_code = settings.get_department_code(department)
# Generate document number
if not doc_number:
doc_number = settings.generate_document_number(doc_type_code, dept_code)
# Prepare properties
props = properties or {}
props.update({
'title': title,
'docType': doc_type_code,
'department': dept_code,
'docNumber': doc_number,
'status': 'DRAFT',
'createdDate': datetime.now(),
'ownerUID': owner.uid if isinstance(owner, DocUser) else owner
})
# Get CDocs root node
cdocs_node = db.run_query("MATCH (c:CDocs) RETURN c LIMIT 1")
if not cdocs_node or 'c' not in cdocs_node[0]:
logger.error("CDocs root node not found")
return None
cdocs_uid = cdocs_node[0]['c']['UID']
# Create node in database
doc_data = db.create_node_with_relationship(
label=NodeLabels.CONTROLLED_DOCUMENT,
properties=props,
to_node_uid=cdocs_uid,
relationship_type='HAS_DOCUMENT',
relationship_direction='INCOMING',
relationship_properties=None
)
if not doc_data:
logger.error("Failed to create controlled document")
return None
# Create the document instance
document = cls(doc_data)
return document
except Exception as e:
logger.error(f"Error creating controlled document: {e}")
return None
@property
def title(self) -> str:
"""Get document title."""
return self._data.get('title', '')
@title.setter
def title(self, value: str) -> None:
"""Set document title."""
self._data['title'] = value
db.update_node(self.uid, {'title': value})
@property
def custom_path(self) -> Optional[str]:
"""Get custom storage path for this document."""
return self._data.get('custom_path',None)
@custom_path.setter
def custom_path(self, path: str) -> None:
"""Set custom storage path for this document."""
self._data['custom_path'] = path
db.update_node(self.uid, {'custom_path': path})
@property
def description(self) -> str:
"""
Get the document description.
Returns:
Document description or empty string if not set
"""
return self._data.get('description', '')
@description.setter
def description(self, value: str) -> None:
"""
Set the document description.
Args:
value: New description
"""
self._data['description'] = value
db.update_node(self.uid, {'description': value})
@property
def doc_number(self) -> str:
"""Get document number."""
return self._data.get('docNumber', '')
@property
def doc_type(self) -> str:
"""Get document type code."""
return self._data.get('docType', '')
@property
def UID(self) -> str:
"""Get document UID."""
return self._data.get('UID', '')
@property
def doc_type_name(self) -> str:
"""Get document type name."""
return settings.get_document_type_name(self.doc_type)
@property
def department(self) -> str:
"""Get department code."""
return self._data.get('department', '')
@property
def status(self) -> str:
"""Get document status code."""
return self._data.get('status', 'DRAFT')
@status.setter
def status(self, value: str) -> None:
"""Set document status using code."""
# Convert from name to code if needed
status_code = value
# Check for valid transitions if there's an existing status
current_status = self._data.get('status', '')
if current_status and not self.can_transition_to(status_code):
logger.warning(f"Invalid status transition from {current_status} to {status_code}")
return
# Check if the status is a valid document status
if status_code not in [
STATUS_DRAFT, STATUS_IN_REVIEW, STATUS_IN_APPROVAL,
STATUS_APPROVED, STATUS_PUBLISHED, STATUS_EFFECTIVE,
STATUS_ARCHIVED, STATUS_OBSOLETE
]:
logger.warning(f"Invalid status: {value}")
return
self._data['status'] = status_code
db.update_node(self.uid, {'status': status_code})
def get_status_name(self) -> str:
"""Get the full status name."""
return settings.get_document_status_name(self.status)
def get_status_color(self) -> str:
"""Get the color for displaying this status."""
return settings.get_status_color(self.status)
@property
def created_date(self) -> Optional[datetime]:
"""Get document creation date."""
return self._data.get('createdDate')
@property
def owner_uid(self) -> Optional[str]:
"""Get owner UID."""
return self._data.get('ownerUID')
@property
def authors(self) -> List[DocUser]:
"""Get authors of the document."""
result = db.run_query(
"""
MATCH (d:ControlledDocument {UID: $uid})-[:AUTHORED_BY]->(u:User)
RETURN u
""",
{"uid": self.uid}
)
if not result:
# If no authors found, add the document owner as a default author
owner = self.owner
if owner:
return [owner]
return []
return [DocUser(record['u']) for record in result if 'u' in record]
@property
def owner(self) -> Optional[DocUser]:
"""Get document owner."""
owner_uid = self.owner_uid
if owner_uid:
return DocUser(uid=owner_uid)
return None
@owner.setter
def owner(self, user: Union[DocUser, str]) -> None:
"""Set document owner."""
uid = user.uid if isinstance(user, DocUser) else user
self._data['ownerUID'] = uid
db.update_node(self.uid, {'ownerUID': uid})
def create_version(self, version_number: str,
created_by: Union[DocUser, str],
file_paths: Optional[Dict[str, str]] = None,
properties: Optional[Dict[str, Any]] = None) -> Optional[DocumentVersion]:
"""
Create a new version of this document.
Args:
version_number: Version number string (e.g., "1.0")
created_by: User creating the version or their UID
file_paths: Dictionary containing Word and PDF file paths in FileCloud
properties: Additional properties for the version
Returns:
New DocumentVersion instance or None if creation failed
"""
version = DocumentVersion.create(self.uid, version_number, created_by, file_paths, properties)
if version:
# Update document status if it's the first version
if self.status == 'DRAFT' and not self.versions:
self.set_current_version(version)
# Clear versions cache
self._versions_cache = None
return version
@property
def versions(self) -> List[DocumentVersion]:
"""Get all versions of this document."""
if self._versions_cache is not None:
return self._versions_cache
result = db.run_query(
"""
MATCH (d:ControlledDocument {UID: $uid})-[:HAS_VERSION]->(v:DocumentVersion)
RETURN v.UID as uid
ORDER BY v.version_number DESC
""",
{"uid": self.uid}
)
versions = [DocumentVersion(record['uid']) for record in result]
self._versions_cache = versions
return versions
def get_all_versions(self) -> List[DocumentVersion]:
"""
Get all versions of this document.
Returns:
List of DocumentVersion instances sorted by version number (newest first)
"""
# This is essentially the same as the versions property but with a different name
# for API compatibility
if self._versions_cache is not None:
return self._versions_cache
result = db.run_query(
"""
MATCH (d:ControlledDocument {UID: $uid})-[:HAS_VERSION]->(v:DocumentVersion)
RETURN v
ORDER BY v.version_number DESC
""",
{"uid": self.uid}
)
versions = [DocumentVersion(record['v']) for record in result]
self._versions_cache = versions
return versions
@property
def current_version(self) -> Optional[DocumentVersion]:
"""Get current version of the document."""
result = db.run_query(
"""
MATCH (d:ControlledDocument {UID: $uid})-[:CURRENT_VERSION]->(v:DocumentVersion)
RETURN v.UID as uid
""",
{"uid": self.uid}
)
if result and 'uid' in result[0]:
return DocumentVersion(uid=result[0]['uid'])
return None
def set_current_version(self, version: Union[DocumentVersion, str]) -> bool:
"""
Set the current version of the document.
Args:
version: DocumentVersion instance or UID
Returns:
Boolean indicating success
"""
try:
version_uid = version.uid if isinstance(version, DocumentVersion) else version
# Remove existing CURRENT_VERSION relationships
db.run_query(
"""
MATCH (d:ControlledDocument {UID: $uid})-[r:CURRENT_VERSION]->()
DELETE r
""",
{"uid": self.uid}
)
# Create new CURRENT_VERSION relationship
success = db.create_relationship(
self.uid,
version_uid,
RelTypes.CURRENT_VERSION
)
return success
except Exception as e:
logger.error(f"Error setting current version: {e}")
return False
def is_current_version(self, version_uid: str) -> bool:
"""
Check if the given version UID is the current version of the document.
Args:
version_uid: UID of the version to check
Returns:
True if it's the current version, False otherwise
"""
# Check current version without having to load the entire version object
result = db.run_query(
"""
MATCH (d:ControlledDocument {UID: $doc_uid})-[:CURRENT_VERSION]->(v:DocumentVersion {UID: $version_uid})
RETURN COUNT(v) > 0 as is_current
""",
{"doc_uid": self.uid, "version_uid": version_uid}
)
if result and 'is_current' in result[0]:
return result[0]['is_current']
return False
def get_document_type_details(self) -> Dict[str, Any]:
"""
Get details about the document type.
Returns:
Dictionary with document type information
"""
return settings.DOCUMENT_CONFIG.get(self.doc_type, {})
def get_department_name(self) -> str:
"""
Get the full department name.
Returns:
Department name
"""
return settings.get_department_name(self.department)
def get_version_by_number(self, version_number: str) -> Optional[DocumentVersion]:
"""
Get a specific version of the document by version number.
Args:
version_number: Version number string (e.g., "1.0")
Returns:
DocumentVersion instance or None if not found
"""
try:
# Query for version with the given number
result = db.run_query(
"""
MATCH (d:ControlledDocument {UID: $doc_uid})-[:HAS_VERSION]->(v:DocumentVersion)
WHERE v.version_number = $version_number
RETURN v.UID as uid
LIMIT 1
""",
{"doc_uid": self.uid, "version_number": version_number}
)
if result and 'uid' in result[0]:
return DocumentVersion(uid=result[0]['uid'])
return None
except Exception as e:
logger.error(f"Error getting document version by number: {e}")
return None
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary representation."""
result = super().to_dict()
# Add document type details
doc_type_details = self.get_document_type_details()
if doc_type_details:
result['docTypeName'] = doc_type_details.get('name', '')
# Add department name
result['departmentName'] = self.get_department_name()
# Add status details
try:
from ..config import settings
# Check if DOCUMENT_STATUSES is a dictionary before using .get()
if hasattr(settings, 'DOCUMENT_STATUSES') and isinstance(settings.DOCUMENT_STATUSES, dict):
status_details = settings.DOCUMENT_STATUSES.get(self.status, {})
if status_details:
result['statusName'] = status_details.get('name', self.status)
result['statusColor'] = status_details.get('color', '#000000')
result['statusIcon'] = status_details.get('icon', 'document')
else:
# Fallback if DOCUMENT_STATUSES is not a dict or doesn't exist
result['statusName'] = self.status
result['statusColor'] = '#6c757d' # Default gray
result['statusIcon'] = 'document' # Default icon
except Exception as e:
# Fallback if there's any error accessing status details
result['statusName'] = self.status
result['statusColor'] = '#6c757d' # Default gray
result['statusIcon'] = 'document' # Default icon
# Add owner information if available
owner = self.owner
if owner:
# Add owner details
pass
# Add current version information if available
current = self.current_version
if current:
# Add current version details
pass
return result
@classmethod
def get_documents(cls, filters: Optional[Dict[str, Any]] = None,
limit: int = 100, skip: int = 0,
sort_by: str = 'docNumber', descending: bool = False) -> List['ControlledDocument']:
"""
Get controlled documents with optional filtering.
Args:
filters: Optional filter criteria
limit: Maximum number of documents to return
skip: Number of documents to skip (for pagination)
sort_by: Field to sort by
descending: Whether to sort in descending order
Returns:
List of ControlledDocument instances
"""
try:
# Start building query
query = "MATCH (d:ControlledDocument) "
params = {}
# Add filters if provided
if filters:
conditions = []
for key, value in filters.items():
if key == 'search':
# Handle search across multiple fields
conditions.append("(d.title CONTAINS $search OR d.docNumber CONTAINS $search)")
params['search'] = value
elif value is not None:
conditions.append(f"d.{key} = ${key}")
params[key] = value
if conditions:
query += "WHERE " + " AND ".join(conditions) + " "
# Add sorting
direction = "DESC" if descending else "ASC"
query += f"RETURN d ORDER BY d.{sort_by} {direction} SKIP {skip} LIMIT {limit}"
# Execute query
result = db.run_query(query, params)
# Create document instances
documents = [cls(record['d']) for record in result if 'd' in record]
return documents
except Exception as e:
logger.error(f"Error getting documents: {e}")
return []
@classmethod
def count_documents(cls, filters: Optional[Dict[str, Any]] = None) -> int:
"""
Count controlled documents with optional filtering.
Args:
filters: Optional filter criteria
Returns:
Number of matching documents
"""
try:
# Start building query
query = "MATCH (d:ControlledDocument) "
params = {}
# Add filters if provided
if filters:
conditions = []
for key, value in filters.items():
if key == 'search':
# Handle search across multiple fields
conditions.append("(d.title CONTAINS $search OR d.docNumber CONTAINS $search)")
params['search'] = value
elif value is not None:
conditions.append(f"d.{key} = ${key}")
params[key] = value
if conditions:
query += "WHERE " + " AND ".join(conditions) + " "
query += "RETURN count(d) as count"
# Execute query
result = db.run_query(query, params)
if result and 'count' in result[0]:
return result[0]['count']
return 0
except Exception as e:
logger.error(f"Error counting documents: {e}")
return 0
def is_editable(self) -> bool:
"""
Check if this document is in an editable state.
Returns:
bool: True if the document can be edited
"""
return is_editable_status(self.status)
def is_published(self) -> bool:
"""
Check if this document is in a published/non-editable state.
Returns:
bool: True if the document is published/non-editable
"""
return is_published_status(self.status)
def get_primary_file_path(self) -> Optional[str]:
"""
Get the primary file path based on document status.
For published/non-editable documents, returns PDF path.
For editable documents, returns editable document path.
Returns:
str: Path to the primary file or None if no files are available
"""
if not self.current_version:
return None
if self.is_published():
filepath = self.current_version.pdf_file_path
else:
filepath = self.current_version.word_file_path
filename = os.path.basename(filepath)
# Escape spaces in filename with + for the first part
encoded_filename = filename.replace(' ', '+')
# Encode path for the second part (after #expl-tabl.)
# Extract directory path without filename
directory_path = os.path.dirname(filepath)
# Ensure path ends with '/'
if directory_path and not directory_path.endswith('/'):
directory_path += '/'
encoded_path = f"/SHARED/vicebio_shares/{directory_path}"
encoded_path = encoded_path.replace(' ', '%20')
# Construct the full URL
file_url = f"https://filecloud.vicebio.com/ui/core/index.html?filter={encoded_filename}#expl-tabl.{encoded_path}"
return file_url
def get_secondary_file_path(self) -> Optional[str]:
"""
Get the secondary file path based on document status.
For published/non-editable documents, returns the editable document path if available.
For editable documents, returns PDF path if available.
Returns:
str: Path to the secondary file or None if no secondary file is available
"""
if not self.current_version:
return None
if self.is_published():
filepath = self.current_version.word_file_path
else:
filepath = self.current_version.pdf_file_path
# Return None if filepath is not set
if not filepath:
return None
filename = os.path.basename(filepath)
# Escape spaces in filename with + for the first part
encoded_filename = filename.replace(' ', '+')
# Encode path for the second part (after #expl-tabl.)
# Extract directory path without filename
directory_path = os.path.dirname(filepath)
# Ensure path ends with '/'
if directory_path and not directory_path.endswith('/'):
directory_path += '/'
encoded_path = f"/SHARED/vicebio_shares/{directory_path}"
encoded_path = encoded_path.replace(' ', '%20')
# Construct the full URL
file_url = f"https://filecloud.vicebio.com/ui/core/index.html?filter={encoded_filename}#expl-tabl.{encoded_path}"
return file_url
def get_approvals(self) -> List[Dict[str, Any]]:
"""
Get all approval workflows for this document.
Returns:
List of approval data dictionaries
"""
result = db.run_query(
"""
MATCH (d:ControlledDocument {UID: $uid})-[:HAS_VERSION]->(v:DocumentVersion)
OPTIONAL MATCH (v)-[:FOR_APPROVAL]->(a:Approval)
RETURN a
ORDER BY a.createdDate DESC
""",
{"uid": self.uid}
)
if not result:
return []
return [record['a'] for record in result if 'a' in record]
def get_active_approval(self) -> Optional[Dict[str, Any]]:
"""
Get the active approval workflow for this document.
Returns:
Approval data dictionary or None if no active approval
"""
result = db.run_query(
"""
MATCH (d:ControlledDocument {UID: $uid})-[:HAS_VERSION]->(v:DocumentVersion)
OPTIONAL MATCH (v)-[:FOR_APPROVAL]->(a:Approval)
WHERE a.status IN ['PENDING', 'IN_PROGRESS']
RETURN a
ORDER BY a.createdDate DESC
LIMIT 1
""",
{"uid": self.uid}
)
if result and 'a' in result[0]:
return result[0]['a']
return None
def can_transition_to(self, new_status: str) -> bool:
"""
Check if document can transition to the specified status.
Args:
new_status: Target status
Returns:
bool: True if transition is allowed
"""
from CDocs.config import settings
# Normalize status codes (convert any full names to codes)
current_status = self.status.upper()
target_status = settings.get_document_status_code(new_status).upper()
# Use the helper function from settings
return settings.is_valid_status_transition(current_status, target_status)
def requires_pdf_conversion(self, new_status: str) -> bool:
"""
Check if transitioning to the specified status requires PDF conversion.
Args:
new_status: Target status
Returns:
bool: True if PDF conversion is required
"""
# PDF conversion is required when moving from editable to non-editable status
current_editable = self.is_editable()
new_editable = is_editable_status(new_status)
return current_editable and not new_editable
def update_status(self, new_status: str, user_id: str, comment: str = None) -> Dict[str, Any]:
"""
Update document status with validation and necessary conversions.
Args:
new_status: Target status
user_id: ID of user performing the status change
comment: Optional comment about the status change
Returns:
Dict: Result of the status change
"""
# Normalize status string
from CDocs.config import settings
target_status = settings.get_document_status_code(new_status)
# Validate transition
if not self.can_transition_to(target_status):
return {
'success': False,
'message': f"Cannot transition document from {self.status} to {target_status}",
'old_status': self.status,
'new_status': target_status
}
# Check if PDF conversion is needed
needs_conversion = self.requires_pdf_conversion(target_status)
# Update status
old_status = self.status
self.status = target_status
# Log the status change
from CDocs.utils import audit_trail
try:
audit_trail.log_document_lifecycle_event(
event_type="STATUS_CHANGED",
user_uid=user_id,
document_uid=self.uid,
details={
'old_status': old_status,
'new_status': target_status,
'comment': comment,
'needs_conversion': needs_conversion
}
)
except Exception as e:
logger.warning(f"Failed to log status change: {str(e)}")
return {
'success': True,
'message': f"Status updated to {target_status}",
'old_status': old_status,
'new_status': target_status,
'needs_conversion': needs_conversion
}
def is_under_review(self) -> bool:
"""
Check if document is currently under review.
Returns:
bool: True if document has an active review cycle
"""
current_version = self.current_version
if not current_version:
return False
result = db.run_query(
"""
MATCH (v:DocumentVersion {UID: $uid})-[:FOR_REVIEW]->(r:ReviewCycle)
WHERE r.status IN ['PENDING', 'IN_PROGRESS']
RETURN COUNT(r) > 0 as under_review
""",
{"uid": current_version.uid}
)
if result and result[0].get('under_review', False):
return True
return False
def is_under_approval(self) -> bool:
"""
Check if document is currently under approval.
Returns:
bool: True if document has an active approval workflow
"""
current_version = self.current_version
if not current_version:
return False
result = db.run_query(
"""
MATCH (v:DocumentVersion {UID: $uid})-[:FOR_APPROVAL]->(a:Approval)
WHERE a.status IN ['PENDING', 'IN_PROGRESS']
RETURN COUNT(a) > 0 as under_approval
""",
{"uid": current_version.uid}
)
if result and result[0].get('under_approval', False):
return True
return False
@property
def share_id(self) -> Optional[str]:
"""
Get the share ID for this document version.
Returns:
str: Share ID or None if no share exists
"""
return self._data.get('share_id')
@share_id.setter
def share_id(self, share_id: str) -> None:
"""
Set the share ID for this document version.
Args:
share_id: Share ID from FileCloud
"""
self._data['share_id'] = share_id
db.update_node(self.uid, {'share_id': share_id})
@property
def share_url(self) -> Optional[str]:
"""
Get the share URL for this document version.
Returns:
str: Share URL or None if no share exists
"""
return self._data.get('share_url')
@share_url.setter
def share_url(self, share_url: str) -> None:
"""
Set the share URL for this document version.
Args:
share_url: Share URL from FileCloud
"""
self._data['share_url'] = share_url
db.update_node(self.uid, {'share_url': share_url})
def save(self) -> None:
"""
Save all changes to this document version to the database.
"""
properties = {}
# Copy all properties that should be persisted
for key in ['versionNumber', 'status', 'fileCloudPath', 'share_id', 'share_url',
'effectiveDate', 'expiryDate', 'file_type', 'file_name']:
if key in self._data:
properties[key] = self._data[key]
# Update in database
if properties:
db.update_node(self.uid, properties)
def get_training_config(self) -> Dict[str, Any]:
"""Get training configuration for this document."""
from CDocs.models.training import DocumentTraining
doc_training = DocumentTraining(document_uid=self.uid)
return doc_training._data
def is_training_enabled(self) -> bool:
"""Check if training is enabled for this document."""
return self._data.get("training_required", False)
def get_training_assignments(self) -> List[Dict[str, Any]]:
"""Get all training assignments for this document."""
from CDocs.models.training import DocumentTraining
doc_training = DocumentTraining(document_uid=self.uid)
return doc_training.get_assigned_users()
def reset_training_on_publish(self) -> bool:
"""Reset training when document is published."""
if self.is_training_enabled():
from CDocs.models.training import DocumentTraining
doc_training = DocumentTraining(document_uid=self.uid)
return doc_training.reset_all_training()
return True
Parameters
| Name | Type | Default | Kind |
|---|---|---|---|
bases |
BaseModel | - |
Parameter Details
bases: Parameter of type BaseModel
Return Value
Returns unspecified type
Class Interface
Methods
__init__(self, data, uid)
Purpose: Initialize a controlled document. Args: data: Dictionary of document properties uid: Document UID to load from database (if data not provided)
Parameters:
data: Type: Optional[Dict[str, Any]]uid: Type: Optional[str]
Returns: None
create(cls, title, doc_type, department, owner, doc_number, properties)
Purpose: Create a new controlled document. Args: title: Document title doc_type: Document type name or code department: Department name or code owner: Owner User object or UID properties: Additional properties Returns: New ControlledDocument instance or None if creation failed
Parameters:
cls: Parametertitle: Parameterdoc_type: Parameterdepartment: Parameterowner: Parameterdoc_number: Parameterproperties: Parameter
Returns: See docstring for return details
title(self) -> str
property
Purpose: Get document title.
Returns: Returns str
title(self, value) -> None
Purpose: Set document title.
Parameters:
value: Type: str
Returns: Returns None
custom_path(self) -> Optional[str]
property
Purpose: Get custom storage path for this document.
Returns: Returns Optional[str]
custom_path(self, path) -> None
Purpose: Set custom storage path for this document.
Parameters:
path: Type: str
Returns: Returns None
description(self) -> str
property
Purpose: Get the document description. Returns: Document description or empty string if not set
Returns: Returns str
description(self, value) -> None
Purpose: Set the document description. Args: value: New description
Parameters:
value: Type: str
Returns: Returns None
doc_number(self) -> str
property
Purpose: Get document number.
Returns: Returns str
doc_type(self) -> str
property
Purpose: Get document type code.
Returns: Returns str
UID(self) -> str
property
Purpose: Get document UID.
Returns: Returns str
doc_type_name(self) -> str
property
Purpose: Get document type name.
Returns: Returns str
department(self) -> str
property
Purpose: Get department code.
Returns: Returns str
status(self) -> str
property
Purpose: Get document status code.
Returns: Returns str
status(self, value) -> None
Purpose: Set document status using code.
Parameters:
value: Type: str
Returns: Returns None
get_status_name(self) -> str
Purpose: Get the full status name.
Returns: Returns str
get_status_color(self) -> str
Purpose: Get the color for displaying this status.
Returns: Returns str
created_date(self) -> Optional[datetime]
property
Purpose: Get document creation date.
Returns: Returns Optional[datetime]
owner_uid(self) -> Optional[str]
property
Purpose: Get owner UID.
Returns: Returns Optional[str]
authors(self) -> List[DocUser]
property
Purpose: Get authors of the document.
Returns: Returns List[DocUser]
owner(self) -> Optional[DocUser]
property
Purpose: Get document owner.
Returns: Returns Optional[DocUser]
owner(self, user) -> None
Purpose: Set document owner.
Parameters:
user: Type: Union[DocUser, str]
Returns: Returns None
create_version(self, version_number, created_by, file_paths, properties) -> Optional[DocumentVersion]
Purpose: Create a new version of this document. Args: version_number: Version number string (e.g., "1.0") created_by: User creating the version or their UID file_paths: Dictionary containing Word and PDF file paths in FileCloud properties: Additional properties for the version Returns: New DocumentVersion instance or None if creation failed
Parameters:
version_number: Type: strcreated_by: Type: Union[DocUser, str]file_paths: Type: Optional[Dict[str, str]]properties: Type: Optional[Dict[str, Any]]
Returns: Returns Optional[DocumentVersion]
versions(self) -> List[DocumentVersion]
property
Purpose: Get all versions of this document.
Returns: Returns List[DocumentVersion]
get_all_versions(self) -> List[DocumentVersion]
Purpose: Get all versions of this document. Returns: List of DocumentVersion instances sorted by version number (newest first)
Returns: Returns List[DocumentVersion]
current_version(self) -> Optional[DocumentVersion]
property
Purpose: Get current version of the document.
Returns: Returns Optional[DocumentVersion]
set_current_version(self, version) -> bool
Purpose: Set the current version of the document. Args: version: DocumentVersion instance or UID Returns: Boolean indicating success
Parameters:
version: Type: Union[DocumentVersion, str]
Returns: Returns bool
is_current_version(self, version_uid) -> bool
Purpose: Check if the given version UID is the current version of the document. Args: version_uid: UID of the version to check Returns: True if it's the current version, False otherwise
Parameters:
version_uid: Type: str
Returns: Returns bool
get_document_type_details(self) -> Dict[str, Any]
Purpose: Get details about the document type. Returns: Dictionary with document type information
Returns: Returns Dict[str, Any]
get_department_name(self) -> str
Purpose: Get the full department name. Returns: Department name
Returns: Returns str
get_version_by_number(self, version_number) -> Optional[DocumentVersion]
Purpose: Get a specific version of the document by version number. Args: version_number: Version number string (e.g., "1.0") Returns: DocumentVersion instance or None if not found
Parameters:
version_number: Type: str
Returns: Returns Optional[DocumentVersion]
to_dict(self) -> Dict[str, Any]
Purpose: Convert to dictionary representation.
Returns: Returns Dict[str, Any]
get_documents(cls, filters, limit, skip, sort_by, descending) -> List['ControlledDocument']
Purpose: Get controlled documents with optional filtering. Args: filters: Optional filter criteria limit: Maximum number of documents to return skip: Number of documents to skip (for pagination) sort_by: Field to sort by descending: Whether to sort in descending order Returns: List of ControlledDocument instances
Parameters:
cls: Parameterfilters: Type: Optional[Dict[str, Any]]limit: Type: intskip: Type: intsort_by: Type: strdescending: Type: bool
Returns: Returns List['ControlledDocument']
count_documents(cls, filters) -> int
Purpose: Count controlled documents with optional filtering. Args: filters: Optional filter criteria Returns: Number of matching documents
Parameters:
cls: Parameterfilters: Type: Optional[Dict[str, Any]]
Returns: Returns int
is_editable(self) -> bool
Purpose: Check if this document is in an editable state. Returns: bool: True if the document can be edited
Returns: Returns bool
is_published(self) -> bool
Purpose: Check if this document is in a published/non-editable state. Returns: bool: True if the document is published/non-editable
Returns: Returns bool
get_primary_file_path(self) -> Optional[str]
Purpose: Get the primary file path based on document status. For published/non-editable documents, returns PDF path. For editable documents, returns editable document path. Returns: str: Path to the primary file or None if no files are available
Returns: Returns Optional[str]
get_secondary_file_path(self) -> Optional[str]
Purpose: Get the secondary file path based on document status. For published/non-editable documents, returns the editable document path if available. For editable documents, returns PDF path if available. Returns: str: Path to the secondary file or None if no secondary file is available
Returns: Returns Optional[str]
get_approvals(self) -> List[Dict[str, Any]]
Purpose: Get all approval workflows for this document. Returns: List of approval data dictionaries
Returns: Returns List[Dict[str, Any]]
get_active_approval(self) -> Optional[Dict[str, Any]]
Purpose: Get the active approval workflow for this document. Returns: Approval data dictionary or None if no active approval
Returns: Returns Optional[Dict[str, Any]]
can_transition_to(self, new_status) -> bool
Purpose: Check if document can transition to the specified status. Args: new_status: Target status Returns: bool: True if transition is allowed
Parameters:
new_status: Type: str
Returns: Returns bool
requires_pdf_conversion(self, new_status) -> bool
Purpose: Check if transitioning to the specified status requires PDF conversion. Args: new_status: Target status Returns: bool: True if PDF conversion is required
Parameters:
new_status: Type: str
Returns: Returns bool
update_status(self, new_status, user_id, comment) -> Dict[str, Any]
Purpose: Update document status with validation and necessary conversions. Args: new_status: Target status user_id: ID of user performing the status change comment: Optional comment about the status change Returns: Dict: Result of the status change
Parameters:
new_status: Type: struser_id: Type: strcomment: Type: str
Returns: Returns Dict[str, Any]
is_under_review(self) -> bool
Purpose: Check if document is currently under review. Returns: bool: True if document has an active review cycle
Returns: Returns bool
is_under_approval(self) -> bool
Purpose: Check if document is currently under approval. Returns: bool: True if document has an active approval workflow
Returns: Returns bool
share_id(self) -> Optional[str]
property
Purpose: Get the share ID for this document version. Returns: str: Share ID or None if no share exists
Returns: Returns Optional[str]
share_id(self, share_id) -> None
Purpose: Set the share ID for this document version. Args: share_id: Share ID from FileCloud
Parameters:
share_id: Type: str
Returns: Returns None
share_url(self) -> Optional[str]
property
Purpose: Get the share URL for this document version. Returns: str: Share URL or None if no share exists
Returns: Returns Optional[str]
share_url(self, share_url) -> None
Purpose: Set the share URL for this document version. Args: share_url: Share URL from FileCloud
Parameters:
share_url: Type: str
Returns: Returns None
save(self) -> None
Purpose: Save all changes to this document version to the database.
Returns: Returns None
get_training_config(self) -> Dict[str, Any]
Purpose: Get training configuration for this document.
Returns: Returns Dict[str, Any]
is_training_enabled(self) -> bool
Purpose: Check if training is enabled for this document.
Returns: Returns bool
get_training_assignments(self) -> List[Dict[str, Any]]
Purpose: Get all training assignments for this document.
Returns: Returns List[Dict[str, Any]]
reset_training_on_publish(self) -> bool
Purpose: Reset training when document is published.
Returns: Returns bool
Required Imports
import logging
import uuid
import hashlib
from typing import Dict
from typing import List
Usage Example
# Example usage:
# result = ControlledDocument(bases)
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
class DocumentVersion_v1 75.1% similar
-
class DocumentDashboard 70.1% similar
-
function create_document 66.0% similar
-
class Document 64.2% similar
-
class DocumentTraining 64.1% similar