🔍 Code Extractor

class ControlledDocument_v1

Maturity: 27

Model representing a controlled document.

File:
/tf/active/vicechatdev/CDocs single class/models/document.py
Lines:
633 - 1454
Complexity:
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, 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
            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 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 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 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": self.uid}
        )
        
        if result and 'v' in result[0]:
            return DocumentVersion(result[0]['v'])
            
        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
                LIMIT 1
                """,
                {"doc_uid": self.uid, "version_number": version_number}
            )
            
            if result and 'v' in result[0]:
                return DocumentVersion(result[0]['v'])
                
            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 documents, returns PDF path.
        For non-published 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

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, 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: Parameter
  • title: Parameter
  • doc_type: Parameter
  • department: Parameter
  • owner: Parameter
  • properties: 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

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

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]

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: str
  • created_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: Parameter
  • filters: Type: Optional[Dict[str, Any]]
  • limit: Type: int
  • skip: Type: int
  • sort_by: Type: str
  • descending: 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: Parameter
  • filters: 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 documents, returns PDF path. For non-published 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: str
  • user_id: Type: str
  • comment: 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

Required Imports

import logging
import uuid
import hashlib
from typing import Dict
from typing import List

Usage Example

# Example usage:
# result = ControlledDocument(bases)

Related Versions

Other versions of this component:

Similar Components

AI-powered semantic similarity - components with related functionality:

  • class DocumentVersion_v1 79.8% similar

    Model representing a specific version of a controlled document.

    From: /tf/active/vicechatdev/CDocs/models/document.py
  • class ControlledDocument 74.6% similar

    A class representing a controlled document in a document management system, providing access to document metadata stored in a Neo4j database.

    From: /tf/active/vicechatdev/CDocs single class/db/db_operations.py
  • class DocumentDashboard_v1 71.1% similar

    Dashboard for viewing and managing controlled documents.

    From: /tf/active/vicechatdev/CDocs single class/ui/document_dashboard.py
  • class DocumentDashboard 68.6% similar

    Dashboard for viewing and managing controlled documents.

    From: /tf/active/vicechatdev/CDocs/ui/document_dashboard.py
← Back to Browse