Package: django-herald
django-heraldA Django messaging library that features:Class-based declaration and registry approach, like Django AdminSupports multiple transmission methods (Email, SMS, Slack, etc) per messageBrowser-based previewing of messagesMaintains a history of messaging sending attempts and can view these messagesDisabling notifications per userPython/Django SupportWe try to make herald support all versions of django that django supports + all versions in between.For python, herald supports all versions of python that the above versions of django support.So as of herald v0.3 we support django 3.2 and 4.x+, and python 3.6, 3.7, 3.8, 3.9, and 3.10.Installationpip install django-heraldAddheraldanddjango.contrib.sitestoINSTALLED_APPS.Add herald's URLS:fromdjango.confimportsettingsfromdjango.conf.urlsimporturl,includeurlpatterns=[]ifsettings.DEBUG:urlpatterns=[url(r'^herald/',include('herald.urls')),]+urlpatternsUsageCreate anotifications.pyfile in any django app. This is where your notification classes will live. Add a class like this:fromheraldimportregistryfromherald.baseimportEmailNotificationclassWelcomeEmail(EmailNotification):# extend from EmailNotification for emailstemplate_name='welcome_email'# name of template, without extensionsubject='Welcome'# subject of emaildef__init__(self,user):# optionally customize the initializationself.context={'user':user}# set context for the template renderingself.to_emails=[user.email]# set list of emails to send to@staticmethoddefget_demo_args():# define a static method to return list of args needed to initialize class for testingfromusers.modelsimportUserreturn[User.objects.order_by('?')[0]]registry.register(WelcomeEmail)# finally, register your notification class# Alternatively, a class decorator can be used to register the notification:@registry.register_decorator()classWelcomeEmail(EmailNotification):...Create templates for rendering the email using this file structure:templates/
     herald/
         text/
             welcome_email.txt
         html/
             welcome_email.htmlTest how your email looks by navigating to/herald/.Send your email wherever you need in your code:WelcomeEmail(user).send()View the sent emails in django admin and even be able to resend it.Email optionsThe following options can be set on the email notification class. For Example:class WelcomeEmail(EmailNotification):
    cc = ['test@example.com']from_email: (str, default:settings.DEFAULT_FROM_EMAIL) email address of sendersubject: (str, default: ) email subjectto_emails: (List[str], default:None) list of email strings to send tobcc: (List[str], default:None) list of email strings to send as bcccc: (List[str], default:None) list of email strings to send as ccheaders: (dict, default:None) extra headers to be passed along to theEmailMultiAlternativesobjectreply_to: (List[str], default:None) list of email strings to send as the Reply-To emailsattachments: (list) list of attachments. See "Email Attachments" below for more infoAutomatically Deleting Old NotificationsHerald can automatically delete old notifications whenever a new notification is sent.To enable this, set theHERALD_NOTIFICATION_RETENTION_TIMEsetting to a timedelta instance.For example:HERALD_NOTIFICATION_RETENTION_TIME = timedelta(weeks=8)Will delete all notifications older than 8 weeks every time a new notification is sent.Manually Deleting Old NotificationsThedelnotifscommand is useful for purging the notification history.The default usage will delete everything from sent during today:pythonmanage.pydelnotifsHowever, you can also pass arguments forstartorenddates.endis up to, but not including that date.if onlyendis specified, delete anything sent before the end date.if onlystartis specified, delete anything sent since the start date.if bothstartandendare specified, delete anything sent in between, not including the end date.pythonmanage.pydelnotifs--start='2016-01-01'--end='2016-01-10'Asynchronous Email SendingIf you are sending slightly different emails to a large number of people, it might take quite a while to process. By default, Django will process this all synchronously. For asynchronous support, we recommend django-celery-email. It is very straightfoward to setup and integrate:https://github.com/pmclanahan/django-celery-emailherald.contrib.authDjango has built-in support for sending password reset emails. If you would like to send those emails using herald, you can use the notification class in herald.contrib.auth.First, addherald.contrib.authtoINSTALLED_APPS(in addition toherald).Second, use theHeraldPasswordResetFormin place of django's built inPasswordResetForm. This step is entirely dependant on your project structure, but it essentially just involves changing the form class on the password reset view in some way:# you may simply just need to override the password reset url like so:url(r'^password_reset/$',password_reset,name='password_reset',{'password_reset_form':HeraldPasswordResetForm}),# of if you are using something like django-authtools:url(r'^password_reset/$',PasswordResetView.as_view(form_class=HeraldPasswordResetForm),name='password_reset'),# or you may have a customized version of the password reset view:classMyPasswordResetView(FormView):form_class=HeraldPasswordResetForm# change the form class here# or, you may have a custom password reset form already. In that case, you will want to extend from the HeraldPasswordResetForm:classMyPasswordResetForm(HeraldPasswordResetForm):...# alternatively, you could even just send the notification wherever you wish, seperate from the form:PasswordResetEmail(some_user).send()Third, you may want to customize the templates for the email. By default, herald will use theregistration/password_reset_email.htmlthat is provided by django for both the html and text versions of the email. But you can simply overrideherald/html/password_reset.htmland/orherald/text/password_reset.txtto suit your needs.User Disabled NotificationsIf you want to disable certain notifications per user, add a record to the UserNotification table and
