/
send_idea_comment_notification.py
124 lines (92 loc) · 3.64 KB
/
send_idea_comment_notification.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# Create your tasks here
from __future__ import absolute_import, unicode_literals
import logging
import os
from typing import Set
import html2text
from constance import config
from django.contrib.contenttypes.models import ContentType
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
from celery import shared_task
from main.models import Comment, User
from plan.tasks.utils import _can_recieve_notification, _get_whitelisted_recipients
RECIEVE_NOTIFICATIONS_PERMISSION = 'main.recieve_idea_email_updates'
NOTIFICATION_LEVEL_PREFERENCE = 'idea_comment_notification_level'
def _get_involved_users(comment: Comment) -> set:
"""
:param comment:
:return: Ø
"""
idea = comment.commentable
# Django objects are hashable, so we can add
# any probable receiver to common set, and then
# filter by permissions
recipients = set()
# Add current stage assignee
recipients.add(idea.editor)
# Add all previous comments in the tread
previous_comments = Comment.objects.filter(
content_type=ContentType.objects.get_for_model(idea),
object_id=idea.id,
# Not required, as idea comments are always private
type=Comment.TYPE_PRIVATE,
).exclude(id=comment.id)
recipients.update([pc.user for pc in previous_comments])
return recipients
def _get_recipients(comment: Comment) -> Set[User]:
"""Build final list of notification recipients
for comments.
Includes:
— those, who should receive all notifications;
— those, who comment is related to.
Excludes users, who restricted any post comments
notifications.
:param comment: Comment, for which we build
recipient list for
:return: Final set of actual recipients
"""
recipients = set()
involved_users = _get_involved_users(comment)
recipients.update(involved_users)
whitelisted_recipients = _get_whitelisted_recipients(NOTIFICATION_LEVEL_PREFERENCE)
recipients.update(whitelisted_recipients)
# Remove users, who cannot receive notifications
# due to their permissions, comment ownership or settings
recipients = {
recipient
for recipient in recipients
if _can_recieve_notification(
recipient, comment, RECIEVE_NOTIFICATIONS_PERMISSION, NOTIFICATION_LEVEL_PREFERENCE
)
}
return recipients
def _send_email(comment, recipients):
subject = f"Комментарий к идее «{comment.commentable}» от {comment.user}"
html_content = render_to_string(
"email/new_comment.html",
{"comment": comment, "commentable_type": 'idea', "APP_URL": os.environ.get('APP_URL', None)},
)
text_content = html2text.html2text(html_content)
msg = EmailMultiAlternatives(subject, text_content, config.PLAN_EMAIL_FROM, recipients)
msg.attach_alternative(html_content, 'text/html')
msg.send()
@shared_task
def send_idea_comment_notification(comment_id: int) -> None:
"""Send email notification for idea comment
Notification is sent to:
- idea author (user)
- everyone, who previously commented comment idea
But only, if users have appropriate permission
permission and not comment author.
"""
logger = logging.getLogger()
logger.info('Sending notifications for comment #%s', comment_id)
# Task will fail if comment is not found
comment = Comment.objects.get(id=comment_id)
recipients = _get_recipients(comment)
if not recipients:
return
# Transform to plain email list
recipients_emails = {recipient.email for recipient in recipients}
_send_email(comment, recipients_emails)