File: email_notifications.py

package info (click to toggle)
python-django-extensions 4.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,820 kB
  • sloc: python: 18,601; javascript: 7,354; makefile: 108; xml: 17
file content (150 lines) | stat: -rw-r--r-- 5,333 bytes parent folder | download
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),
        )