1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
# -*- coding: utf-8 -*-
import sys
import traceback
from django.conf import settings
from django.core.mail import send_mail
from django.core.management import BaseCommand
class EmailNotificationCommand(BaseCommand):
"""
A BaseCommand subclass which adds sending email fuctionality.
Subclasses will have an extra command line option ``--email-notification``
and will be able to send emails by calling ``send_email_notification()``
if SMTP host and port are specified in settings. The handling of the
command line option is left to the management command implementation.
Configuration is done in settings.EMAIL_NOTIFICATIONS dict.
Configuration example::
EMAIL_NOTIFICATIONS = {
'scripts.my_script': {
'subject': 'my_script subject',
'body': 'my_script body',
'from_email': 'from_email@example.com',
'recipients': ('recipient0@example.com',),
'no_admins': False,
'no_traceback': False,
'notification_level': 0,
'fail_silently': False
},
'scripts.another_script': {
...
},
...
}
Configuration explained:
subject: Email subject.
body: Email body.
from_email: Email from address.
recipients: Sequence of email recipient addresses.
no_admins: When True do not include ADMINS to recipients.
no_traceback: When True do not include traceback to email body.
notification_level: 0: send email on fail, 1: send email always.
fail_silently: Parameter passed to django's send_mail().
"""
def add_arguments(self, parser):
parser.add_argument(
"--email-notifications",
action="store_true",
default=False,
dest="email_notifications",
help="Send email notifications for command.",
)
parser.add_argument(
"--email-exception",
action="store_true",
default=False,
dest="email_exception",
help="Send email for command exceptions.",
)
def run_from_argv(self, argv):
"""Overriden in order to access the command line arguments."""
self.argv_string = " ".join(argv)
super().run_from_argv(argv)
def execute(self, *args, **options):
"""
Overriden in order to send emails on unhandled exception.
If an unhandled exception in ``def handle(self, *args, **options)``
occurs and `--email-exception` is set or `self.email_exception` is
set to True send an email to ADMINS with the traceback and then
reraise the exception.
"""
try:
super().execute(*args, **options)
except Exception:
if options["email_exception"] or getattr(self, "email_exception", False):
self.send_email_notification(include_traceback=True)
raise
def send_email_notification(
self, notification_id=None, include_traceback=False, verbosity=1
):
"""
Send email notifications.
Reads settings from settings.EMAIL_NOTIFICATIONS dict, if available,
using ``notification_id`` as a key or else provides reasonable
defaults.
"""
# Load email notification settings if available
if notification_id is not None:
try:
email_settings = settings.EMAIL_NOTIFICATIONS.get(notification_id, {})
except AttributeError:
email_settings = {}
else:
email_settings = {}
# Exit if no traceback found and not in 'notify always' mode
if not include_traceback and not email_settings.get("notification_level", 0):
print(self.style.ERROR("Exiting, not in 'notify always' mode."))
return
# Set email fields.
subject = email_settings.get("subject", "Django extensions email notification.")
command_name = self.__module__.split(".")[-1]
body = email_settings.get(
"body", "Reporting execution of command: '%s'" % command_name
)
# Include traceback
if include_traceback and not email_settings.get("no_traceback", False):
try:
exc_type, exc_value, exc_traceback = sys.exc_info()
trb = "".join(traceback.format_tb(exc_traceback))
body += "\n\nTraceback:\n\n%s\n" % trb
finally:
del exc_traceback
# Set from address
from_email = email_settings.get("from_email", settings.DEFAULT_FROM_EMAIL)
# Calculate recipients
recipients = list(email_settings.get("recipients", []))
if not email_settings.get("no_admins", False):
recipients.extend(settings.ADMINS)
if not recipients:
if verbosity > 0:
print(self.style.ERROR("No email recipients available."))
return
# Send email...
send_mail(
subject,
body,
from_email,
recipients,
fail_silently=email_settings.get("fail_silently", True),
)
|