function gen_send_email
Sends templated emails using either MS365 or SMTP provider, with support for multiple recipients, attachments, and HTML/text rendering.
/tf/active/vicechatdev/CDocs/utils/notifications.py
613 - 702
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
loggingdatetimeremsalrequestssmtplibemail.mime.multipartemail.mime.textemail.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
Optionalfrom CDocs.utils import audit_trail
Condition: May be used for logging email activities
Optionalfrom CDocs.controllers.document_controller import get_document_edit_url
Condition: May be used in template data preparation
OptionalUsage 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
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
function send_email_smtp 76.6% similar
-
function send_email_ms365 75.4% similar
-
function send_test_email 61.3% similar
-
function test_send_email 57.7% similar
-
function main_v31 57.3% similar