class ApprovalComment
Model class representing a comment made during a document approval cycle, with support for resolution tracking and database persistence.
/tf/active/vicechatdev/CDocs/models/approval_bis.py
22 - 216
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 withuid: 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 tocommenter: DocUser instance or user UID string of the person making the commenttext: The comment text contentrequires_resolution: Boolean flag indicating if this comment must be resolved before approvalproperties: 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
logginguuidtypingdatetimeCDocs.dbCDocs.configCDocs.db.schema_managerCDocs.models.baseCDocs.models.decoratorsCDocs.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
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
class ApprovalComment_v1 97.4% similar
-
class ReviewComment 80.4% similar
-
class ApprovalCycle 67.1% similar
-
function add_approval_comment 66.8% similar
-
function add_approval_comment_v1 66.2% similar