🔍 Code Extractor

function send_notification

Maturity: 85

Sends in-app notifications to one or more users and optionally sends corresponding email notifications using templates.

File:
/tf/active/vicechatdev/CDocs/utils/notifications.py
Lines:
1132 - 1234
Complexity:
moderate

Purpose

This function provides a unified interface for sending notifications to users in a document management system. It handles both in-app notifications (stored in database) and email notifications. It supports flexible user input (DocUser objects or UID strings), resource linking, custom messages, and templated emails. The function is designed to notify users about various events like document updates, reviews, approvals, or system alerts.

Source Code

def send_notification(notification_type: str,
                     users: Union[List[Union[DocUser, str]], DocUser, str],
                     resource_uid: Optional[str] = None,
                     resource_type: Optional[str] = None,
                     message: Optional[str] = None,
                     details: Optional[Dict[str, Any]] = None,
                     send_email: bool = False,
                     email_template: Optional[str] = None,
                     email_data: Optional[Dict[str, Any]] = None) -> List[str]:
    """
    Send a notification to one or more users, optionally with email.
    
    Args:
        notification_type: Type of notification from NOTIFICATION_TYPES
        users: User(s) to notify (DocUser instance(s) or UID(s))
        resource_uid: Optional UID of resource this notification is about
        resource_type: Optional type of resource
        message: Optional message text
        details: Optional additional details
        send_email: Whether to also send email notification
        email_template: Template to use for email (if sending email)
        email_data: Data for email template (if sending email)
        
    Returns:
        List of created notification UIDs
    """
    logger.info(f"Sending notification of type {notification_type} to users: {users}")
    # Ensure users is a list
    if not isinstance(users, list):
        users = [users]
        
    # Extract UIDs and emails
    user_uids = []
    user_emails = []
    for user in users:
        if isinstance(user, DocUser):
            user_uids.append(user.uid)
            if user.email:
                user_emails.append(user.email)
        else:
            user_uids.append(user)
            # Try to get email from database
            result = db.run_query(
                "MATCH (u:User {UID: $uid}) RETURN u.Mail as email",
                {"uid": user}
            )
            if result and result[0].get('email'):
                user_emails.append(result[0]['email'])
    
    # Create in-app notifications
    notification_uids = []
    for user_uid in user_uids:
        notification_uid = create_notification(
            notification_type, user_uid, resource_uid, 
            resource_type, message, details
        )
        if notification_uid:
            notification_uids.append(notification_uid)
    
    # Send email if requested
    if send_email and user_emails:
        # Get notification info
        notification_info = NOTIFICATION_TYPES.get(notification_type, {})
        
        # Prepare subject
        subject = notification_info.get('subject', 'Notification from ' + settings.APP_NAME)
        
        # Format subject with details if available
        if details:
            try:
                subject = subject.format(**details)
            except KeyError:
                pass
                
        # Default to message as subject if formatting fails
        if subject == notification_info.get('subject') and message:
            subject = message
            
        # Send email
        if email_template:
            # Merge details and email_data
            template_data = {}
            if details:
                template_data.update(details)
                logger.debug(f"Added details to template_data: {list(details.keys()) if details else 'None'}")
            if email_data:
                template_data.update(email_data)
                logger.debug(f"Added email_data to template_data: {list(email_data.keys()) if email_data else 'None'}")
                logger.debug(f"Email_data contents: {email_data}")
            else:
                logger.warning(f"No email_data provided for template '{email_template}'")
                
            # Add message if provided
            if message:
                template_data['message'] = message
                
            logger.debug(f"Final template_data keys for '{email_template}': {list(template_data.keys())}")
            
            # Send email
            logger.info(f"Sending email to {user_emails} with subject: {subject}")
            gen_send_email(user_emails, subject, email_template, template_data)
    
    return notification_uids

Parameters

Name Type Default Kind
notification_type str - positional_or_keyword
users Union[List[Union[DocUser, str]], DocUser, str] - positional_or_keyword
resource_uid Optional[str] None positional_or_keyword
resource_type Optional[str] None positional_or_keyword
message Optional[str] None positional_or_keyword
details Optional[Dict[str, Any]] None positional_or_keyword
send_email bool False positional_or_keyword
email_template Optional[str] None positional_or_keyword
email_data Optional[Dict[str, Any]] None positional_or_keyword

Parameter Details

notification_type: String identifier for the type of notification, must match a key in NOTIFICATION_TYPES dictionary. This determines the notification category and default subject line for emails.

users: Flexible input accepting a single user or list of users. Each user can be either a DocUser model instance or a string UID. The function normalizes this to extract UIDs and email addresses.

