🔍 Code Extractor

function send_email

Maturity: 81

Sends templated emails to one or more recipients using either MS365 or SMTP provider based on configuration, with support for CC, BCC, and attachments.

File:
/tf/active/vicechatdev/CDocs single class/utils/notifications.py
Lines:
569 - 641
Complexity:
moderate

Purpose

This function provides a unified interface for sending emails in the application, abstracting away the underlying email provider (MS365 or SMTP). It handles template rendering with dynamic data, automatic conversion to plain text, address normalization, and error handling. The function is designed to be used throughout the application for all email notifications, including user notifications, document reviews, approvals, and system alerts.

Source Code

def send_email(to_addresses: Union[str, List[str]], 
             subject: str, 
             template_name: str,
             template_data: Dict[str, Any] = None,
             cc_addresses: Union[str, List[str]] = None,
             bcc_addresses: Union[str, List[str]] = None,
             attachments: List[Dict[str, Any]] = None) -> bool:
    """
    Send email using configured method (MS365 or SMTP).
    
    Args:
        to_addresses: Recipient email address(es)
        subject: Email subject
        template_name: Name of email template to use
        template_data: Data to populate the template
        cc_addresses: Optional CC recipient(s)
        bcc_addresses: Optional BCC recipient(s)
        attachments: Optional list of attachments
        
    Returns:
        Boolean indicating success
    """
    try:
        # Convert single addresses to lists
        if isinstance(to_addresses, str):
            to_addresses = [to_addresses]
        if isinstance(cc_addresses, str):
            cc_addresses = [cc_addresses]
        if isinstance(bcc_addresses, str):
            bcc_addresses = [bcc_addresses]
            
        # Check that we have at least one recipient
        if not to_addresses:
            logger.error("No recipients specified")
            return False
            
        # Default template data
        data = template_data or {}
        data.update({
            'app_name': settings.APP_NAME,
            'app_url': settings.APP_URL,
            'current_year': datetime.now().year,
            'sender_name': settings.EMAIL_SENDER_NAME
        })
        
        # Get template content
        if template_name not in EMAIL_TEMPLATES:
            logger.error(f"Email template not found: {template_name}")
            return False
            
        template = EMAIL_TEMPLATES[template_name]
        
        # Render template
        body_html = render_template(template, data)
        
        # Generate plain text version
        body_text = html_to_text(body_html)
        
        # Send email using configured method
        if settings.EMAIL_PROVIDER == 'MS365':
            return send_email_ms365(
                to_addresses, subject, body_html, body_text,
                cc_addresses, bcc_addresses, attachments
            )
        else:
            return send_email_smtp(
                to_addresses, subject, body_html, body_text,
                cc_addresses, bcc_addresses, attachments
            )
            
    except Exception as e:
        logger.error(f"Error sending email: {e}")
        return False

Parameters

Name Type Default Kind
to_addresses Union[str, List[str]] - positional_or_keyword
subject str - positional_or_keyword
template_name str - positional_or_keyword
template_data Dict[str, Any] None positional_or_keyword
cc_addresses Union[str, List[str]] None positional_or_keyword
bcc_addresses Union[str, List[str]] None positional_or_keyword
attachments List[Dict[str, Any]] None positional_or_keyword

Parameter Details

to_addresses: Primary recipient email address(es). Can be a single email string or a list of email strings. At least one recipient must be provided or the function will return False.

subject: The subject line of the email. Should be a non-empty string that clearly describes the email content.

template_name: Name/key of the email template to use from the EMAIL_TEMPLATES dictionary. Must match an existing template key or the function will return False with an error logged.

template_data: Optional dictionary containing key-value pairs to populate template variables. If None, an empty dict is used. The function automatically adds 'app_name', 'app_url', 'current_year', and 'sender_name' to this data.

cc_addresses: Optional carbon copy recipient(s). Can be a single email string, a list of email strings, or None if no CC recipients are needed.

bcc_addresses: Optional blind carbon copy recipient(s). Can be a single email string, a list of email strings, or None if no BCC recipients are needed.

attachments: Optional list of attachment dictionaries. Each dictionary should contain attachment metadata (likely including 'filename', 'content', and 'content_type' keys based on typical email attachment patterns). Can be None if no attachments are needed.

Return Value

Type: bool

Returns a boolean value: True if the email was successfully sent through the configured provider (MS365 or SMTP), False if any error occurred including missing recipients, invalid template name, template rendering errors, or provider-specific sending failures. All errors are logged before returning False.

