Hide keyboard shortcuts

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 

2 

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 

11 

12UserModel = get_user_model() 

13 

14# Exports 

15 

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) 

25 

26# Constants 

27 

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) 

31 

32# Functions 

33 

34 

35def get_promoted_notification(user): 

36 """Get the (first) promoted notification for the given user. 

37 

38 :param user: The user instance to be checked. 

39 :type user: AUTH_USER_MODEL 

40 

41 :rtype: NotificationUser | None 

42 

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 

53 

54 

55def get_sms_callback(raise_exception=True): 

56 """Get the callback function used to send SMS/text messages. 

57 

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. 

60 

61 .. code-block:: python 

62 

63 def send_sms(body, number, from_number=None): 

64 # ... 

65 

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.") 

71 

72 return None 

73 

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) 

79 

80 return None 

81 

82 

83def get_unread_notification_count(user): 

84 """Get the unread notification count for a user. 

85 

86 :param user: The user instance to be checked. 

87 :type user: AUTH_USER_MODEL 

88 

89 :rtype: int 

90 

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 

98 

99 

100def get_unread_notifications(user): 

101 """Get the unread notifications for a user. 

102 

103 :param user: The user instance to be checked. 

104 :type user: AUTH_USER_MODEL 

105 

106 :rtype: QuerySet 

107 

108 """ 

109 criteria = { 

110 'has_been_viewed': False, 

111 'is_available': True, 

112 'user': user, 

113 } 

114 return NotificationUser.objects.filter(**criteria) 

115 

116 

117def has_unread_notifications(user): 

118 """Indicates whether a given user has unread notifications. 

119 

120 :param user: The user instance to be checked. 

121 :type user: AUTH_USER_MODEL 

122 

123 :rtype: bool 

124 

125 """ 

126 criteria = { 

127 'has_been_viewed': False, 

128 'is_available': True, 

129 'user': user, 

130 } 

131 return NotificationUser.objects.filter(**criteria).exists() 

132 

133 

134def notify(body, subject, action_url=None, email_enabled=False, icon_override=None, level=LEVEL.INFO, users=None): 

135 """Issue a notification. 

136 

137 :param body: The body of the notification. 

138 :type body: str 

139 

140 :param subject: The subject of the notification. 

141 :type subject: str 

142 

143 :param action_url: The URL to which the user is directed. 

144 :type action_url: str 

145 

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 

149 

150 :param icon_override: The icon to use for the notification. Overrides the default. 

151 :type icon_override: str 

152 

153 :param level: The notification level. 

154 :type level: int 

155 

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] 

158 

159 :rtype: Notification 

160 

161 """ 

162 notification = Notification( 

163 action_url=action_url, 

164 body=body, 

165 icon_override=icon_override, 

166 level=level, 

167 subject=subject 

168 ) 

169 

170 notification.save() 

171 

172 if not email_enabled: 

173 notification.mark_sent() 

174 

175 if users is None: 

176 users = UserModel.objects.filter(is_active=True) 

177 

178 for user in users: 

179 link = NotificationUser(notification=notification, user=user) 

180 link.save() 

181 

182 return notification 

183 

184 

185class Message(object): 

186 """Represents a single email and/or SMS message to a user.""" 

187 

188 def __init__(self, notification, user, preferences=None): 

189 """Initialize a message. 

190 

191 :param notification: The notification instance. 

192 :type notification: superdjango.contrib.notifications.models.Notification 

193 

194 :param user: The user instance. 

195 :type user: AUTH_USER_MODEL 

196 

197 :param preferences: The user preference instance. 

198 :type preferences: superdjango.contrib.notifications.models.UserPreference 

199 

200 """ 

201 self.notification = notification 

202 self.preferences = preferences 

203 self.user = user 

204 

205 def email(self): 

206 """Send a notification via email. 

207 

208 :rtype: bool 

209 :returns: ``True`` if the email was sent. 

210 

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 

219 

220 if not self.preferences.email_enabled: 

221 return False 

222 

223 context = { 

224 'from_email': FROM_EMAIL, 

225 'from_name': FROM_NAME, 

226 'notification': self.notification, 

227 'user': self.user, 

228 } 

229 

230 body = parse_template("notifications/templates/email_notification_body.txt", context) 

231 

232 body_html = None 

233 if self.notification.html_enabled: 

234 body_html = parse_template("notifications/templates/email_notification_body.html", context) 

235 

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 ) 

243 

244 return True 

245 

246 def sms(self): 

247 """Send a notification via SMS/text. 

248 

249 :rtype: bool 

250 :returns: ``True`` if the SMS was sent. 

251 

252 """ 

253 send_sms = get_sms_callback(raise_exception=False) 

254 

255 if send_sms is None: 

256 return False 

257 

258 if self.preferences is None: 

259 return False 

260 

261 if not self.preferences.sms_enabled: 

262 return False 

263 

264 if not self.preferences.mobile_number: 

265 return False 

266 

267 send_sms( 

268 self.notification.body_sms, 

269 self.preferences.mobile_number, 

270 from_number=FROM_NUMBER 

271 ) 

272 

273 return True