🔍 Code Extractor

class ApprovalComment

Maturity: 51

Model class representing a comment made during a document approval cycle, with support for resolution tracking and database persistence.

File:
/tf/active/vicechatdev/CDocs/models/approval_bis.py
Lines:
22 - 216
Complexity:
moderate

Purpose

ApprovalComment manages comments made by users during document approval workflows. It handles creation, storage, and retrieval of comments from a graph database, tracks whether comments require resolution, stores resolution details, and maintains relationships with approval cycles and users. The class provides properties for accessing comment metadata, commenter information, and resolution status.

Source Code

class ApprovalComment(BaseModel):
    """Model representing a comment made during document approval."""
    
    def __init__(self, data: Optional[Dict[str, Any]] = None, uid: Optional[str] = None):
        """
        Initialize an approval comment.
        
        Args:
            data: Dictionary of comment properties
            uid: Comment UID to load from database (if data not provided)
        """
        if data is None and uid is not None:
            # Fetch comment data from database
            data = db.get_node_by_uid(uid)
            
        super().__init__(data or {})
    
    @classmethod
    def create(cls, approval_cycle_uid: str, 
              commenter: Union[DocUser, str], 
              text: str,
              requires_resolution: bool = False,
              properties: Optional[Dict[str, Any]] = None) -> Optional['ApprovalComment']:
        """
        Create a new approval comment.
        
        Args:
            approval_cycle_uid: UID of the approval cycle this comment belongs to
            commenter: User making the comment or their UID
            text: Comment text
            requires_resolution: Whether this comment requires resolution
            properties: Additional properties for the comment
            
        Returns:
            New ApprovalComment instance or None if creation failed
        """
        try:
            # Prepare properties
            props = properties or {}
            props.update({
                'text': text,
                'timestamp': datetime.now(),
                'requiresResolution': requires_resolution,
                'commenterUID': commenter.uid if isinstance(commenter, DocUser) else commenter
            })
            
            # Create the comment node first
            comment_uid = str(uuid.uuid4())
            props['UID'] = comment_uid
            
            # Create node in database
            success = db.create_node(NodeLabels.APPROVAL_COMMENT, props)
            
            if not success:
                logger.error(f"Failed to create approval comment for approval cycle {approval_cycle_uid}")
                return None
            
            # Create relationship: Comment -> COMMENTED_ON -> Approval Cycle
            rel_success = db.create_relationship(
                comment_uid, 
                approval_cycle_uid,
                RelTypes.COMMENTED_ON
            )
            
            if not rel_success:
                logger.error(f"Failed to create relationship between comment {comment_uid} and approval cycle {approval_cycle_uid}")
                # Clean up the orphaned comment node
                db.delete_node(comment_uid)
                return None
                
            # Create the comment instance
            comment = cls(props)
            
            return comment
            
        except Exception as e:
            logger.error(f"Error creating approval comment: {e}")
            return None

    def save(self) -> bool:
        """Save changes to database."""
        try:
            # If node doesn't exist, create it
            if not db.node_exists(self.uid):
                created = db.create_node_with_uid(
                    NodeLabels.APPROVAL_COMMENT,
                    self._data,
                    self.uid
                )
                
                if created and self._data.get('approval_cycle_uid'):
                    # Create relationship between comment and approval cycle
                    db.create_relationship(
                        self.uid,
                        self._data['approval_cycle_uid'],
                        RelTypes.COMMENTED_ON
                    )
                return created
            
            # Otherwise update existing node
            return db.update_node(self.uid, self._data)
        except Exception as e:
            logger.error(f"Error saving approval comment: {e}")
            return False
    
    @property
    def text(self) -> str:
        """Get comment text."""
        return self._data.get('text', '')
    
    @property
    def timestamp(self) -> Optional[datetime]:
        """Get when comment was made."""
        return self._data.get('timestamp')
    
    @property
    def requires_resolution(self) -> bool:
        """Whether comment requires resolution."""
        return self._data.get('requiresResolution', False)
    
    @property
    def resolution(self) -> Optional[str]:
        """Get resolution text."""
        return self._data.get('resolution')
    
    @resolution.setter
    def resolution(self, text: str) -> None:
        """Set resolution text."""
        self._data['resolution'] = text
        self._data['resolutionDate'] = datetime.now()
        db.update_node(self.uid, {
            'resolution': text,
            'resolutionDate': self._data['resolutionDate']
        })
    
    @property
    def resolution_date(self) -> Optional[datetime]:
        """Get when comment was resolved."""
        return self._data.get('resolutionDate')
    
    @property
    def is_resolved(self) -> bool:
        """Whether comment has been resolved."""
        return self.resolution is not None
    
    @property
    def commenter_uid(self) -> Optional[str]:
        """Get UID of user who made the comment."""
        return self._data.get('commenterUID')
    
    @property
    def commenter(self) -> Optional[DocUser]:
        """Get user who made the comment."""
        commenter_uid = self.commenter_uid
        if commenter_uid:
            return DocUser(uid=commenter_uid)
        return None
    
    @property
    def approval_cycle_uid(self) -> Optional[str]:
        """Get the UID of the approval cycle this comment belongs to."""
        # First check if we already have it in properties
        if self._data.get('approval_cycle_uid'):
            return self._data.get('approval_cycle_uid')
        
        # Otherwise query the database
        result = db.run_query(
            """
            MATCH (c:ApprovalComment {UID: $uid})-[:COMMENTED_ON]->(a:ApprovalCycle)
            RETURN a.UID as approval_uid
            """,
            {"uid": self.uid}
        )
        
        if result and 'approval_uid' in result[0]:
            # Cache it for future use
            self._data['approval_cycle_uid'] = result[0]['approval_uid']
            return result[0]['approval_uid']
            
        return None
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary representation."""
        result = super().to_dict()
        
        # Add commenter information if available
        commenter = self.commenter
        if commenter:
            result['commenter'] = {
                'UID': commenter.uid,
                'name': commenter.name,
                'email': commenter.email
            }
            
        return result

Parameters

Name Type Default Kind
bases BaseModel -

Parameter Details

data: Optional dictionary containing comment properties (text, timestamp, requiresResolution, commenterUID, etc.). If provided, initializes the comment with this data. If None and uid is provided, data will be fetched from the database.

uid: Optional unique identifier string for the comment. If provided without data, the comment will be loaded from the database using this UID. If data is provided, this parameter is ignored.

Return Value

The __init__ method returns an ApprovalComment instance. The create() class method returns a new ApprovalComment instance if successful, or None if creation failed. The save() method returns a boolean indicating success. Properties return their respective types (str, datetime, bool, DocUser, etc.).

Class Interface

Methods

__init__(self, data: Optional[Dict[str, Any]] = None, uid: Optional[str] = None)

Purpose: Initialize an approval comment instance, either from provided data or by loading from database using uid

Parameters:

  • data: Dictionary of comment properties to initialize with
  • uid: Comment UID to load from database if data is not provided

Returns: ApprovalComment instance

create(cls, approval_cycle_uid: str, commenter: Union[DocUser, str], text: str, requires_resolution: bool = False, properties: Optional[Dict[str, Any]] = None) -> Optional['ApprovalComment']

Purpose: Create a new approval comment in the database with all necessary relationships

Parameters:

  • approval_cycle_uid: UID of the approval cycle this comment belongs to
  • commenter: DocUser instance or user UID string of the person making the comment
  • text: The comment text content
  • requires_resolution: Boolean flag indicating if this comment must be resolved before approval
  • properties: Additional custom properties to store with the comment

Returns: New ApprovalComment instance if successful, None if creation failed

save(self) -> bool

Purpose: Persist comment changes to the database, creating the node if it doesn't exist or updating if it does

Returns: Boolean indicating whether the save operation was successful

text(self) -> str property

Purpose: Get the comment text content

Returns: String containing the comment text, empty string if not set

timestamp(self) -> Optional[datetime] property

Purpose: Get the datetime when the comment was created

Returns: datetime object of when comment was made, or None if not set

requires_resolution(self) -> bool property

Purpose: Check if this comment requires resolution before approval can proceed

Returns: Boolean indicating whether resolution is required, defaults to False

resolution(self) -> Optional[str] property

Purpose: Get the resolution text for this comment

Returns: String containing resolution text, or None if not resolved

resolution(self, text: str) -> None property

Purpose: Set the resolution text and automatically update the database with resolution date

Parameters:

  • text: The resolution text explaining how the comment was addressed

Returns: None, but updates database as side effect

resolution_date(self) -> Optional[datetime] property

Purpose: Get the datetime when the comment was resolved

Returns: datetime object of when comment was resolved, or None if not resolved

is_resolved(self) -> bool property

Purpose: Check if the comment has been resolved

Returns: Boolean indicating whether resolution text exists

commenter_uid(self) -> Optional[str] property

Purpose: Get the UID of the user who made the comment

Returns: String UID of the commenter, or None if not set

commenter(self) -> Optional[DocUser] property

Purpose: Get the DocUser instance of the person who made the comment

Returns: DocUser instance of the commenter, or None if commenter_uid is not set

approval_cycle_uid(self) -> Optional[str] property

Purpose: Get the UID of the approval cycle this comment belongs to, querying database if needed and caching result

Returns: String UID of the approval cycle, or None if relationship doesn't exist

to_dict(self) -> Dict[str, Any]

Purpose: Convert the comment to a dictionary representation with enriched commenter information

Returns: Dictionary containing all comment data plus nested commenter object with UID, name, and email

Attributes

Name Type Description Scope
_data Dict[str, Any] Internal dictionary storing all comment properties (inherited from BaseModel), including text, timestamp, requiresResolution, commenterUID, resolution, resolutionDate, approval_cycle_uid instance
uid str Unique identifier for the comment (inherited from BaseModel), used as primary key in database instance

Dependencies

  • logging
  • uuid
  • typing
  • datetime
  • CDocs.db
  • CDocs.config
  • CDocs.db.schema_manager
  • CDocs.models.base
  • CDocs.models.decorators
  • CDocs.models.user_extensions

Required Imports

import logging
import uuid
from typing import Dict, List, Any, Optional, Union, Set
from datetime import datetime, timedelta
from CDocs import db
from CDocs.config import settings
from CDocs.db.schema_manager import NodeLabels, RelTypes
from CDocs.models.base import BaseModel
from CDocs.models.decorators import register_model
from CDocs.models.user_extensions import DocUser

Usage Example

# Create a new approval comment
comment = ApprovalComment.create(
    approval_cycle_uid='cycle-123',
    commenter='user-456',
    text='This section needs clarification',
    requires_resolution=True
)

if comment:
    # Access comment properties
    print(comment.text)
    print(comment.timestamp)
    print(comment.requires_resolution)
    
    # Get commenter information
    commenter = comment.commenter
    if commenter:
        print(commenter.name)
    
    # Resolve the comment
    comment.resolution = 'Clarification added in section 3.2'
    
    # Check resolution status
    if comment.is_resolved:
        print(f'Resolved on: {comment.resolution_date}')
    
    # Convert to dictionary
    comment_dict = comment.to_dict()

# Load existing comment from database
existing_comment = ApprovalComment(uid='comment-789')
print(existing_comment.text)

Best Practices

  • Always use the create() class method to instantiate new comments rather than directly calling __init__ with data, as create() handles database persistence and relationship creation
  • Check if create() returns None before using the comment instance, as creation can fail due to database errors
  • Use the resolution property setter to mark comments as resolved, as it automatically updates the database and sets the resolution date
  • The commenter property performs a database query, so cache the result if you need to access it multiple times
  • The approval_cycle_uid property caches its result after the first database query for performance
  • Call save() after modifying comment properties (except resolution, which auto-saves) to persist changes to the database
  • The class automatically creates bidirectional relationships between comments and approval cycles
  • Handle potential None returns from properties like commenter and approval_cycle_uid when relationships may not exist
  • Use to_dict() for serialization, as it includes enriched commenter information beyond raw data

Similar Components

AI-powered semantic similarity - components with related functionality:

  • class ApprovalComment_v1 97.4% similar

    A model class representing a comment made during a document approval cycle, with support for resolution tracking and database persistence.

    From: /tf/active/vicechatdev/CDocs/models/approval.py
  • class ReviewComment 80.4% similar

    A model class representing a comment made during document review, with support for resolution tracking and database persistence.

    From: /tf/active/vicechatdev/CDocs/models/review.py
  • class ApprovalCycle 67.1% similar

    Model representing an approval cycle for a document version.

    From: /tf/active/vicechatdev/CDocs/models/approval_bis.py
  • function add_approval_comment 66.8% similar

    Adds a comment to an approval cycle for a controlled document, with support for threaded comments, different comment types, and automatic notifications to relevant stakeholders.

    From: /tf/active/vicechatdev/CDocs/controllers/approval_controller.py
  • function add_approval_comment_v1 66.2% similar

    Adds a comment to an approval cycle with optional location information, page references, and reply threading. Validates user permissions, logs audit trails, and sends notifications to other approvers for issue-type comments.

    From: /tf/active/vicechatdev/CDocs/controllers/approval_controller_bis.py
← Back to Browse