add notifications to the disabled_notifications many to many table.For example:user=User.objects.get(id=user.id)notification=Notification.objects.get(notification_class=MyNotification.get_class_path())# disable the notificationuser.usernotification.disabled_notifications.add(notification)By default, notifications can be disabled.  You can put can_disable = False in your notification class and the system will
populate the database with this default.  Your Notification class can also override the verbose_name by setting it in your
inherited Notification class.  Like this:classMyNotification(EmailNotification):can_disable=Falseverbose_name="My Required Notification"Email AttachmentsTo send attachments, assign a list of attachments to the attachments attribute of your EmailNotification instance, or override the get_attachments() method.Each attachment in the list can be one of the following:A tuple which consists of the filename, the raw attachment data, and the mimetype. It is up to you to get the attachment data. Like this:raw_data=get_pdf_data()email.attachments=[('Report.pdf',raw_data,'application/pdf'),('report.txt','text version of report','text/plain')]email.send()A MIMEBase object. See the documentation for attachments under EmailMessage Objects/attachments in the Django documentation.A djangoFileobject.Inline AttachmentsSometimes you want to embed an image directly into the email content.  Do that by using a MIMEImage assigning a content id header to a MIMEImage, like this:email=WelcomeEmail(user)im=get_thumbnail(image_file.name,'600x600',quality=95)my_image=MIMEImage(im.read())# MIMEImage inherits from MIMEBasemy_image.add_header('Content-ID','<{}>'.format(image_file.name))You can refer to these images in your html email templates using the Content ID (cid) like this:<imgsrc="cid:{{image_file.name}}"/>You would of course need to add the "image_file" to your template context in the example above.  You can also accomplish this using file operations.  In this example we overrode the get_attachments method of an EmailNotification.classMyNotification(EmailNotification):context={'hello':'world'}template_name='welcome_email'to_emails=['somebody@example.com']subject="My email test"defget_attachments(self):fp=open('python.jpeg','rb')img=MIMEImage(fp.read())img.add_header('Content-ID','<{}>'.format('python.jpeg'))return[img,]And in your template you would refer to it like this, and you would not need to add anything to the context:<imgsrc="cid:python.jpeg"/>HTML2Text SupportDjango Herald can auto convert your HTML emails to plain text.  Any email without a plain text version
will be auto converted if you enable this feature.# Install html2text
pip install django-herald[html2text]In your settings.py file:HERALD_HTML2TEXT_ENABLED = TrueYou can customize the output of HTML2Text by setting a configuration dictionary. SeeHTML2Text Configurationfor optionsHERALD_HTML2TEXT_CONFIG = {
    # Key / value configuration of html2text 
    'ignore_images': True  # Ignores images in conversion
}HERALD_RAISE_MISSING_TEMPLATES = TrueBy default, Herald will raise an exception if a template is missing when true (default).Twilio# Install twilio
pip install django-herald[twilio]You can retrieve these values onTwilio Console. Once you have retrieve the necessary ids, you can place those to yoursettings.py.For reference, Twilio has some great tutorials for python.Twilio Python Tutorial# Twilio configurations
# values taken from `twilio console`
TWILIO_ACCOUNT_SID = "your_account_sid"
TWILIO_AUTH_TOKEN = "your_auth_token"
TWILIO_DEFAULT_FROM_NUMBER = "+1234567890"Other MIME attachmentsYou can also attach any MIMEBase objects as regular attachments, but you must add a content-disposition header, or they will be inaccessible:my_image.add_header('Content-Disposition','attachment; filename="python.jpg"')Attachments can cause your database to become quite large, so you should be sure to run the management commands to purge the database of old messages.Running Testspythonruntests.py