Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Imports
3from django.conf import settings
4from django.contrib.auth import get_user_model
5from django.core.exceptions import ImproperlyConfigured
6from django.core.mail import send_mail
7from importlib import import_module
8from superdjango.shortcuts import has_o2o, parse_template
9from .constants import LEVEL, STAGE
10from .models import Notification, NotificationUser
12UserModel = get_user_model()
14# Exports
16__all__ = (
17 "get_promoted_notification",
18 "get_sms_callback",
19 "get_unread_notification_count",
20 "get_unread_notifications",
21 "has_unread_notifications",
22 "notify",
23 "Message",
24)
26# Constants
28FROM_EMAIL = getattr(settings, "SUPERDJANGO_NOTIFICATIONS_FROM_EMAIL")
29FROM_NAME = getattr(settings, "SUPERDJANGO_NOTIFICATIONS_FROM_NAME", "Webmaster")
30FROM_NUMBER = getattr(settings, "SUPERDJANGO_NOTIFICATIONS_FROM_NUMBER", None)
32# Functions
35def get_promoted_notification(user):
36 """Get the (first) promoted notification for the given user.
38 :param user: The user instance to be checked.
39 :type user: AUTH_USER_MODEL
41 :rtype: NotificationUser | None
43 """
44 criteria = {
45 'has_been_viewed': False,
46 'notification__is_promoted': True,
47 'user': user,
48 }
49 try:
50 return NotificationUser.objects.filter(**criteria).order_by("notification__added_dt").latest("pk")
51 except NotificationUser.DoesNotExist:
52 return None
55def get_sms_callback(raise_exception=True):
56 """Get the callback function used to send SMS/text messages.
58 The callback is defined using ``SUPERDJANGO_NOTIFICATIONS_SMS_CALLBACK`` which is the dotted path to a callable that
59 accepts a body, mobile number, and optionally a from number.
61 .. code-block:: python
63 def send_sms(body, number, from_number=None):
64 # ...
66 """
67 dotted = getattr(settings, "SUPERDJANGO_NOTIFICATIONS_SMS_CALLBACK", None)
68 if dotted is None:
69 if raise_exception:
70 raise ImproperlyConfigured("You must define SUPERDJANGO_NOTIFICATIONS_SMS_CALLBACK.")
72 return None
74 try:
75 return import_module(dotted)
76 except ImportError:
77 if raise_exception:
78 raise ImproperlyConfigured('SMS callback is defined but does not exist: %s' % dotted)
80 return None
83def get_unread_notification_count(user):
84 """Get the unread notification count for a user.
86 :param user: The user instance to be checked.
87 :type user: AUTH_USER_MODEL
89 :rtype: int
91 """
92 criteria = {
93 'has_been_viewed': False,
94 'is_available': True,
95 'user': user,
96 }
97 return NotificationUser.objects.filter(**criteria).count() or 0
100def get_unread_notifications(user):
101 """Get the unread notifications for a user.
103 :param user: The user instance to be checked.
104 :type user: AUTH_USER_MODEL
106 :rtype: QuerySet
108 """
109 criteria = {
110 'has_been_viewed': False,
111 'is_available': True,
112 'user': user,
113 }
114 return NotificationUser.objects.filter(**criteria)
117def has_unread_notifications(user):
118 """Indicates whether a given user has unread notifications.
120 :param user: The user instance to be checked.
121 :type user: AUTH_USER_MODEL
123 :rtype: bool
125 """
126 criteria = {
127 'has_been_viewed': False,
128 'is_available': True,
129 'user': user,
130 }
131 return NotificationUser.objects.filter(**criteria).exists()
134def notify(body, subject, action_url=None, email_enabled=False, icon_override=None, level=LEVEL.INFO, users=None):
135 """Issue a notification.
137 :param body: The body of the notification.
138 :type body: str
140 :param subject: The subject of the notification.
141 :type subject: str
143 :param action_url: The URL to which the user is directed.
144 :type action_url: str
146 :param email_enabled: Indicates the notification should be sent via email. Otherwise, it is only an in-app
147 notification.
148 :type email_enabled: bool
150 :param icon_override: The icon to use for the notification. Overrides the default.
151 :type icon_override: str
153 :param level: The notification level.
154 :type level: int
156 :param users: The users to which the notification should be sent. It omitted, all active users are notified.
157 :type users: list[AUTH_USER_MODEL]
159 :rtype: Notification
161 """
162 notification = Notification(
163 action_url=action_url,
164 body=body,
165 icon_override=icon_override,
166 level=level,
167 subject=subject
168 )
170 notification.save()
172 if not email_enabled:
173 notification.mark_sent()
175 if users is None:
176 users = UserModel.objects.filter(is_active=True)
178 for user in users:
179 link = NotificationUser(notification=notification, user=user)
180 link.save()
182 return notification
185class Message(object):
186 """Represents a single email and/or SMS message to a user."""
188 def __init__(self, notification, user, preferences=None):
189 """Initialize a message.
191 :param notification: The notification instance.
192 :type notification: superdjango.contrib.notifications.models.Notification
194 :param user: The user instance.
195 :type user: AUTH_USER_MODEL
197 :param preferences: The user preference instance.
198 :type preferences: superdjango.contrib.notifications.models.UserPreference
200 """
201 self.notification = notification
202 self.preferences = preferences
203 self.user = user
205 def email(self):
206 """Send a notification via email.
208 :rtype: bool
209 :returns: ``True`` if the email was sent.
211 """
212 # Mandatory messages ignore preferences.
213 if self.notification.is_mandatory:
214 pass
215 else:
216 # Otherwise, preferences must be defined and email enabled.
217 if self.preferences is None:
218 return False
220 if not self.preferences.email_enabled:
221 return False
223 context = {
224 'from_email': FROM_EMAIL,
225 'from_name': FROM_NAME,
226 'notification': self.notification,
227 'user': self.user,
228 }
230 body = parse_template("notifications/templates/email_notification_body.txt", context)
232 body_html = None
233 if self.notification.html_enabled:
234 body_html = parse_template("notifications/templates/email_notification_body.html", context)
236 send_mail(
237 self.notification.subject,
238 body,
239 "%s <%s>" % (FROM_NAME, FROM_EMAIL),
240 [self.user.email],
241 html_message=body_html
242 )
244 return True
246 def sms(self):
247 """Send a notification via SMS/text.
249 :rtype: bool
250 :returns: ``True`` if the SMS was sent.
252 """
253 send_sms = get_sms_callback(raise_exception=False)
255 if send_sms is None:
256 return False
258 if self.preferences is None:
259 return False
261 if not self.preferences.sms_enabled:
262 return False
264 if not self.preferences.mobile_number:
265 return False
267 send_sms(
268 self.notification.body_sms,
269 self.preferences.mobile_number,
270 from_number=FROM_NUMBER
271 )
273 return True