Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added edx-ace template and message type for email notifications #34315

Merged
merged 8 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion lms/envs/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -5387,10 +5387,13 @@ def _make_locale_paths(settings): # pylint: disable=missing-function-docstring
SUBSCRIPTIONS_TRIAL_LENGTH = 7
SUBSCRIPTIONS_SERVICE_WORKER_USERNAME = 'subscriptions_worker'

############## NOTIFICATIONS EXPIRY ##############
############## NOTIFICATIONS ##############
NOTIFICATIONS_EXPIRY = 60
EXPIRED_NOTIFICATIONS_DELETE_BATCH_SIZE = 10000
NOTIFICATION_CREATION_BATCH_SIZE = 99
NOTIFICATIONS_DEFAULT_FROM_EMAIL = "[email protected]"
NOTIFICATION_TYPE_ICONS = {}
DEFAULT_NOTIFICATION_ICON_URL = ""

############################ AI_TRANSLATIONS ##################################
AI_TRANSLATIONS_API_URL = 'http://localhost:18760/api/v1'
Expand Down
Empty file.
18 changes: 18 additions & 0 deletions openedx/core/djangoapps/notifications/email/message_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""
Email notifications MessageType
"""
from django.conf import settings
from edx_ace.message import MessageType


class EmailNotificationMessageType(MessageType):
"""
Edx-ace MessageType for Email Notifications
"""

NAME = "notifications"

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.options['transactional'] = True
self.options['from_address'] = settings.NOTIFICATIONS_DEFAULT_FROM_EMAIL
45 changes: 45 additions & 0 deletions openedx/core/djangoapps/notifications/email/notification_icons.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""
Notification Icons
"""
from django.conf import settings


class NotificationTypeIcons:
"""
Notification Mapping with icons
"""
CHECK_CIRCLE_GREEN = "CHECK_CIRCLE_GREEN"
HELP_OUTLINE = "HELP_OUTLINE"
NEWSPAPER = "NEWSPAPER"
POST_OUTLINE = "POST_OUTLINE"
QUESTION_ANSWER_OUTLINE = "QUESTION_ANSWER_OUTLINE"
REPORT_RED = "REPORT_RED"
VERIFIED = "VERIFIED"

@classmethod
def get_icon_name_for_notification_type(cls, notification_type, default="POST_OUTLINE"):
"""
Returns icon name for notification type
"""
notification_type_dict = {
"new_comment_on_response": cls.QUESTION_ANSWER_OUTLINE,
"new_comment": cls.QUESTION_ANSWER_OUTLINE,
"new_response": cls.QUESTION_ANSWER_OUTLINE,
"new_discussion_post": cls.POST_OUTLINE,
"new_question_post": cls.HELP_OUTLINE,
"response_on_followed_post": cls.QUESTION_ANSWER_OUTLINE,
"comment_on_followed_post": cls.QUESTION_ANSWER_OUTLINE,
"content_reported": cls.REPORT_RED,
"response_endorsed_on_thread": cls.VERIFIED,
"response_endorsed": cls.CHECK_CIRCLE_GREEN,
"course_update": cls.NEWSPAPER,
}
return notification_type_dict.get(notification_type, default)

