Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

nickfrez / unb-djutils   python

Repository URL to install this package:

Version: 0.0.24 

/ unb_djutils / email.py

"""
Templated Email
===============

Class-based, multi-part emails with template support.


TODO(nick): pynliner allows css to be written in a separate file, then
  automatically inlined into the html (essential for email html messages).
  There may be a significant performance penalty if doing this on the fly
  (according to some random package's docs), so an alternative would be to
  have a build/pre-process step that inlines css into the templates
  themselves, instead of into the generated html.

"""

from django.conf import settings
from django.core.mail import EmailMultiAlternatives
from django.template import Context, Template
from django.template.loader import get_template



class Email(object):
  """Class-based, multi-part emails with template support.

  Class attributes (to, from_email, etc.) provide a simple way to define static
  default values.

  default_* methods provide an easy way for subclasses to provide runtime
  computed values.

  Defaults are override-able by providing appropriate arguments to __init__.

  Precedence: parameters > default_* methods > class attributes

  Args
  ----
  to : str or list
      Recipient of the email.
  from_email : str
      Sender's address.
  subject : str
      The subject of the email.  The subject is processed by the template
      engine, using the same context as the email body, and therefore may
      contain template tags.
  template_name : str
      The app path to the template, without a suffix.  Email will check for
      both `template_name.txt` and `template_name.html` at that location.
  text_template : str
      The app path to the text template to use as the body of the email.  If
      provided, this will override the `template_name` argument.
  html_template : str
      The app path to the html template to use as the body of the email.  If
      provided, this will override the `template_name` argument.
  context : dict
      The context to use when rendering templates.  This will be merged with
      the default context, overwriting any existing values in the default
      context.

  Example
  -------

  A simple example:

      from unb.utils import email

      class NewSignupEmail(email.Email):
        subject = "{{email}} has signed up!"
        template_name = 'signups/emails/new_signup'

        def default_context(self):
          return {
            'email': self.to,
          }

      # Usage
      # -----

      e = NewSignupEmail(to='nick@unb.services')
      e.send()


  A more complicated example:

      from unb.utils import email

      class NewSignupEmail(email.Email):
        text_template = 'signups/emails/new_signup.txt'
        html_template = 'signups/emails/new_signup.html'

        @property
        def user(self):
          return self.kwargs['user']

        def default_to(self):
          return self.user.email

        def default_subject(self):
          if self.user.name:
            return self.user.name + " has signed up!"
          if self.user.username:
            return self.user.username + " has signed up!"
          return self.subject

        def default_context(self):
          return {
            'username': self.user.username,
            'email': self.user.email,
          }


      # Usage
      # -----

      from unb.users.models import User

      u = User.objects.get('username'='nick')
      e = NewSignupEmail(user=u)
      e.send()

  """

  to = None
  from_email = None
  subject = ''
  template_name = None
  text_template = None
  html_template = None

  def __init__(self,
               to=None,
               from_email=None,
               subject=None,
               template_name=None,
               text_template=None,
               html_template=None,
               context=None,
               **kwargs):
    self.kwargs = kwargs
    self.to = to or self.default_to()
    if not isinstance(self.to, list) or not isinstance(self.to, tuple):
      self.to = [self.to]
    self.from_email = (
      from_email or self.default_from_email() or settings.DEFAULT_FROM_EMAIL)
    self.subject = subject or self.default_subject()
    self.template_name = template_name or self.default_template_name()
    self.text_template = text_template or self.default_text_template()
    self.html_template = html_template or self.default_html_template()
    ctx = self.default_context()
    if context:
      ctx.update(context)
    self.context = Context(ctx)

  def _update_context(self, ctx):
    if ctx:
      self.context.update(ctx)
    return self.context

  def default_to(self):
    return self.to

  def default_from_email(self):
    return self.from_email

  def default_subject(self):
    return self.subject

  def default_template_name(self):
    return self.template_name

  def default_text_template(self):
    return self.text_template

  def default_html_template(self):
    return self.html_template

  def default_context(self):
    return {}

  def render_string(self, string, ctx):
    if string:
      return Template(string).render(ctx)
    else:
      return ''

  def render_template(self, template, ctx):
    if template:
      template = get_template(template)
      return template.render(ctx)
    else:
      return ''

  def message(self, context=None):
    """Get a compiled message without sending it."""
    ctx = self._update_context(context)

    # Render templates
    subject = self.render_string(self.subject, ctx)
    text = self.render_template(self.text_template, ctx)
    html = self.render_template(self.html_template, ctx)

    # Construct the message
    msg = EmailMultiAlternatives(
      to=self.to,
      from_email=self.from_email,
      subject=subject,
      body=text)
    if html:
      msg.attach_alternative(html, 'text/html')
    return msg

  def send(self, context=None):
    """Compile and send the message."""
    msg = self.message(context=context)
    msg.send()