# Imports
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.exceptions import ImproperlyConfigured
from django.core.mail import send_mail
from django.utils.module_loading import import_string
# from importlib import import_module
from superdjango.shortcuts import parse_template
from .constants import LEVEL
from .models import Notification, NotificationUser
UserModel = get_user_model()
# Exports
__all__ = (
"get_promoted_notification",
"get_sms_callback",
"get_unread_notification_count",
"get_unread_notifications",
"has_unread_notifications",
"notify",
"Message",
)
# Constants
FROM_EMAIL = getattr(settings, "SUPERDJANGO_NOTIFICATIONS_FROM_EMAIL")
FROM_NAME = getattr(settings, "SUPERDJANGO_NOTIFICATIONS_FROM_NAME", "Webmaster")
FROM_NUMBER = getattr(settings, "SUPERDJANGO_NOTIFICATIONS_FROM_NUMBER", None)
# Functions
[docs]def get_sms_callback(raise_exception=True):
"""Get the callback function used to send SMS/text messages.
The callback is defined using ``SUPERDJANGO_NOTIFICATIONS_SMS_CALLBACK`` which is the dotted path to a callable that
accepts a body, mobile number, and optionally a from number.
.. code-block:: python
def send_sms(body, number, from_number=None):
# ...
"""
dotted = getattr(settings, "SUPERDJANGO_NOTIFICATIONS_SMS_CALLBACK", None)
if dotted is None:
if raise_exception:
raise ImproperlyConfigured("You must define SUPERDJANGO_NOTIFICATIONS_SMS_CALLBACK.")
return None
try:
return import_string(dotted)
except ImportError as e:
if raise_exception:
raise ImproperlyConfigured('SMS callback is defined (%s) but could not be imported: %s' % (dotted, e))
return None
[docs]def get_unread_notification_count(user):
"""Get the unread notification count for a user.
:param user: The user instance to be checked.
:type user: AUTH_USER_MODEL
:rtype: int
"""
criteria = {
'has_been_viewed': False,
'user': user,
}
return NotificationUser.objects.filter(**criteria).count() or 0
[docs]def get_unread_notifications(user):
"""Get the unread notifications for a user.
:param user: The user instance to be checked.
:type user: AUTH_USER_MODEL
:rtype: QuerySet
"""
criteria = {
'has_been_viewed': False,
'user': user,
}
return NotificationUser.objects.filter(**criteria)
[docs]def has_unread_notifications(user):
"""Indicates whether a given user has unread notifications.
:param user: The user instance to be checked.
:type user: AUTH_USER_MODEL
:rtype: bool
"""
criteria = {
'has_been_viewed': False,
'user': user,
}
return NotificationUser.objects.filter(**criteria).exists()
[docs]def notify(body, subject, action_url=None, email_enabled=False, icon_override=None, level=LEVEL.INFO, users=None):
"""Issue a notification.
:param body: The body of the notification.
:type body: str
:param subject: The subject of the notification.
:type subject: str
:param action_url: The URL to which the user is directed.
:type action_url: str
:param email_enabled: Indicates the notification should be sent via email. Otherwise, it is only an in-app
notification.
:type email_enabled: bool
:param icon_override: The icon to use for the notification. Overrides the default.
:type icon_override: str
:param level: The notification level.
:type level: int
:param users: The users to which the notification should be sent. It omitted, all active users are notified.
:type users: list[AUTH_USER_MODEL]
:rtype: Notification
"""
notification = Notification(
action_url=action_url,
body=body,
icon_override=icon_override,
level=level,
subject=subject
)
notification.save()
if not email_enabled:
notification.mark_sent()
if users is None:
users = UserModel.objects.filter(is_active=True)
for user in users:
link = NotificationUser(notification=notification, user=user)
link.save()
return notification
[docs]class Message(object):
"""Represents a single email and/or SMS message to a user."""
[docs] def __init__(self, notification, user, preferences=None):
"""Initialize a message.
:param notification: The notification instance.
:type notification: superdjango.contrib.notifications.models.Notification
:param user: The user instance.
:type user: AUTH_USER_MODEL
:param preferences: The user preference instance.
:type preferences: superdjango.contrib.notifications.models.UserPreference
"""
self.notification = notification
self.preferences = preferences
self.user = user
[docs] def email(self):
"""Send a notification via email.
:rtype: bool
:returns: ``True`` if the email was sent.
"""
# Mandatory messages ignore preferences.
if self.notification.is_mandatory:
pass
else:
# Otherwise, preferences must be defined and email enabled.
if self.preferences is None:
return False
if not self.preferences.email_enabled:
return False
context = {
'from_email': FROM_EMAIL,
'from_name': FROM_NAME,
'notification': self.notification,
'user': self.user,
}
body = parse_template("notifications/templates/email_notification_body.txt", context)
body_html = None
if self.notification.html_enabled:
body_html = parse_template("notifications/templates/email_notification_body.html", context)
send_mail(
self.notification.subject,
body,
"%s <%s>" % (FROM_NAME, FROM_EMAIL),
[self.user.email],
html_message=body_html
)
return True
[docs] def sms(self):
"""Send a notification via SMS/text.
:rtype: bool
:returns: ``True`` if the SMS was sent.
"""
send_sms = get_sms_callback(raise_exception=False)
if send_sms is None:
return False
if self.preferences is None:
return False
if not self.preferences.sms_enabled:
return False
if not self.preferences.mobile_number:
return False
send_sms(
self.notification.body_sms,
self.preferences.mobile_number,
from_number=FROM_NUMBER
)
return True