Dependencies

  • logging
  • datetime
  • typing
  • email.mime.multipart
  • email.mime.text
  • email.mime.application
  • smtplib
  • msal
  • requests
  • CDocs
  • CDocs.config
  • CDocs.models.user_extensions
  • CDocs.utils

Required Imports

from typing import Union, List, Dict, Any
from datetime import datetime
import logging

Conditional/Optional Imports

These imports are only needed under specific conditions:

from CDocs.config import settings

Condition: Required to access EMAIL_PROVIDER, APP_NAME, APP_URL, EMAIL_SENDER_NAME configuration

Required (conditional)
import smtplib

Condition: Only used if settings.EMAIL_PROVIDER is not 'MS365' (SMTP fallback)

Optional
import msal

Condition: Only used if settings.EMAIL_PROVIDER is 'MS365'

Optional
import requests

Condition: Only used if settings.EMAIL_PROVIDER is 'MS365' for API calls

Optional
from email.mime.multipart import MIMEMultipart

Condition: Only used if settings.EMAIL_PROVIDER is not 'MS365' (SMTP fallback)

Optional
from email.mime.text import MIMEText

Condition: Only used if settings.EMAIL_PROVIDER is not 'MS365' (SMTP fallback)

Optional
from email.mime.application import MIMEApplication

Condition: Only used if settings.EMAIL_PROVIDER is not 'MS365' and attachments are provided

Optional

Usage Example

# Basic usage with single recipient
success = send_email(
    to_addresses='user@example.com',
    subject='Welcome to Our Application',
    template_name='welcome_email',
    template_data={'user_name': 'John Doe'}
)

# Advanced usage with multiple recipients and attachments
success = send_email(
    to_addresses=['user1@example.com', 'user2@example.com'],
    subject='Document Review Required',
    template_name='review_notification',
    template_data={
        'document_name': 'Q4 Report',
        'reviewer_name': 'Jane Smith',
        'due_date': '2024-01-15'
    },
    cc_addresses='manager@example.com',
    bcc_addresses=['audit@example.com', 'compliance@example.com'],
    attachments=[
        {
            'filename': 'report.pdf',
            'content': pdf_bytes,
            'content_type': 'application/pdf'
        }
    ]
)

if success:
    print('Email sent successfully')
else:
    print('Failed to send email - check logs for details')

Best Practices

  • Always check the return value to verify email was sent successfully before proceeding with dependent operations
  • Ensure EMAIL_TEMPLATES dictionary is populated with all required templates before calling this function
  • Validate email addresses before passing to this function to avoid silent failures
  • Use meaningful template_name values that clearly indicate the email purpose
  • Keep template_data keys consistent with template variable names to avoid rendering errors
  • Configure appropriate logging to capture email sending failures for debugging
  • Test both MS365 and SMTP configurations if your application supports multiple providers
  • Be cautious with BCC addresses for privacy/compliance reasons - document their usage
  • Consider rate limiting when sending bulk emails to avoid provider throttling
  • Ensure attachments are properly formatted with required keys (filename, content, content_type)
  • The function automatically adds standard template variables (app_name, app_url, current_year, sender_name) so avoid conflicts in template_data
  • Single string addresses are automatically converted to lists, but providing lists is more explicit and recommended for multiple recipients

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function gen_send_email 95.6% similar

    Sends templated emails using either MS365 or SMTP provider, with support for multiple recipients, attachments, and HTML/text rendering.

    From: /tf/active/vicechatdev/CDocs/utils/notifications.py
  • function send_email_ms365 78.0% similar

    Sends an email through Microsoft 365 Graph API with support for HTML content, multiple recipients (to/cc/bcc), and file attachments.

    From: /tf/active/vicechatdev/CDocs/utils/notifications.py
  • function send_email_smtp 77.9% similar

    Sends emails via SMTP server with support for HTML/text content, multiple recipients (to/cc/bcc), and file attachments.

    From: /tf/active/vicechatdev/CDocs/utils/notifications.py
  • function send_email_ms365_v1 77.7% similar

    Sends an email through the Microsoft 365 Graph API with support for HTML content, multiple recipients, CC/BCC, and file attachments.

    From: /tf/active/vicechatdev/CDocs single class/utils/notifications.py
  • function send_test_email 59.0% similar

    Sends a test email via SMTP to verify email forwarding service functionality, creating a MIME multipart message with customizable sender, recipient, subject, and body content.

    From: /tf/active/vicechatdev/email-forwarder/send_test_email.py
← Back to Browse