🔍 Code Extractor

class ApprovalComment_v1

Maturity: 49

A 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.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 text and dates, and maintains relationships with approval cycles and users. The class provides properties for accessing comment metadata and methods for persisting changes to the database.

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 a 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]->(r:ApprovalCycle)
            RETURN r.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 string representing the unique identifier of an existing comment in the database. Used to load an existing comment when data is not provided. If both data and uid are None, creates an empty comment instance.

Return Value

The constructor returns an ApprovalComment instance. The create() class method returns an Optional[ApprovalComment] - either a new comment instance if creation succeeds, or None if creation fails. The save() method returns a boolean indicating success/failure. 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 not provided

Returns: None (constructor)

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 relationships to approval cycle and commenter

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 creation succeeds, None if creation fails

save(self) -> bool

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

Returns: True if save operation succeeds, False if it fails

text(self) -> str property

Purpose: Get the comment text content

Returns: The comment text as a string, 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, None if not set

requires_resolution(self) -> bool property

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

Returns: True if comment requires resolution, False otherwise

resolution(self) -> Optional[str] property

Purpose: Get the resolution text for this comment

Returns: Resolution text as string if resolved, None if not resolved

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

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

Parameters:

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

Returns: None (setter)

resolution_date(self) -> Optional[datetime] property

Purpose: Get the datetime when the comment was resolved

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

is_resolved(self) -> bool property

Purpose: Check if the comment has been resolved

Returns: True if comment has resolution text, False otherwise

commenter_uid(self) -> Optional[str] property

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

Returns: User UID string if set, None otherwise

commenter(self) -> Optional[DocUser] property

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

Returns: DocUser instance if commenter_uid is set, None otherwise

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: Approval cycle UID string if found, None otherwise

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.user_extensions
  • CDocs.models.document

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, register_model
from CDocs.models.user_extensions import DocUser
from CDocs.models.document import DocumentVersion, ControlledDocument

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
    user = comment.commenter
    if user:
        print(user.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}')
    
    # Save changes
    comment.save()
    
    # Convert to dictionary
    comment_dict = comment.to_dict()

# Load existing comment
existing_comment = ApprovalComment(uid='comment-789')

Best Practices

  • Always use the create() class method to instantiate new comments rather than direct instantiation, as it handles database creation and relationship setup
  • Check the return value of create() for None before using the comment instance, as creation can fail
  • Use the resolution property setter to mark comments as resolved, as it automatically updates the resolution date and persists to the database
  • Call save() after modifying comment properties to persist changes to the database
  • The commenter property performs a database lookup, so cache the result if using it multiple times
  • The approval_cycle_uid property caches its result after first lookup for performance
  • Handle exceptions when working with database operations, as network or database issues can cause failures
  • Use to_dict() when serializing comments for API responses or JSON output, as it includes enriched commenter information

Similar Components

AI-powered semantic similarity - components with related functionality:

  • class ApprovalComment 97.4% similar

    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_bis.py
  • class ReviewComment 80.1% 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
  • function add_approval_comment_v1 68.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
  • class ApprovalCycle_v1 67.1% similar

    Model representing a approval cycle for a document version.

    From: /tf/active/vicechatdev/CDocs/models/approval.py
  • function add_approval_comment 66.7% 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
← Back to Browse