1
+ import logging
1
2
from datetime import timedelta
2
3
4
+ from django .conf import settings
3
5
from django .db import models
4
6
from django .utils import timezone
5
7
from django .utils .encoding import force_bytes
9
11
from apps .user .models import EmailNotificationType , User
10
12
from main .permalinks import Permalink
11
13
from main .tokens import TokenManager
14
+ from utils .common import logger_log_extra
12
15
from utils .emails import send_email
13
16
17
+ logger = logging .getLogger (__name__ )
18
+
14
19
15
20
def generate_unsubscribe_user_alert_subscription_url (subscription : UserAlertSubscription ) -> str :
16
21
uid = urlsafe_base64_encode (force_bytes (subscription .pk ))
@@ -22,7 +27,7 @@ def generate_unsubscribe_user_alert_subscription_url(subscription: UserAlertSubs
22
27
def generate_user_alert_subscription_email_context (
23
28
user : User ,
24
29
email_frequency : UserAlertSubscription .EmailFrequency ,
25
- ) -> tuple [dict , models .QuerySet [UserAlertSubscription ]]:
30
+ ) -> tuple [bool , dict , models .QuerySet [UserAlertSubscription ]]:
26
31
# NOTE: Number of subscription is static and less than UserAlertSubscription.LIMIT_PER_USER
27
32
subscription_qs = UserAlertSubscription .objects .filter (user = user , email_frequency = email_frequency )
28
33
@@ -31,62 +36,90 @@ def generate_user_alert_subscription_email_context(
31
36
elif email_frequency == UserAlertSubscription .EmailFrequency .WEEKLY :
32
37
from_datetime_threshold = timezone .now () - timedelta (days = 7 )
33
38
elif email_frequency == UserAlertSubscription .EmailFrequency .MONTHLY :
34
- # TODO: Calculate days instead of using 30 days
39
+ # TODO: Calculate month days instead of using 30 days
35
40
from_datetime_threshold = timezone .now () - timedelta (days = 30 )
36
41
37
- subscription_data = [
38
- {
39
- 'subscription' : subscription ,
40
- 'unsubscribe_url' : generate_unsubscribe_user_alert_subscription_url (subscription ),
41
- 'latest_alerts' : [
42
- subscription_alert .alert
43
- # NOTE: N+1 query, but N < 10 for now
44
- # TODO: Index/partition alert__sent column?
45
- for subscription_alert in (
46
- SubscriptionAlert .objects .select_related ('alert' )
47
- .filter (
48
- subscription = subscription ,
49
- alert__sent__gte = from_datetime_threshold ,
50
- )
51
- .order_by ('-alert__sent' )[:5 ]
52
- )
53
- ],
42
+ def _alert_data (alert ):
43
+ # TODO: Fix N+1 for alert.infos.first() and alert.admin1s
44
+ info = alert .infos .first ()
45
+ return {
46
+ "url" : Permalink .alert_detail (alert .pk ),
47
+ "name" : info and info .event or f"Alert #{ alert .pk } " ,
48
+ "urgency" : info and info .urgency or '-' ,
49
+ "severity" : info and info .severity or '-' ,
50
+ "certainty" : info and info .certainty or '-' ,
51
+ "admins" : "," .join (list (alert .admin1s .values_list ("name" , flat = True ))) or '-' ,
54
52
}
55
- for subscription in subscription_qs
56
- ]
53
+
54
+ subscription_data = []
55
+ for subscription in subscription_qs .iterator ():
56
+ latest_alerts = [
57
+ _alert_data (subscription_alert .alert )
58
+ # NOTE: N+1 query, but N < 10 for now
59
+ # TODO: Index/partition alert__sent column?
60
+ for subscription_alert in (
61
+ SubscriptionAlert .objects .select_related ('alert' )
62
+ .filter (
63
+ subscription = subscription ,
64
+ alert__sent__gte = from_datetime_threshold ,
65
+ )
66
+ .order_by ('-alert__sent' )[:5 ]
67
+ )
68
+ ]
69
+ if latest_alerts :
70
+ subscription_data .append (
71
+ {
72
+ 'subscription' : subscription ,
73
+ 'url' : Permalink .subscription_detail (subscription .pk ),
74
+ 'unsubscribe_url' : generate_unsubscribe_user_alert_subscription_url (subscription ),
75
+ 'latest_alerts' : latest_alerts ,
76
+ }
77
+ )
57
78
58
79
context = {
59
- 'subscriptions ' : subscription_data ,
80
+ 'subscriptions_data ' : subscription_data ,
60
81
}
61
82
62
- return context , subscription_qs
83
+ return len ( context [ "subscriptions_data" ]) > 0 , context , subscription_qs
63
84
64
85
65
86
def send_user_alert_subscription_email (user : User , email_frequency : UserAlertSubscription .EmailFrequency ):
66
- context , subscription_qs = generate_user_alert_subscription_email_context (user , email_frequency )
87
+ have_data , context , subscription_qs = generate_user_alert_subscription_email_context (user , email_frequency )
67
88
sent_at = timezone .now ()
68
89
69
- send_email (
70
- user = user ,
71
- email_type = EmailNotificationType .ALERT_SUBSCRIPTIONS ,
72
- subject = "Daily Alerts" , # TODO: Is this fine?
73
- email_html_template = 'emails/subscription/body.html' ,
74
- email_text_template = 'emails/subscription/body.txt' ,
75
- context = context ,
76
- )
90
+ if have_data :
91
+ send_email (
92
+ user = user ,
93
+ email_type = EmailNotificationType .ALERT_SUBSCRIPTIONS ,
94
+ subject = f"{ settings .EMAIL_SUBJECT_PREFIX } { email_frequency .label } " ,
95
+ email_html_template = 'emails/subscription/body.html' ,
96
+ email_text_template = 'emails/subscription/body.txt' ,
97
+ context = context ,
98
+ )
77
99
78
100
# Post action
79
101
subscription_qs .update (email_last_sent_at = sent_at )
80
102
81
103
82
104
def send_user_alert_subscriptions_email (email_frequency : UserAlertSubscription .EmailFrequency ):
83
- # TODO: Send in parallel if email service supports it
105
+ # TODO: Send in parallel if email service supports it?
84
106
users_qs = User .objects .filter (
85
107
id__in = UserAlertSubscription .objects .filter (email_frequency = email_frequency ).values ('user' ),
86
108
)
87
109
88
- # TODO: Handle failure
89
110
for user in users_qs .iterator ():
90
- # TODO: Trigger this as cronjob
91
- # TODO: Pass timezone.now for ref time
92
- send_user_alert_subscription_email (user , email_frequency )
111
+ # TODO: Trigger this as cronjob?
112
+ # TODO: Pass timezone.now for ref time?
113
+ try :
114
+ send_user_alert_subscription_email (user , email_frequency )
115
+ except Exception :
116
+ logger .error (
117
+ "Subscription: Failed to send email to user" ,
118
+ exc_info = True ,
119
+ extra = logger_log_extra (
120
+ {
121
+ 'user_id' : user .pk ,
122
+ 'email_frequency' : email_frequency ,
123
+ }
124
+ ),
125
+ )
0 commit comments