edx-ace 1.11.3__py2.py3-none-any.whl → 1.12.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- edx_ace/__init__.py +1 -1
- edx_ace/channel/__init__.py +3 -0
- edx_ace/channel/braze_push_notification.py +64 -0
- edx_ace/presentation.py +1 -0
- edx_ace/renderers.py +15 -0
- edx_ace/tests/channel/test_braze_push_notification.py +105 -0
- edx_ace/tests/test_policy.py +10 -6
- edx_ace/tests/utils/test_braze_utils.py +59 -0
- edx_ace/utils/braze.py +27 -0
- edx_ace/utils/signals.py +4 -0
- {edx_ace-1.11.3.dist-info → edx_ace-1.12.0.dist-info}/METADATA +41 -46
- {edx_ace-1.11.3.dist-info → edx_ace-1.12.0.dist-info}/RECORD +16 -12
- {edx_ace-1.11.3.dist-info → edx_ace-1.12.0.dist-info}/WHEEL +1 -1
- {edx_ace-1.11.3.dist-info → edx_ace-1.12.0.dist-info}/entry_points.txt +1 -1
- {edx_ace-1.11.3.dist-info → edx_ace-1.12.0.dist-info}/LICENSE.txt +0 -0
- {edx_ace-1.11.3.dist-info → edx_ace-1.12.0.dist-info}/top_level.txt +0 -0
edx_ace/__init__.py
CHANGED
edx_ace/channel/__init__.py
CHANGED
@@ -28,6 +28,7 @@ class ChannelType(Enum):
|
|
28
28
|
|
29
29
|
EMAIL = 'email'
|
30
30
|
PUSH = 'push'
|
31
|
+
BRAZE_PUSH = 'braze_push'
|
31
32
|
|
32
33
|
def __str__(self):
|
33
34
|
return str(self.value)
|
@@ -184,6 +185,8 @@ def get_channel_for_message(channel_type, message):
|
|
184
185
|
channel_names = [settings.ACE_CHANNEL_DEFAULT_EMAIL]
|
185
186
|
elif channel_type == ChannelType.PUSH and getattr(settings, "ACE_CHANNEL_DEFAULT_PUSH", None):
|
186
187
|
channel_names = [settings.ACE_CHANNEL_DEFAULT_PUSH]
|
188
|
+
elif channel_type == ChannelType.BRAZE_PUSH and getattr(settings, "ACE_CHANNEL_BRAZE_PUSH", None):
|
189
|
+
channel_names = [settings.ACE_CHANNEL_BRAZE_PUSH]
|
187
190
|
|
188
191
|
try:
|
189
192
|
possible_channels = [
|
@@ -0,0 +1,64 @@
|
|
1
|
+
"""
|
2
|
+
Channel for sending push notifications using braze.
|
3
|
+
"""
|
4
|
+
import logging
|
5
|
+
|
6
|
+
from django.conf import settings
|
7
|
+
|
8
|
+
from edx_ace.channel import Channel, ChannelType
|
9
|
+
from edx_ace.message import Message
|
10
|
+
from edx_ace.renderers import RenderedPushNotification
|
11
|
+
from edx_ace.utils.braze import get_braze_client
|
12
|
+
|
13
|
+
LOG = logging.getLogger(__name__)
|
14
|
+
|
15
|
+
|
16
|
+
class BrazePushNotificationChannel(Channel):
|
17
|
+
"""
|
18
|
+
A channel for sending push notifications using braze.
|
19
|
+
"""
|
20
|
+
channel_type = ChannelType.BRAZE_PUSH
|
21
|
+
_CAMPAIGNS_SETTING = 'ACE_CHANNEL_BRAZE_PUSH_CAMPAIGNS'
|
22
|
+
|
23
|
+
@classmethod
|
24
|
+
def enabled(cls):
|
25
|
+
"""
|
26
|
+
Returns: True iff braze client is available.
|
27
|
+
"""
|
28
|
+
return bool(get_braze_client())
|
29
|
+
|
30
|
+
def deliver(self, message: Message, rendered_message: RenderedPushNotification) -> None:
|
31
|
+
"""
|
32
|
+
Transmit a rendered message to a recipient.
|
33
|
+
|
34
|
+
Args:
|
35
|
+
message: The message to transmit.
|
36
|
+
rendered_message: The rendered content of the message that has been personalized
|
37
|
+
for this particular recipient.
|
38
|
+
"""
|
39
|
+
braze_campaign = message.options['braze_campaign']
|
40
|
+
emails = message.options.get('emails') or [message.recipient.email_address]
|
41
|
+
campaign_id = self._campaign_id(braze_campaign)
|
42
|
+
if not campaign_id:
|
43
|
+
LOG.info('Could not find braze campaign for notification %s', braze_campaign)
|
44
|
+
return
|
45
|
+
|
46
|
+
try:
|
47
|
+
braze_client = get_braze_client()
|
48
|
+
braze_client.send_campaign_message(
|
49
|
+
campaign_id=campaign_id,
|
50
|
+
trigger_properties=message.context['post_data'],
|
51
|
+
emails=emails
|
52
|
+
)
|
53
|
+
LOG.info('Sent push notification for %s with Braze', braze_campaign)
|
54
|
+
except Exception as exc: # pylint: disable=broad-except
|
55
|
+
LOG.error(
|
56
|
+
'Unable to send push notification for %s with Braze. Reason: %s',
|
57
|
+
braze_campaign,
|
58
|
+
str(exc)
|
59
|
+
)
|
60
|
+
|
61
|
+
@classmethod
|
62
|
+
def _campaign_id(cls, braze_campaign):
|
63
|
+
"""Returns the campaign ID for a given ACE message name or None if no match is found"""
|
64
|
+
return getattr(settings, cls._CAMPAIGNS_SETTING, {}).get(braze_campaign)
|
edx_ace/presentation.py
CHANGED
edx_ace/renderers.py
CHANGED
@@ -99,3 +99,18 @@ class PushNotificationRenderer(AbstractRenderer):
|
|
99
99
|
A renderer for :attr:`.ChannelType.PUSH` channels.
|
100
100
|
"""
|
101
101
|
rendered_message_cls = RenderedPushNotification
|
102
|
+
|
103
|
+
|
104
|
+
@attr.s
|
105
|
+
class RenderedBrazePushNotification:
|
106
|
+
"""
|
107
|
+
Encapsulates all values needed to send a :class:`.Message`
|
108
|
+
over an :attr:`.ChannelType.BRAZE_PUSH`.
|
109
|
+
"""
|
110
|
+
|
111
|
+
|
112
|
+
class BrazePushNotificationRenderer(AbstractRenderer):
|
113
|
+
"""
|
114
|
+
A renderer for :attr:`.ChannelType.PUSH` channels.
|
115
|
+
"""
|
116
|
+
rendered_message_cls = RenderedBrazePushNotification
|
@@ -0,0 +1,105 @@
|
|
1
|
+
"""
|
2
|
+
Tests for TestBrazePushNotificationChannel.
|
3
|
+
"""
|
4
|
+
from unittest.mock import MagicMock, patch
|
5
|
+
|
6
|
+
import pytest
|
7
|
+
|
8
|
+
from django.contrib.auth import get_user_model
|
9
|
+
from django.test import TestCase, override_settings
|
10
|
+
|
11
|
+
from edx_ace.channel.braze_push_notification import BrazePushNotificationChannel
|
12
|
+
from edx_ace.message import Message
|
13
|
+
from edx_ace.recipient import Recipient
|
14
|
+
from edx_ace.renderers import RenderedBrazePushNotification
|
15
|
+
|
16
|
+
BRAZE_URL = "https://example.braze.com"
|
17
|
+
API_KEY = "test-api-key"
|
18
|
+
User = get_user_model()
|
19
|
+
|
20
|
+
|
21
|
+
@pytest.mark.django_db
|
22
|
+
@override_settings(
|
23
|
+
EDX_BRAZE_API_KEY=API_KEY,
|
24
|
+
EDX_BRAZE_API_SERVER=BRAZE_URL,
|
25
|
+
)
|
26
|
+
class TestBrazePushNotificationChannel(TestCase):
|
27
|
+
|
28
|
+
def setUp(self):
|
29
|
+
super().setUp()
|
30
|
+
self.user = User.objects.create(username='username', email='email@example.com')
|
31
|
+
self.lms_user_id = self.user.id
|
32
|
+
self.mocked_post_data = {
|
33
|
+
'notification_type': 'new_response',
|
34
|
+
'course_id': 'course-v1:edX+DemoX+Demo_Course',
|
35
|
+
'content_url': 'http://localhost',
|
36
|
+
'replier_name': 'verified',
|
37
|
+
'post_title': 'New test response',
|
38
|
+
'course_name': 'Demonstration Course',
|
39
|
+
'thread_id': '67bedeb9ceb0b101343294c5',
|
40
|
+
'topic_id': 'i4x-edx-eiorguegnru-course-foobarbaz',
|
41
|
+
'response_id': '67ffa1f1ceb0b10134db3d8e',
|
42
|
+
'comment_id': None,
|
43
|
+
'strong': 'strong', 'p': 'p'
|
44
|
+
}
|
45
|
+
|
46
|
+
self.mocked_payload = {
|
47
|
+
'campaign_id': '1234test',
|
48
|
+
'trigger_properties': self.mocked_post_data,
|
49
|
+
'emails': ['edx@example.com']
|
50
|
+
}
|
51
|
+
|
52
|
+
@patch('edx_ace.channel.braze_push_notification.get_braze_client', return_value=True)
|
53
|
+
def test_enabled(self, mock_braze_client):
|
54
|
+
"""
|
55
|
+
Test that the channel is enabled when the settings are configured.
|
56
|
+
"""
|
57
|
+
assert BrazePushNotificationChannel.enabled()
|
58
|
+
|
59
|
+
@patch('edx_ace.channel.braze_push_notification.get_braze_client', return_value=False)
|
60
|
+
def test_disabled(self, mock_braze_client):
|
61
|
+
"""
|
62
|
+
Test that the channel is disabled when the settings are not configured.
|
63
|
+
"""
|
64
|
+
assert not BrazePushNotificationChannel.enabled()
|
65
|
+
|
66
|
+
@override_settings(ACE_CHANNEL_BRAZE_PUSH_CAMPAIGNS={'new_response': "1234test"})
|
67
|
+
@patch('edx_ace.channel.braze_push_notification.get_braze_client')
|
68
|
+
def test_deliver_success(self, mock_braze_function):
|
69
|
+
mock_braze_client = MagicMock()
|
70
|
+
mock_braze_function.return_value = mock_braze_client
|
71
|
+
mock_braze_client.send_campaign_message = MagicMock(return_value=True)
|
72
|
+
|
73
|
+
rendered_message = RenderedBrazePushNotification()
|
74
|
+
message = Message(
|
75
|
+
app_label='testapp',
|
76
|
+
name="test_braze",
|
77
|
+
recipient=Recipient(lms_user_id="1", email_address="user@example.com"),
|
78
|
+
context={'post_data': self.mocked_post_data},
|
79
|
+
options={'emails': ['edx@example.com'], 'braze_campaign': 'new_response'}
|
80
|
+
)
|
81
|
+
channel = BrazePushNotificationChannel()
|
82
|
+
channel.deliver(message, rendered_message)
|
83
|
+
mock_braze_client.send_campaign_message.assert_called_once()
|
84
|
+
args, kwargs = mock_braze_client.send_campaign_message.call_args
|
85
|
+
|
86
|
+
# Verify the payload
|
87
|
+
self.assertEqual(kwargs, self.mocked_payload)
|
88
|
+
|
89
|
+
@patch('edx_ace.channel.braze_push_notification.get_braze_client')
|
90
|
+
def test_campaign_not_configured(self, mock_braze_function):
|
91
|
+
mock_braze_client = MagicMock()
|
92
|
+
mock_braze_function.return_value = mock_braze_client
|
93
|
+
mock_braze_client.send_campaign_message = MagicMock(return_value=True)
|
94
|
+
|
95
|
+
rendered_message = RenderedBrazePushNotification()
|
96
|
+
message = Message(
|
97
|
+
app_label='testapp',
|
98
|
+
name="test_braze",
|
99
|
+
recipient=Recipient(lms_user_id="1", email_address="user@example.com"),
|
100
|
+
context={'post_data': self.mocked_post_data},
|
101
|
+
options={'emails': ['edx@example.com'], 'braze_campaign': 'new_response'}
|
102
|
+
)
|
103
|
+
channel = BrazePushNotificationChannel()
|
104
|
+
channel.deliver(message, rendered_message)
|
105
|
+
mock_braze_client.send_campaign_message.assert_not_called()
|
edx_ace/tests/test_policy.py
CHANGED
@@ -24,12 +24,16 @@ class TestPolicy(TestCase):
|
|
24
24
|
PolicyCase(deny_values=[set(ChannelType)], expected_channels=set()),
|
25
25
|
|
26
26
|
# deny only email
|
27
|
-
PolicyCase(deny_values=[{ChannelType.EMAIL}],
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
27
|
+
PolicyCase(deny_values=[{ChannelType.EMAIL}],
|
28
|
+
expected_channels={ChannelType.PUSH, ChannelType.BRAZE_PUSH}), # single policy
|
29
|
+
PolicyCase(deny_values=[{ChannelType.EMAIL}, set()],
|
30
|
+
expected_channels={ChannelType.PUSH, ChannelType.BRAZE_PUSH}), # multiple policies
|
31
|
+
|
32
|
+
# deny email, push and braze_push
|
33
|
+
PolicyCase(deny_values=[{ChannelType.EMAIL, ChannelType.PUSH, ChannelType.BRAZE_PUSH}],
|
34
|
+
expected_channels=set()), # single policy
|
35
|
+
PolicyCase(deny_values=[{ChannelType.EMAIL}, {ChannelType.PUSH},
|
36
|
+
{ChannelType.BRAZE_PUSH}], expected_channels=set()), # multiple policies
|
33
37
|
|
34
38
|
# deny all and email
|
35
39
|
PolicyCase(deny_values=[{ChannelType.EMAIL}, set(ChannelType)], expected_channels=set()),
|
@@ -0,0 +1,59 @@
|
|
1
|
+
"""
|
2
|
+
Test cases for utils.braze
|
3
|
+
"""
|
4
|
+
from unittest.mock import patch
|
5
|
+
|
6
|
+
import pytest
|
7
|
+
|
8
|
+
from django.contrib.auth import get_user_model
|
9
|
+
from django.test import TestCase, override_settings
|
10
|
+
|
11
|
+
from edx_ace.utils.braze import get_braze_client
|
12
|
+
|
13
|
+
BRAZE_URL = "https://example.braze.com"
|
14
|
+
API_KEY = "test-api-key"
|
15
|
+
User = get_user_model()
|
16
|
+
|
17
|
+
|
18
|
+
@pytest.mark.django_db
|
19
|
+
class TestBrazeClient(TestCase):
|
20
|
+
""" Test cases for utils.braze """
|
21
|
+
|
22
|
+
@patch('edx_ace.utils.braze.BrazeClient')
|
23
|
+
def test_disabled(self, mock_braze_client):
|
24
|
+
"""
|
25
|
+
Test that the channel is settings aren't configured.
|
26
|
+
"""
|
27
|
+
result = get_braze_client()
|
28
|
+
self.assertEqual(result, None)
|
29
|
+
mock_braze_client.assert_not_called()
|
30
|
+
|
31
|
+
@override_settings(ACE_CHANNEL_BRAZE_API_KEY=API_KEY)
|
32
|
+
@patch('edx_ace.utils.braze.BrazeClient')
|
33
|
+
def test_braze_url_not_configured(self, mock_braze_client):
|
34
|
+
"""
|
35
|
+
Test that the channel is settings aren't configured.
|
36
|
+
"""
|
37
|
+
result = get_braze_client()
|
38
|
+
self.assertEqual(result, None)
|
39
|
+
mock_braze_client.assert_not_called()
|
40
|
+
|
41
|
+
@override_settings(ACE_CHANNEL_BRAZE_REST_ENDPOINT=API_KEY)
|
42
|
+
@patch('edx_ace.utils.braze.BrazeClient')
|
43
|
+
def test_braze_api_key_not_configured(self, mock_braze_client):
|
44
|
+
"""
|
45
|
+
Test that the channel is settings aren't configured.
|
46
|
+
"""
|
47
|
+
result = get_braze_client()
|
48
|
+
self.assertEqual(result, None)
|
49
|
+
mock_braze_client.assert_not_called()
|
50
|
+
|
51
|
+
@override_settings(ACE_CHANNEL_BRAZE_REST_ENDPOINT=API_KEY, ACE_CHANNEL_BRAZE_API_KEY=API_KEY)
|
52
|
+
@patch('edx_ace.utils.braze.BrazeClient', return_value=True)
|
53
|
+
def test_success(self, mock_braze_client):
|
54
|
+
"""
|
55
|
+
Test that the channel is settings aren't configured.
|
56
|
+
"""
|
57
|
+
result = get_braze_client()
|
58
|
+
self.assertEqual(result, True)
|
59
|
+
mock_braze_client.assert_called_once()
|
edx_ace/utils/braze.py
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
"""
|
2
|
+
Helper Methods related to braze client
|
3
|
+
"""
|
4
|
+
|
5
|
+
try:
|
6
|
+
from braze.client import BrazeClient
|
7
|
+
except ImportError:
|
8
|
+
BrazeClient = None
|
9
|
+
from django.conf import settings
|
10
|
+
|
11
|
+
|
12
|
+
def get_braze_client():
|
13
|
+
""" Returns a Braze client. """
|
14
|
+
if not BrazeClient:
|
15
|
+
return None
|
16
|
+
|
17
|
+
braze_api_key = getattr(settings, 'ACE_CHANNEL_BRAZE_API_KEY', None)
|
18
|
+
braze_api_url = getattr(settings, 'ACE_CHANNEL_BRAZE_REST_ENDPOINT', None)
|
19
|
+
|
20
|
+
if not braze_api_key or not braze_api_url:
|
21
|
+
return None
|
22
|
+
|
23
|
+
return BrazeClient(
|
24
|
+
api_key=braze_api_key,
|
25
|
+
api_url=f"https://{braze_api_url}",
|
26
|
+
app_id='',
|
27
|
+
)
|
edx_ace/utils/signals.py
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
"""
|
2
2
|
Utils for signals.
|
3
3
|
"""
|
4
|
+
from django.utils import translation
|
5
|
+
|
4
6
|
from edx_ace.signals import ACE_MESSAGE_SENT
|
5
7
|
|
6
8
|
|
@@ -42,5 +44,7 @@ def send_ace_message_sent_signal(channel, message):
|
|
42
44
|
'options': message.options,
|
43
45
|
'uuid': str(message.uuid),
|
44
46
|
'send_uuid': str(message.send_uuid),
|
47
|
+
'message_language': message.language,
|
48
|
+
'translation_language': translation.get_language()
|
45
49
|
}
|
46
50
|
ACE_MESSAGE_SENT.send(sender=channel, message=make_serializable_object(data))
|
@@ -1,13 +1,12 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: edx-ace
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.12.0
|
4
4
|
Summary: Framework for Messaging
|
5
5
|
Home-page: https://github.com/openedx/edx-ace
|
6
6
|
Author: edX
|
7
7
|
Author-email: oscm@edx.org
|
8
8
|
License: AGPL 3.0
|
9
9
|
Keywords: Django edx
|
10
|
-
Platform: UNKNOWN
|
11
10
|
Classifier: Development Status :: 3 - Alpha
|
12
11
|
Classifier: Framework :: Django
|
13
12
|
Classifier: Framework :: Django :: 4.2
|
@@ -18,9 +17,11 @@ Classifier: Programming Language :: Python :: 3
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.8
|
19
18
|
Classifier: Programming Language :: Python :: 3.12
|
20
19
|
Description-Content-Type: text/x-rst
|
20
|
+
License-File: LICENSE.txt
|
21
21
|
Requires-Dist: Django>=2.2
|
22
22
|
Requires-Dist: attrs>=17.2.0
|
23
23
|
Requires-Dist: django-push-notifications
|
24
|
+
Requires-Dist: edx-braze-client==1.0.1
|
24
25
|
Requires-Dist: edx-django-utils>=5.14.2
|
25
26
|
Requires-Dist: firebase-admin
|
26
27
|
Requires-Dist: python-dateutil
|
@@ -39,7 +40,7 @@ edX Automated Communication Engine (A.C.E.)
|
|
39
40
|
|doc-badge| |pyversions-badge|
|
40
41
|
|
41
42
|
Purpose
|
42
|
-
|
43
|
+
*******
|
43
44
|
|
44
45
|
The `automated communication engine <https://edx-ace.readthedocs.io/en/latest/>`_, A.C.E. for short, is a Django app
|
45
46
|
for messaging learners on the Open edX platform. This
|
@@ -49,12 +50,12 @@ are the currently supported delivery channels. In the future we may add support
|
|
49
50
|
notifications.
|
50
51
|
|
51
52
|
Getting Started
|
52
|
-
|
53
|
+
***************
|
53
54
|
|
54
|
-
For instructions on starting local development, see `Getting Started <https://github.com/openedx/edx-ace/blob/master/docs/getting_started.rst>`_.
|
55
|
+
For instructions on starting local development, see `Getting Started page <https://github.com/openedx/edx-ace/blob/master/docs/getting_started.rst>`_.
|
55
56
|
|
56
57
|
Getting Help
|
57
|
-
|
58
|
+
************
|
58
59
|
|
59
60
|
Documentation
|
60
61
|
-------------
|
@@ -79,14 +80,14 @@ can provide.
|
|
79
80
|
|
80
81
|
https://github.com/openedx/edx-ace/issues
|
81
82
|
|
82
|
-
For more information about these options, see the `
|
83
|
+
For more information about these options, see the `help`_ page.
|
83
84
|
|
84
85
|
.. _Slack invitation: https://openedx.org/slack
|
85
86
|
.. _community Slack workspace: https://openedx.slack.com/
|
86
|
-
..
|
87
|
+
.. _help: https://openedx.org/getting-help
|
87
88
|
|
88
89
|
License
|
89
|
-
|
90
|
+
*******
|
90
91
|
|
91
92
|
The code in this repository is licensed under the AGPL 3.0 unless
|
92
93
|
otherwise noted.
|
@@ -94,7 +95,7 @@ otherwise noted.
|
|
94
95
|
Please see ``LICENSE.txt`` for details.
|
95
96
|
|
96
97
|
Contributing
|
97
|
-
|
98
|
+
************
|
98
99
|
|
99
100
|
Contributions are very welcome.
|
100
101
|
|
@@ -111,14 +112,14 @@ Issue report template should be automatically applied if you are sending it from
|
|
111
112
|
can find it at `ISSUE_TEMPLATE.md <https://github.com/openedx/edx-ace/blob/master/.github/ISSUE_TEMPLATE.md>`_
|
112
113
|
|
113
114
|
The Open edX Code of Conduct
|
114
|
-
|
115
|
+
****************************
|
115
116
|
|
116
117
|
All community members are expected to follow the `Open edX Code of Conduct`_.
|
117
118
|
|
118
119
|
.. _Open edX Code of Conduct: https://openedx.org/code-of-conduct/
|
119
120
|
|
120
121
|
People
|
121
|
-
|
122
|
+
******
|
122
123
|
|
123
124
|
The assigned maintainers for this component and other project details may be
|
124
125
|
found in `Backstage`_. Backstage pulls this data from the ``catalog-info.yaml``
|
@@ -127,7 +128,7 @@ file in this repo.
|
|
127
128
|
.. _Backstage: https://open-edx-backstage.herokuapp.com/catalog/default/component/edx-ace
|
128
129
|
|
129
130
|
Reporting Security Issues
|
130
|
-
|
131
|
+
*************************
|
131
132
|
|
132
133
|
Please do not report security issues in public. Please email security@openedx.org.
|
133
134
|
|
@@ -168,7 +169,7 @@ Please do not report security issues in public. Please email security@openedx.or
|
|
168
169
|
|
169
170
|
|
170
171
|
Change Log
|
171
|
-
|
172
|
+
##########
|
172
173
|
|
173
174
|
..
|
174
175
|
All enhancements and patches to edx_ace will be documented
|
@@ -181,54 +182,55 @@ Change Log
|
|
181
182
|
.. There should always be an "Unreleased" section for changes pending release.
|
182
183
|
|
183
184
|
Unreleased
|
184
|
-
|
185
|
+
**********
|
185
186
|
|
186
187
|
[1.8.0] - 2024-03-06
|
187
|
-
|
188
|
+
--------------------
|
189
|
+
|
188
190
|
* Added support for ``Python 3.12``
|
189
191
|
* Dropped support for ``Django 3.2``
|
190
192
|
|
191
193
|
|
192
194
|
[1.7.0] - 2023-07-21
|
193
|
-
|
195
|
+
--------------------
|
194
196
|
|
195
197
|
* Switch from ``edx-sphinx-theme`` to ``sphinx-book-theme`` since the former is
|
196
198
|
deprecated
|
197
199
|
* Added support for django 4.2
|
198
200
|
|
199
201
|
[1.6.1] - 2023-02-07
|
200
|
-
|
202
|
+
--------------------
|
201
203
|
|
202
204
|
* Updated code coverage target in https://github.com/openedx/edx-ace/pull/189
|
203
205
|
* Updated Python Requirements in https://github.com/openedx/edx-ace/pull/199
|
204
206
|
* **Full Changelog**: https://github.com/openedx/edx-ace/compare/v1.6.0...v1.6.1
|
205
207
|
|
206
208
|
[1.5.0] - 2022-02-15
|
207
|
-
|
209
|
+
--------------------
|
208
210
|
|
209
211
|
* Added support for Django40
|
210
212
|
* Removed support for Django22, 30 and 31
|
211
213
|
|
212
214
|
[1.4.1] - 2021-12-06
|
213
|
-
|
215
|
+
--------------------
|
214
216
|
|
215
217
|
* Adds in the ability to override frequency caps for Braze emails. Can be accessed via
|
216
218
|
Message options using the key ``override_frequency_capping``. All emails containing the
|
217
219
|
``transactional`` Message option will also override frequency caps.
|
218
220
|
|
219
221
|
[1.4.0] - 2021-11-08
|
220
|
-
|
222
|
+
--------------------
|
221
223
|
|
222
224
|
* Deprecate the action_links property
|
223
225
|
* Add a get_action_links method and template tag to allow passing arguments to action links
|
224
226
|
|
225
227
|
[1.3.1] - 2021-08-17
|
226
|
-
|
228
|
+
--------------------
|
227
229
|
|
228
230
|
* Adjust name ``handles_delivery_for_message`` to ``overrides_delivery_for_message``
|
229
231
|
|
230
232
|
[1.3.0] - 2021-08-16
|
231
|
-
|
233
|
+
--------------------
|
232
234
|
|
233
235
|
* New channel method ``handles_delivery_for_message`` for allowing a default channel
|
234
236
|
to claim a message, even if it would normally be delivered to the configured
|
@@ -238,29 +240,29 @@ Unreleased
|
|
238
240
|
needed.
|
239
241
|
|
240
242
|
[1.2.0] - 2021-07-16
|
241
|
-
|
243
|
+
--------------------
|
242
244
|
|
243
245
|
* Added support for django 3.2
|
244
246
|
|
245
247
|
[1.1.1] - 2021-07-09
|
246
|
-
|
248
|
+
--------------------
|
247
249
|
|
248
250
|
* Removed upper constraint from Django
|
249
251
|
|
250
252
|
[1.1.0] - 2021-03-26
|
251
|
-
|
253
|
+
--------------------
|
252
254
|
|
253
255
|
* Braze: Add ACE_CHANNEL_BRAZE_FROM_EMAIL setting to override the normal from address
|
254
256
|
* Sailthru: Remove Braze rollout waffle flag
|
255
257
|
|
256
258
|
[1.0.1] - 2021-03-15
|
257
|
-
|
259
|
+
--------------------
|
258
260
|
|
259
261
|
* Braze: Add an unsubscribe action link
|
260
262
|
* Braze: Don't ask Braze to inline css, as ACE templates already have inline css
|
261
263
|
|
262
264
|
[1.0.0] - 2021-03-11
|
263
|
-
|
265
|
+
--------------------
|
264
266
|
|
265
267
|
* BREAKING: Recipient objects now take `lms_user_id` instead of `username`
|
266
268
|
* New `braze_email` backend, needing the following new configuration:
|
@@ -271,59 +273,52 @@ Unreleased
|
|
271
273
|
* ACE_CHANNEL_BRAZE_CAMPAIGNS (an optional dictionary of ACE message names to Braze campaign identifiers)
|
272
274
|
|
273
275
|
[0.1.18] - 2020-11-19
|
274
|
-
|
276
|
+
---------------------
|
275
277
|
|
276
278
|
* Updated he travis-badge in README.rst to point to travis-ci.com
|
277
279
|
|
278
280
|
[0.1.17] - 2020-10-19
|
279
|
-
|
281
|
+
---------------------
|
280
282
|
|
281
283
|
* Use IntEnum to avoid silent failure in value comparisons
|
282
284
|
|
283
285
|
[0.1.16] - 2020-10-17
|
284
|
-
|
286
|
+
---------------------
|
285
287
|
|
286
288
|
* Fixed Enum usage for Python 3.8 to avoid TypeError when comparing values
|
287
289
|
|
288
290
|
[0.1.15] - 2020-03-11
|
289
|
-
|
291
|
+
---------------------
|
290
292
|
|
291
293
|
* Added support for Python 3.8
|
292
294
|
* Removed support for Django 2.0 and 2.1
|
293
295
|
|
294
296
|
[0.1.14] - 2020-03-11
|
295
|
-
|
297
|
+
---------------------
|
296
298
|
|
297
299
|
* Fix trivial warning from deprecated use of attr library.
|
298
300
|
|
299
301
|
[0.1.13] - 2019-12-06
|
300
|
-
|
302
|
+
---------------------
|
301
303
|
|
302
304
|
* Django22 Support.
|
303
305
|
|
304
306
|
[0.1.12] - 2019-10-16
|
305
|
-
|
307
|
+
---------------------
|
306
308
|
|
307
309
|
* Reply_to field added in emails.
|
308
310
|
|
309
311
|
[0.1.10] - 2018-11-01
|
310
|
-
|
312
|
+
---------------------
|
311
313
|
|
312
314
|
* Django lazy text translations are handled properly.
|
313
315
|
|
314
|
-
|
315
316
|
[0.1.9] - 2018-07-13
|
316
|
-
|
317
|
+
--------------------
|
317
318
|
|
318
319
|
* Updated delivery logging
|
319
320
|
|
320
|
-
|
321
321
|
[0.1.0] - 2017-08-08
|
322
|
-
|
323
|
-
|
324
|
-
Added
|
325
|
-
_____
|
326
|
-
|
327
|
-
* First release on PyPI.
|
328
|
-
|
322
|
+
--------------------
|
329
323
|
|
324
|
+
* [Added] First release on PyPI.
|
@@ -1,4 +1,4 @@
|
|
1
|
-
edx_ace/__init__.py,sha256=
|
1
|
+
edx_ace/__init__.py,sha256=cJnxgaJn9f2ItO7dGSOm44vUsQw3O7-KC1oL-RghtSc,636
|
2
2
|
edx_ace/ace.py,sha256=0nP8zvIWiME4qmpMT1PaV4nRDG6YR9Dxm7A6FGoBa8I,2501
|
3
3
|
edx_ace/apps.py,sha256=xNedkdW6TNpm-W1uxnj3Vre2R1akkh2n_7DkSfKXmAk,216
|
4
4
|
edx_ace/delivery.py,sha256=zo5lNHngycEn0tu-e3JM26njRVmaAttAH3D5ohz_468,2812
|
@@ -6,14 +6,15 @@ edx_ace/errors.py,sha256=y0MqT55qXLkpn_r5LVhHcYuU2-zHs56CQTAbNCqb72k,1065
|
|
6
6
|
edx_ace/message.py,sha256=lhmPor9vnaLvC4NPyRgB-obpGjGv9Lni0obcOUTgyCg,8340
|
7
7
|
edx_ace/monitoring.py,sha256=6nEcAJMCr9keCTJw9JjGecTg_J1ubcGJfuGiFq2B8G4,257
|
8
8
|
edx_ace/policy.py,sha256=Zi0oupnmvgnW7jOkEpgsOd-gj6isR-3Mna2SxsdG4U8,2915
|
9
|
-
edx_ace/presentation.py,sha256=
|
9
|
+
edx_ace/presentation.py,sha256=o_BbUOzNxBqmcGHIq0PAhxouDo3cVaEnn7uJGCK9y4o,909
|
10
10
|
edx_ace/recipient.py,sha256=ogZjjKorAc6yiqvcnXcAMsHz2uAfhYU5qLPUoifofXQ,576
|
11
11
|
edx_ace/recipient_resolver.py,sha256=ChY0cgLSt_HioKSHyuCh7iHSJOsBWuivsOvAc6QyedE,959
|
12
|
-
edx_ace/renderers.py,sha256=
|
12
|
+
edx_ace/renderers.py,sha256=f1bPdmWpBvMa9OCPsq0NgnxsZi2Y4gwW5ZcYk9Qmpsw,3499
|
13
13
|
edx_ace/serialization.py,sha256=EptnQqbI9j5kVqdUDAlm2pcm3dUsFwsP9tAji3y5uqc,3775
|
14
14
|
edx_ace/signals.py,sha256=7kwpw7SgpPP6TRTtTQeGEOZ2cD_AcD4sSrpVxd_a19w,160
|
15
|
-
edx_ace/channel/__init__.py,sha256=
|
15
|
+
edx_ace/channel/__init__.py,sha256=PzfSJhBbsW5a8r1NnMm5q1PhwSadSoeIehjhU-C88pM,7221
|
16
16
|
edx_ace/channel/braze.py,sha256=k-R9jZpiBQTV0FQ3Z2gOipVgxNpBMPSLBL71muh9go4,10240
|
17
|
+
edx_ace/channel/braze_push_notification.py,sha256=XzFARmDotqthpARqvdvmcUy-ieEzusY-pH449nuciM4,2239
|
17
18
|
edx_ace/channel/django_email.py,sha256=9TNdiFJ2U3QjKEriUl3_9twAFkH-E2EmrWujStUOWN0,2174
|
18
19
|
edx_ace/channel/file.py,sha256=_Jx8o5Cw8IpEdaF7g-ljF7CEqswBHmuwyupm_tkI2wk,3028
|
19
20
|
edx_ace/channel/mixins.py,sha256=kmSBFcBU2OhMqlzaDZPrZpT3lh6ccte0cQeCCvo8FNc,1313
|
@@ -26,9 +27,10 @@ edx_ace/tests/test_ace.py,sha256=H5ss4ah-v5XDJ47udl6Ob6iSVdcX0qkjPI2h0kgWp1k,327
|
|
26
27
|
edx_ace/tests/test_date.py,sha256=RteoiCrc269BXnIerrtz8I0K1weldZ_Qg9Oc0Nd8ixs,639
|
27
28
|
edx_ace/tests/test_delivery.py,sha256=6nTSm3y28U0J0ChfduVPxkRyMA-IVEZ9H8TkmSHktAI,5787
|
28
29
|
edx_ace/tests/test_message.py,sha256=uAf3XltW605WieNGdi0u5p6bY_oSqLsmV5pHU3J_TiQ,4430
|
29
|
-
edx_ace/tests/test_policy.py,sha256=
|
30
|
+
edx_ace/tests/test_policy.py,sha256=bksNJntzD7YXaVx95LCR4n9uyxAfugXlen3HElUHsaU,1791
|
30
31
|
edx_ace/tests/test_presentation.py,sha256=elgVz_sR04h3KKs73G0qQBzt0CazOPWKrDF2rVC_R4U,541
|
31
32
|
edx_ace/tests/channel/test_braze.py,sha256=J7p9_xF5NTzZDC-9P8aFsV3cLc2RyDMBNVlyimwORuU,7296
|
33
|
+
edx_ace/tests/channel/test_braze_push_notification.py,sha256=1BhDIOjuCnZCjKWTy3jpf7978SyeLSv0xJqZ1Av-DXU,4241
|
32
34
|
edx_ace/tests/channel/test_channel_helpers.py,sha256=QwzHhdpEGkdW_WV0bfLkj8nBzHy4zFoo_lsIVlN31QU,3196
|
33
35
|
edx_ace/tests/channel/test_django_email.py,sha256=FCllUVAAtGEtDsosU-auf7zOApk-12oM_xWYs7CqVn8,4114
|
34
36
|
edx_ace/tests/channel/test_file_email.py,sha256=0kWb5xhdHJMgMNrnlEM2QV4NFLZERWgRU-3Lx0MQxLM,1795
|
@@ -36,15 +38,17 @@ edx_ace/tests/channel/test_push_notification.py,sha256=u6BBplfPwQLMRL02VFlWlNDEw
|
|
36
38
|
edx_ace/tests/channel/test_sailthru.py,sha256=Hfs-v2y9N9H2u0LlA_cJkhcuU0iC02wzwTt4pHsRS7o,3197
|
37
39
|
edx_ace/tests/test_templates/testapp/edx_ace/testmessage/email/body.html,sha256=gDeXQeQFnC2EFzdJ-OwWL7yDdkiR42sUYIxyeJJUD9s,646
|
38
40
|
edx_ace/tests/test_templates/testapp/edx_ace/testmessage/email/head.html,sha256=XvxX2Go_eKUtG0Oo1qPPMOtdS2IoGi7PSY98Di_Ev3s,19
|
41
|
+
edx_ace/tests/utils/test_braze_utils.py,sha256=aywPWmpnhUqSToWPiKMpduAyVojZ_s1ZeKzR3oSLu-o,1899
|
39
42
|
edx_ace/tests/utils/test_signals.py,sha256=k0F76oxcnECd95MeGw666YD3d2z-mIx4AS_MTYjwY5Q,1513
|
40
43
|
edx_ace/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
44
|
+
edx_ace/utils/braze.py,sha256=I0CcJELYkbfMTI3EWGkb8-unxO3Wkxu6C1GHA93H_0Y,622
|
41
45
|
edx_ace/utils/date.py,sha256=Rmz3RAUCdd30hu1qcKH7FmAypaw1aSoZg-yfZig1c8A,1483
|
42
46
|
edx_ace/utils/once.py,sha256=sY3szBh3gvvAjrKbKq4S2mCejompjh5YcYD7XOhzjGU,2024
|
43
47
|
edx_ace/utils/plugins.py,sha256=U-l-eU2uWUiiwYV-H-2DfmwjoksqskAsYwS7QnThy2Q,2090
|
44
|
-
edx_ace/utils/signals.py,sha256=
|
45
|
-
edx_ace-1.
|
46
|
-
edx_ace-1.
|
47
|
-
edx_ace-1.
|
48
|
-
edx_ace-1.
|
49
|
-
edx_ace-1.
|
50
|
-
edx_ace-1.
|
48
|
+
edx_ace/utils/signals.py,sha256=efmnxI78juMLo6PJkPyfJ6pYTMY3kHnGy-_IR8FsDm8,1637
|
49
|
+
edx_ace-1.12.0.dist-info/LICENSE.txt,sha256=VrSJ4gO4NCpskzfNHbaTB4VcN9Q213YdcHbpOZSwcOA,35138
|
50
|
+
edx_ace-1.12.0.dist-info/METADATA,sha256=QmLbJDtWiKXzqEtSTymkv6skzRvAZ1zqAWD91EMKLGc,10078
|
51
|
+
edx_ace-1.12.0.dist-info/WHEEL,sha256=Kh9pAotZVRFj97E15yTA4iADqXdQfIVTHcNaZTjxeGM,110
|
52
|
+
edx_ace-1.12.0.dist-info/entry_points.txt,sha256=9jbgYKntqyu4kP1pI_Js1BSWqKBG7OacftVn1BAKsLI,413
|
53
|
+
edx_ace-1.12.0.dist-info/top_level.txt,sha256=5eg_80KI88VkeiCVqZUqcYcc_PfPOg8o1GA4HxsiRU8,8
|
54
|
+
edx_ace-1.12.0.dist-info/RECORD,,
|
@@ -1,7 +1,7 @@
|
|
1
1
|
[openedx.ace.channel]
|
2
2
|
braze_email = edx_ace.channel.braze:BrazeEmailChannel
|
3
|
+
braze_push = edx_ace.channel.braze_push_notification:BrazePushNotificationChannel
|
3
4
|
django_email = edx_ace.channel.django_email:DjangoEmailChannel
|
4
5
|
file_email = edx_ace.channel.file:FileEmailChannel
|
5
6
|
push_notification = edx_ace.channel.push_notification:PushNotificationChannel
|
6
7
|
sailthru_email = edx_ace.channel.sailthru:SailthruEmailChannel
|
7
|
-
|
File without changes
|
File without changes
|