🔍 Code Extractor

function gen_send_email

Maturity: 77

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

File:
/tf/active/vicechatdev/CDocs/utils/notifications.py
Lines:
613 - 702
Complexity:
moderate

Purpose

This function provides a unified interface for sending emails through different providers (MS365 or SMTP). It handles template rendering with dynamic data, converts single recipients to lists, generates both HTML and plain text versions, validates template placeholders, and routes to the appropriate email provider based on configuration. It's designed for application-level email notifications with consistent branding and formatting.

Source Code

def gen_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,
            'cdocs_app_url': 'https://cdocs.vicebio.com',
            'cdocs_app_text': 'Access the CDocs Application'
        })
        
        # Add debug logging to help troubleshoot template issues
        logger.info(f"Sending email with template '{template_name}' to {to_addresses}")
        logger.debug(f"Template data keys: {list(data.keys())}")
        logger.debug(f"Template data values: {data}")
        
        # 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)
        
        # Check if any placeholders remain unreplaced (debugging)
        import re
        remaining_placeholders = re.findall(r'{{[^}]*}}', body_html)
        if remaining_placeholders:
            logger.warning(f"Template '{template_name}' has unreplaced placeholders: {remaining_placeholders}")
            logger.debug(f"First 500 chars of rendered template: {body_html[:500]}")
        else:
            logger.debug(f"Template '{template_name}' rendered successfully, all placeholders replaced")
        
        # Generate plain text version
        body_text = html_to_text(body_html)
        logger.info("Generated email body HTML and text")
        # Send email using configured method
        if settings.EMAIL_PROVIDER == 'MS365':
            logger.info("Sending email using 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 string email address or a list of email addresses. At least one recipient is required or the function returns False.

subject: The subject line of the email as a string. This will be displayed in the recipient's inbox.

template_name: Name/key of the email template to use from the EMAIL_TEMPLATES dictionary. Must exist in EMAIL_TEMPLATES or the function returns False.

template_data: Optional dictionary containing key-value pairs to populate template placeholders. If None, an empty dict is used. The function automatically adds default keys like 'app_name', 'app_url', 'current_year', 'sender_name', 'cdocs_app_url', and 'cdocs_app_text'.

cc_addresses: Optional carbon copy recipient(s). Can be a single string email address or a list of email addresses. Will be converted to list internally.

bcc_addresses: Optional blind carbon copy recipient(s). Can be a single string email address or a list of email addresses. Will be converted to list internally.

attachments: Optional list of attachment dictionaries. Each dictionary should contain attachment metadata (format depends on the underlying send_email_ms365 or send_email_smtp function requirements).

Return Value

Type: bool

Returns a boolean value: True if the email was sent successfully through the configured provider (MS365 or SMTP), False if there was an error (no recipients, template not found, rendering failure, or provider send failure).

Dependencies

  • logging
  • datetime
  • re
  • msal
  • requests
  • smtplib
  • email.mime.multipart
  • email.mime.text
  • email.mime.application

Required Imports

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

Conditional/Optional Imports

These imports are only needed under specific conditions:

from CDocs import db

Condition: Required for database access if used by underlying email functions

Required (conditional)
from CDocs.config import settings

Condition: Required for configuration values like EMAIL_PROVIDER, APP_NAME, APP_URL, EMAIL_SENDER_NAME

Required (conditional)
from CDocs.models.user_extensions import DocUser

Condition: May be used by template rendering or underlying email functions

Optional
from CDocs.utils import audit_trail

Condition: May be used for logging email activities

Optional
from CDocs.controllers.document_controller import get_document_edit_url

Condition: May be used in template data preparation

Optional

Usage Example

# Basic usage with single recipient
success = gen_send_email(
    to_addresses='user@example.com',
    subject='Welcome to CDocs',
    template_name='welcome_email',
    template_data={'user_name': 'John Doe', 'activation_link': 'https://app.com/activate/123'}
)

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

if success:
    print('Email sent successfully')
else:
    print('Failed to send email')

Best Practices

  • Always check the return value to verify email was sent successfully
  • Ensure EMAIL_TEMPLATES dictionary contains the template_name before calling
  • Provide all required template_data keys expected by your template to avoid unreplaced placeholders
  • The function automatically adds common template variables (app_name, app_url, current_year, etc.), so don't override these unless necessary
  • Use the logging output to debug template rendering issues - the function logs unreplaced placeholders
  • Configure settings.EMAIL_PROVIDER correctly ('MS365' or other for SMTP)
  • For production use, ensure proper error handling around this function call
  • The function converts single string addresses to lists automatically, so both formats are acceptable
  • Attachments format must match the expectations of the underlying send_email_ms365 or send_email_smtp functions
  • Template placeholders should use {{variable_name}} format based on the regex pattern used for validation

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function send_email_smtp 76.6% 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 75.4% 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_test_email 61.3% 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
  • function test_send_email 57.7% similar

    Interactive test function that prompts the user to send a test email through the O365Client to verify email sending functionality.

    From: /tf/active/vicechatdev/email-forwarder/test_service.py
  • function main_v31 57.3% similar

    A test function that validates email template rendering by testing multiple HTML email templates with sample data structures for document review and approval workflows.

    From: /tf/active/vicechatdev/test_comprehensive_templates.py
← Back to Browse