resource_uid: Optional string UID of the resource (e.g., document, review cycle) that this notification relates to. Used for linking notifications to specific resources in the system.

resource_type: Optional string describing the type of resource (e.g., 'document', 'review', 'approval'). Helps categorize what the notification is about.

message: Optional custom message text for the notification. Can be used as fallback email subject if template formatting fails.

details: Optional dictionary containing additional structured data about the notification. Used for subject line formatting and merged into email template data.

send_email: Boolean flag (default False) indicating whether to send email notifications in addition to in-app notifications.

email_template: Optional string name of the email template to use when send_email is True. Template should exist in the email system.

email_data: Optional dictionary containing data specifically for email template rendering. Merged with details dictionary, with email_data taking precedence.

Return Value

Type: List[str]

Returns a list of strings, where each string is a UID of a successfully created in-app notification. The list length corresponds to the number of users notified. Returns empty list if no notifications were created.

Dependencies

  • logging
  • typing
  • CDocs
  • smtplib
  • email.mime
  • msal
  • requests

Required Imports

from typing import List, Union, Optional, Dict, Any
from CDocs.models.user_extensions import DocUser
from CDocs import db
from CDocs.config import settings
import logging

Conditional/Optional Imports

These imports are only needed under specific conditions:

from CDocs.controllers.document_controller import get_document_edit_url

Condition: May be used by email templates or notification creation logic

Optional
from models.review import ReviewCycle

Condition: If notifications relate to review cycles

Optional
from models.approval import Approval

Condition: If notifications relate to approval workflows

Optional

Usage Example

# Example 1: Simple in-app notification
notification_uids = send_notification(
    notification_type='document_updated',
    users='user123',
    resource_uid='doc456',
    resource_type='document',
    message='Your document has been updated'
)

# Example 2: Notification with email to multiple users
from CDocs.models.user_extensions import DocUser

users = [DocUser(uid='user1', email='user1@example.com'), 'user2']
notification_uids = send_notification(
    notification_type='review_assigned',
    users=users,
    resource_uid='review789',
    resource_type='review',
    message='You have been assigned a new review',
    details={'document_name': 'Q4 Report', 'due_date': '2024-01-15'},
    send_email=True,
    email_template='review_assignment',
    email_data={'reviewer_name': 'John Doe', 'document_link': 'https://app.example.com/doc/123'}
)

# Example 3: Bulk notification with template formatting
notification_uids = send_notification(
    notification_type='approval_required',
    users=['user1', 'user2', 'user3'],
    resource_uid='approval101',
    resource_type='approval',
    details={'approver_count': 3, 'deadline': '2024-01-20'},
    send_email=True,
    email_template='approval_request',
    email_data={'document_title': 'Budget Proposal 2024'}
)

Best Practices

  • Always define notification_type in NOTIFICATION_TYPES dictionary before calling this function
  • When using send_email=True, ensure email_template exists and email_data contains all required template variables
  • The details dictionary is merged with email_data, with email_data taking precedence for duplicate keys
  • User UIDs should exist in the database to retrieve email addresses when passing string UIDs
  • Handle the returned notification_uids list to track which notifications were successfully created
  • Use DocUser instances when possible to avoid additional database queries for email addresses
  • The function logs extensively - ensure logging is configured appropriately for debugging
  • Subject line formatting uses details dictionary with .format() - ensure keys match placeholders in NOTIFICATION_TYPES subject templates
  • If email sending fails, in-app notifications are still created - implement error handling for email failures if needed
  • Consider rate limiting or batching when sending notifications to large user lists

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function create_notification 74.8% similar

    Creates an in-app notification for a user in a graph database, linking it to the user and optionally to a related resource.

    From: /tf/active/vicechatdev/CDocs/utils/notifications.py
  • function notify_approval 68.8% similar

    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.

    From: /tf/active/vicechatdev/CDocs/utils/notifications.py
  • function notify_document_update 65.1% similar

    Sends notifications to interested users (owners, department members, subscribers, and users with permissions) when a controlled document is created, updated, or has a status/version change.

    From: /tf/active/vicechatdev/CDocs/utils/notifications.py
  • function notify_review 64.3% similar

    Sends review notifications to specified reviewers for a document review cycle, supporting multiple notification types (requested, reminder, overdue, completed).

    From: /tf/active/vicechatdev/CDocs/utils/notifications.py
  • function get_user_notifications 54.6% similar

    Retrieves notifications for a specific user from a Neo4j graph database, with options for filtering unread notifications and pagination support.

    From: /tf/active/vicechatdev/CDocs/utils/notifications.py
← Back to Browse