@classmethod
def get_icon_url_for_notification_type(cls, notification_type):
"""
Returns icon url for notification type
"""
icon_name = cls.get_icon_name_for_notification_type(notification_type)
return settings.NOTIFICATION_TYPE_ICONS.get(icon_name, settings.DEFAULT_NOTIFICATION_ICON_URL)
64 changes: 64 additions & 0 deletions openedx/core/djangoapps/notifications/email/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""
Email Notifications Utils
"""
from django.conf import settings
from lms.djangoapps.branding.api import get_logo_url_for_email
from .notification_icons import NotificationTypeIcons


def create_datetime_string(datetime_instance):
return datetime_instance.strftime('%A, %b %d')


def get_icon_url_for_notification_type(notification_type):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this method when we already have the get icon method in NotificationTypeIcons class

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can remove it. I thought of making all methods available in utils.py.

"""
Returns icon url for notification type
"""
return NotificationTypeIcons.get_icon_url_for_notification_type(notification_type)


def create_email_template_context():
"""
Creates email context for header and footer
"""
social_media_urls = settings.SOCIAL_MEDIA_FOOTER_ACE_URLS
social_media_icons = settings.SOCIAL_MEDIA_LOGO_URLS
social_media_info = {
social_platform: {
'url': social_media_urls[social_platform],
'icon': social_media_icons[social_platform]
}
for social_platform in social_media_urls.keys()
if social_media_icons.get(social_platform)
}
return {
"platform_name": settings.PLATFORM_NAME,
"mailing_address": settings.CONTACT_MAILING_ADDRESS,
"logo_url": get_logo_url_for_email(),
"social_media": social_media_info,
"notification_settings_url": f"{settings.ACCOUNT_MICROFRONTEND_URL}/notifications",
}


def create_email_digest_content(start_date, end_date=None, digest_frequency="Daily",
notifications_count=0, updates_count=0, email_content=None):
"""
Creates email context based on content
start_date: datetime instance
end_date: datetime instance
Comment on lines +45 to +48
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please update DocString with some more details, including the optional params

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be updated on integration.

