class ApprovalComment_v1
A 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.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 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 withuid: 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 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 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
logginguuidtypingdatetimeCDocs.dbCDocs.configCDocs.db.schema_managerCDocs.models.baseCDocs.models.user_extensionsCDocs.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
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
class ApprovalComment 97.4% similar
-
class ReviewComment 80.1% similar
-
function add_approval_comment_v1 68.2% similar
-
class ApprovalCycle_v1 67.1% similar
-
function add_approval_comment 66.7% similar