function notify_approval
Sends approval notifications to designated approvers for a document version, supporting multiple notification types (requested, reminder, overdue, completed, rejected, signed) with email and in-app notifications.
/tf/active/vicechatdev/CDocs/utils/notifications.py
1326 - 1457
moderate
Purpose
This function orchestrates the approval notification workflow by retrieving document and version details, preparing notification content based on the notification type, and dispatching notifications to specified approvers. It handles various approval lifecycle events including initial requests, reminders, overdue notices, completion, rejection, and document signing. The function constructs detailed email templates with document metadata and approval URLs, logs comprehensive debugging information, and gracefully handles errors by returning empty lists on failure.
Source Code
def notify_approval(approval,
notification_type: str,
step: Optional[str] = None,
approver_uids: Optional[List[str]] = None) -> List[str]:
"""
Send approval notifications to approvers.
Args:
approval: Approval instance
notification_type: Type of notification
approver_uids: Optional specific approvers to notify (default: all)
Returns:
List of created notification UIDs
"""
try:
# Get document and version details
version = approval.document_version
if not version:
logger.error(f"Could not find document version for approval {approval.uid}")
return []
document = version.document
if not document:
logger.error(f"Could not find document for approval {approval.uid}")
return []
# Log the document details for debugging
logger.info(f"notify_approval called for document {document.doc_number} - {document.title}")
logger.debug(f"Document data: doc_number={document.doc_number}, title={document.title}, doc_type={document.doc_type}")
logger.debug(f"Version data: version_number={version.version_number}")
logger.debug(f"Approval data: uid={approval.uid}, due_date={approval.due_date}")
# Get approvers to notify
if approver_uids is None:
approver_uids = approval.approver_uids
if not approver_uids:
logger.warning(f"No approvers to notify for approval {approval.uid}")
return []
# Prepare notification details
details = {
'approval_uid': approval.uid,
'version_uid': version.uid,
'doc_uid': document.uid,
'doc_number': document.doc_number,
'doc_type': document.doc_type,
'title': document.title,
'step': step,
'version_number': version.version_number,
'due_date': approval.due_date.strftime('%Y-%m-%d') if approval.due_date else None
}
# Create message based on notification type
message = None
if notification_type == 'APPROVAL_REQUESTED':
message = f"Please approve {document.doc_number} - {document.title} (v{version.version_number})"
elif notification_type == 'APPROVAL_REMINDER':
message = f"Reminder: Your approval of {document.doc_number} (v{version.version_number}) is due soon"
elif notification_type == 'APPROVAL_OVERDUE':
message = f"Your approval of {document.doc_number} (v{version.version_number}) is overdue"
elif notification_type == 'APPROVAL_COMPLETED':
message = f"The approval process for {document.doc_number} (v{version.version_number}) has been completed"
elif notification_type == 'APPROVAL_REJECTED':
message = f"The approval for {document.doc_number} (v{version.version_number}) has been rejected"
elif notification_type == 'DOCUMENT_SIGNED':
message = f"Document {document.doc_number} (v{version.version_number}) has been signed"
# Determine email template based on notification type
email_template = None
if notification_type == 'APPROVAL_REQUESTED':
email_template = 'approval_requested'
elif notification_type == 'APPROVAL_REMINDER':
email_template = 'approval_reminder'
elif notification_type == 'APPROVAL_OVERDUE':
email_template = 'approval_overdue'
elif notification_type == 'APPROVAL_COMPLETED':
email_template = 'approval_completed'
elif notification_type == 'APPROVAL_REJECTED':
email_template = 'approval_rejected'
elif notification_type == 'DOCUMENT_SIGNED':
email_template = 'document_signed'
# Add approval instructions to email data if available
email_data = {
'doc_number': document.doc_number,
'title': document.title,
'doc_type': document.doc_type,
'version_number': version.version_number,
'due_date': approval.due_date.strftime('%B %d, %Y') if approval.due_date else 'Not specified',
'document_url': f"{settings.APP_URL}/document/{document.uid}",
'approval_url': f"{settings.APP_URL}/approval/{approval.uid}",
'cdocs_app_url': 'https://cdocs.vicebio.com',
'cdocs_app_text': 'Access the CDocs Application'
}
# Debug: Log the actual email_data being created
logger.info(f"notify_approval: Creating email_data for template '{email_template}'")
logger.info(f"notify_approval: Document attributes - doc_number: '{document.doc_number}', title: '{document.title}', doc_type: '{document.doc_type}'")
logger.info(f"notify_approval: Version attributes - version_number: '{version.version_number}'")
logger.info(f"notify_approval: Email_data created: {email_data}")
# Log the email data being constructed
logger.info(f"Constructed email_data for template '{email_template}':")
for key, value in email_data.items():
logger.info(f" {key}: {value}")
if approval.instructions:
email_data['instructions'] = approval.instructions
logger.debug(f"Added instructions: {approval.instructions}")
if step:
email_data['step'] = step
logger.debug(f"Added step: {step}")
# Send notification
return send_notification(
notification_type=notification_type,
users=approver_uids,
resource_uid=approval.uid,
resource_type='Approval',
message=message,
details=details,
send_email=True,
email_template=email_template,
email_data=email_data
)
except Exception as e:
logger.error(f"Error sending approval notification: {e}")
return []
Parameters
| Name | Type | Default | Kind |
|---|---|---|---|
approval |
- | - | positional_or_keyword |
notification_type |
str | - | positional_or_keyword |
step |
Optional[str] | None | positional_or_keyword |
approver_uids |
Optional[List[str]] | None | positional_or_keyword |
Parameter Details
approval: An Approval model instance containing approval details including uid, document_version relationship, approver_uids list, due_date, and optional instructions. Must have valid relationships to document_version and document.
notification_type: String specifying the type of notification to send. Valid values: 'APPROVAL_REQUESTED', 'APPROVAL_REMINDER', 'APPROVAL_OVERDUE', 'APPROVAL_COMPLETED', 'APPROVAL_REJECTED', 'DOCUMENT_SIGNED'. Determines the message content and email template used.
step: Optional string indicating the current approval step or stage in a multi-step approval process. Included in notification details and email data when provided.
approver_uids: Optional list of user UID strings to notify. If None, defaults to all approvers from approval.approver_uids. Allows targeting specific approvers for notifications rather than the entire approval group.
Return Value
Type: List[str]
Returns a list of strings containing the UIDs of successfully created notifications. Returns an empty list if no approvers are specified, if document/version retrieval fails, or if an exception occurs during notification processing. The UIDs can be used to track or reference the created notifications.
Dependencies
loggingtypingdatetimeCDocsCDocs.configCDocs.models.user_extensionsCDocs.utilsCDocs.controllers.document_controllermodels.reviewmodels.approval
Required Imports
import logging
from typing import List, Optional
from datetime import datetime
from CDocs.config import settings
from models.approval import Approval
Usage Example
# Assuming you have an approval instance from the database
from models.approval import Approval
from typing import List
# Get an approval instance
approval = Approval.query.filter_by(uid='approval-123').first()
# Send initial approval request to all approvers
notification_uids = notify_approval(
approval=approval,
notification_type='APPROVAL_REQUESTED',
step='Initial Review'
)
print(f"Created {len(notification_uids)} notifications")
# Send reminder to specific approvers
specific_approvers = ['user-uid-1', 'user-uid-2']
reminder_uids = notify_approval(
approval=approval,
notification_type='APPROVAL_REMINDER',
step='Final Review',
approver_uids=specific_approvers
)
# Send overdue notification
overdue_uids = notify_approval(
approval=approval,
notification_type='APPROVAL_OVERDUE'
)
# Notify completion
completion_uids = notify_approval(
approval=approval,
notification_type='APPROVAL_COMPLETED'
)
Best Practices
- Ensure the approval instance has valid relationships to document_version and document before calling this function
- The function logs extensively at INFO and DEBUG levels; configure logging appropriately for production environments
- Handle the returned list of notification UIDs to track notification success/failure
- Validate notification_type parameter against the supported types to avoid silent failures with None messages
- The function gracefully handles errors by returning empty lists; check return value length to detect failures
- Due dates are formatted differently for details (YYYY-MM-DD) and email display (Month DD, YYYY)
- Email templates must be properly configured in the email system for notifications to render correctly
- The function depends on send_notification being available in the same module scope
- Consider implementing retry logic for failed notifications at a higher level
- The approver_uids parameter allows selective notification, useful for escalation or targeted reminders
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
function complete_approval_v1 70.0% similar
-
function add_approver_to_active_approval_v1 69.1% similar
-
function send_notification 68.8% similar
-
function create_approval_cycle 68.6% similar
-
function notify_review 68.0% similar