"""
context = create_email_template_context()
start_date_str = create_datetime_string(start_date)
end_date_str = create_datetime_string(end_date if end_date else start_date)
Comment on lines +51 to +52
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are these going to be UTC? What time will the user receive these digest emails?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The task will run at 00:00 UTC but timezone is not needed for creating string. Format for string is Monday, Jun 12

context.update({
"start_date": start_date_str,
"end_date": end_date_str,
"digest_frequency": digest_frequency,
"updates": [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we rename this updates. We already have a notification app with this name, might cause confusion!
Something like email_digest_updates might be clearer!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be updated when it will be integrated to send email. There are some ambiguities where it is being used.

{"count": updates_count, "type": "Updates"},
{"count": notifications_count, "type": "Notifications"}
],
"email_content": email_content if email_content else [],
"get_icon_url_for_notification_type": get_icon_url_for_notification_type,
})
return context
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{% for update in email_content %}
<h3 style="font-size: 1.375rem; font-weight:700; line-height:28px; margin: 0.75rem 0 0;">
{{ update.title }}
</h3>
{% if update.help_text %}
<p style="margin: 0; height: 1.5rem; font-weight: 400; font-size: 14px; line-height: 24px">
<span style="float:left; color:#707070;">
{{ update.help_text }}
</span>
{% if update.help_text_url %}
<span style="float:right; margin-right: 0.25rem">
<a href="{{help_text_url}}" style="text-decoration: none; color: #00688D">
View all
</a>
</span>
{% endif %}
</p>
{% endif %}
<p style="height: 1rem; margin: 0;" />
<p style="margin: 0;">
<table style="background-color: #FBFAF9; border-radius: 8px; padding: 0.5rem 0" width="100%">
<tbody>
{% for content in update.content %}
<tr>
<td align="left" valign="top" style="padding: 1rem 0 1rem 1rem">
<img
src="https://edx-notifications-static.edx.org/icons/newspaper.png"
style="max-height: 28px; max-width: 28px; margin: 0.75rem 1rem 0.75rem 0"
/>
</td>
<td width="100%" align="left" valign="top" style=" padding: 1rem 1rem 1rem 0.5rem">
<p style="font-size: 0.875rem; font-weight:400; line-height:24px; color:#454545; margin: 0;">
{{ content.title }}
</p>
<p style="height: 0.5rem; margin: 0"></p>
<p style="color:#707070; margin: 0">
<span style="float: left">
<span>{{ content.course_name }}</span>
<span style="padding: 0 0.375rem">&middot</span>
<span>{{ content.time_ago }}</span>
</span>
<span style="float: right">
<a href="{{content.url}}" style="text-decoration: none; color: #00688D">
View
</a>
</span>
</p>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</p>
<p style="height: 0.75rem; margin: 0;" />
{% endfor %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<table cellpadding="0" cellspacing="0" style="color:black; font-weight:400; font-size:0.75rem;line-height:20px" width="100%">
<tbody>
<tr>
<td>
<table width="100%">
<tbody>
<tr>
<td width="25%" align="left" style="padding: 0">
<img src="{{ logo_url }}" style="width: auto;" height="24" alt="Logo"/></a>
</td>
<td width="75%" align="right" style="padding: 0">
<table>
<tbody>
<tr>
{% for social_name, social_data in social_media.items %}
<td style="padding: 0">
<a href="{{social_data.url}}">
<img src="{{social_data.icon}}" style="max-height: 24px; max-width: 24px; margin-left: 0.6rem" alt="{{social_name}}" />
</a>
</td>
{% endfor %}
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td>
<p style="margin: 1.5rem 0 0 0;">
You are receiving this email because you have subscribed to email digest
</p>
<p style="margin: 0.625rem 0">
<a href="{{notification_settings_url}}" rel="noopener noreferrer" target="_blank" style="color: black">
Notification Settings
</a>
</p>
<p>
&copy; {% now "Y" %} {{ platform_name }}. All Rights Reserved <br/>
{{ mailing_address }}
</p>
</td>
</tr>
</tbody>
</table>
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<table
border="0"
cellpadding="0"
cellspacing="0"
style="background: #00262b; color: white; width: 100%; padding: 1.5rem"
>
<tbody>
<tr align="center">
<td>
<img src="{{ logo_url }}" style="width: 64px" height="auto" alt="logo_url" />
</td>
</tr>
<tr style="height: 20px"></tr>
<tr align="center">
<td style="font-family: Inter; font-size: 32px; font-style: normal; font-weight: 700; line-height: 36px">
{{ digest_frequency }} email digest
</td>
</tr>
<tr style="height: 10px"></tr>
<tr align="center">
<td
style="
color: #bfc9ca;
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 28px;
"
>
{{ start_date }} {% if digest_frequency == "Weekly" %} - {{ end_date }} {% endif %}
</td>
</tr>
<tr style="height: 24px"></tr>
<tr align="center">
<td>
<table width="100%" border="0" cellpadding="0">
<tbody>
<tr>
{% for update in updates %}
<td width="50%">
<p style="
{% if not forloop.last %}padding-right: 0.5rem{% endif %}
{% if not forloop.first %}padding-left: 0.5rem{% endif %}"
>
<table
width="100%"
cellpadding="0"
cellspacing="0"
style="background: #1b3b40; color: white; border-radius: 8px;
padding: 1.25rem 1rem;"
>
<tbody>
<tr align="center">
<td style="font-weight: 700; font-size: 32px; line-height: 44px; padding: 0">
{{update.count}}
</td>
</tr>
<tr align="center">
<td style="font-weight: 600; font-size: 0.875rem; line-height: 20px; padding: 0">
{{update.type}}
</td>
</tr>
</tbody>
</table>
</p>
</td>
{% endfor %}
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%page expression_filter="h"/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<div style="margin:0; padding:0; min-width: 100%; background-color: background-color:#C9C9C9">
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" style="line-height:1.5; max-width:600px; font-family:Inter">
<tbody style="background-color:#f5f5f5">
<tr>
<td>
{% include 'notifications/digest_header.html' %}
</td>
</tr>
<tr>
<td style="padding:0.75rem 1.5rem; background-color:white">
{% include 'notifications/digest_content.html' %}
</td>
</tr>
<tr>
<td style="padding: 1.5rem; background-color: #F2F0EF">
{% include 'notifications/digest_footer.html' %}
</td>
</tr>
</tbody>
</table>

</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{ platform_name }}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%page expression_filter="h"/>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{ digest_frequency }} Notifications Digest for {% if digest_frequency == "Weekly" %}the Week of {% endif %}{{ start_date }}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens here for Daily Digest?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For daily digest: Daily Notifications Digest for Jun 12
For weekly digest: Weekly Notifications Digest for the week of Jun 12

Loading