diff --git a/lms/djangoapps/course_goals/management/commands/goal_reminder_email.py b/lms/djangoapps/course_goals/management/commands/goal_reminder_email.py index 0f8227e3201a..abd1dc53755b 100644 --- a/lms/djangoapps/course_goals/management/commands/goal_reminder_email.py +++ b/lms/djangoapps/course_goals/management/commands/goal_reminder_email.py @@ -23,7 +23,7 @@ from openedx.core.djangoapps.user_api.preferences.api import get_user_preference from openedx.core.lib.celery.task_utils import emulate_http_request from openedx.features.course_duration_limits.access import get_user_course_expiration_date -from openedx.features.course_experience import ENABLE_COURSE_GOALS +from openedx.features.course_experience import ENABLE_COURSE_GOALS, ENABLE_SES_FOR_GOALREMINDER from openedx.features.course_experience.url_helpers import get_learning_mfe_home_url log = logging.getLogger(__name__) @@ -86,13 +86,24 @@ def send_ace_message(goal): 'programs_url': getattr(settings, 'ACE_EMAIL_PROGRAMS_URL', None), }) + options = {'transactional': True} + + is_ses_enabled = ENABLE_SES_FOR_GOALREMINDER.is_enabled(goal.course_key) + + if is_ses_enabled: + options = { + 'transactional': True, + 'from_address': settings.LMS_COMM_DEFAULT_FROM_EMAIL, + 'override_default_channel': 'django_email', + } + msg = Message( name="goalreminder", app_label="course_goals", recipient=Recipient(user.id, user.email), language=language, context=message_context, - options={'transactional': True}, + options=options, ) with emulate_http_request(site, user): diff --git a/lms/djangoapps/course_goals/management/commands/tests/test_goal_reminder_email.py b/lms/djangoapps/course_goals/management/commands/tests/test_goal_reminder_email.py index ad60420e0d30..5b98b202d41f 100644 --- a/lms/djangoapps/course_goals/management/commands/tests/test_goal_reminder_email.py +++ b/lms/djangoapps/course_goals/management/commands/tests/test_goal_reminder_email.py @@ -5,6 +5,7 @@ from unittest import mock # lint-amnesty, pylint: disable=wrong-import-order import ddt +from django.conf import settings from django.core.management import call_command from django.test import TestCase from edx_toggles.toggles.testutils import override_waffle_flag @@ -20,7 +21,7 @@ from lms.djangoapps.certificates.data import CertificateStatuses from lms.djangoapps.certificates.tests.factories import GeneratedCertificateFactory from openedx.core.djangolib.testing.utils import skip_unless_lms -from openedx.features.course_experience import ENABLE_COURSE_GOALS +from openedx.features.course_experience import ENABLE_COURSE_GOALS, ENABLE_SES_FOR_GOALREMINDER # Some constants just for clarity of tests (assuming week starts on a Monday, as March 2021 used below does) MONDAY = 0 @@ -180,3 +181,33 @@ def test_no_days_per_week(self): def test_old_course(self, end): self.make_valid_goal(overview__end=end) self.call_command(expect_sent=False) + + @mock.patch('lms.djangoapps.course_goals.management.commands.goal_reminder_email.ace.send') + def test_params_with_ses(self, mock_ace): + """Test that the parameters of the msg passed to ace.send() are set correctly when SES is enabled""" + with override_waffle_flag(ENABLE_SES_FOR_GOALREMINDER, active=None): + goal = self.make_valid_goal() + flag = get_waffle_flag_model().get(ENABLE_SES_FOR_GOALREMINDER.name) + flag.users.add(goal.user) + + with freeze_time('2021-03-02 10:00:00'): + call_command('goal_reminder_email') + + assert mock_ace.call_count == 1 + msg = mock_ace.call_args[0][0] + assert msg.options['override_default_channel'] == 'django_email' + assert msg.options['from_address'] == settings.LMS_COMM_DEFAULT_FROM_EMAIL + + @mock.patch('lms.djangoapps.course_goals.management.commands.goal_reminder_email.ace.send') + def test_params_without_ses(self, mock_ace): + """Test that the parameters of the msg passed to ace.send() are set correctly when SES is not enabled""" + self.make_valid_goal() + + with freeze_time('2021-03-02 10:00:00'): + call_command('goal_reminder_email') + + assert mock_ace.call_count == 1 + msg = mock_ace.call_args[0][0] + assert msg.options['transactional'] is True + assert 'override_default_channel' not in msg.options + assert 'from_address' not in msg.options diff --git a/lms/envs/common.py b/lms/envs/common.py index 770c642d5fb8..b59d60c751f2 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -5534,3 +5534,6 @@ def _should_send_learning_badge_events(settings): # .. for now it wil impact country listing in auth flow and user profile. # .. eg ['US', 'CA'] DISABLED_COUNTRIES = [] + + +LMS_COMM_DEFAULT_FROM_EMAIL = "no-reply@example.com" diff --git a/openedx/features/course_experience/__init__.py b/openedx/features/course_experience/__init__.py index f8a662709ee7..a45d863e09c5 100644 --- a/openedx/features/course_experience/__init__.py +++ b/openedx/features/course_experience/__init__.py @@ -34,6 +34,16 @@ # .. toggle_warning: This temporary feature toggle does not have a target removal date. ENABLE_COURSE_GOALS = CourseWaffleFlag(f'{WAFFLE_FLAG_NAMESPACE}.enable_course_goals', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation +# .. toggle_name: course_experience.enable_ses_for_goalreminder +# .. toggle_implementation: CourseWaffleFlag +# .. toggle_default: False +# .. toggle_description: Used to determine whether or not to use AWS SES to send goal reminder emails for the course. +# .. toggle_use_cases: opt_in, temporary +# .. toggle_creation_date: 2024-10-06 +# .. toggle_target_removal_date: None +# .. toggle_warning: This temporary feature toggle does not have a target removal date. +ENABLE_SES_FOR_GOALREMINDER = CourseWaffleFlag(f'{WAFFLE_FLAG_NAMESPACE}.enable_ses_for_goalreminder', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation + # Waffle flag to enable anonymous access to a course SEO_WAFFLE_FLAG_NAMESPACE = 'seo' COURSE_ENABLE_UNENROLLED_ACCESS_FLAG = CourseWaffleFlag( # lint-amnesty, pylint: disable=toggle-missing-annotation diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt index b92ad29f8abf..2bac3df49c71 100644 --- a/requirements/edx/base.txt +++ b/requirements/edx/base.txt @@ -401,7 +401,7 @@ drf-yasg==1.21.7 # via # django-user-tasks # edx-api-doc-tools -edx-ace==1.11.2 +edx-ace==1.11.3 # via -r requirements/edx/kernel.in edx-api-doc-tools==2.0.0 # via diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt index a5713bc3dd33..d61db58ae4c2 100644 --- a/requirements/edx/development.txt +++ b/requirements/edx/development.txt @@ -657,7 +657,7 @@ drf-yasg==1.21.7 # -r requirements/edx/testing.txt # django-user-tasks # edx-api-doc-tools -edx-ace==1.11.2 +edx-ace==1.11.3 # via # -r requirements/edx/doc.txt # -r requirements/edx/testing.txt diff --git a/requirements/edx/doc.txt b/requirements/edx/doc.txt index 57f36577a5db..32ef31e8d3e5 100644 --- a/requirements/edx/doc.txt +++ b/requirements/edx/doc.txt @@ -481,7 +481,7 @@ drf-yasg==1.21.7 # -r requirements/edx/base.txt # django-user-tasks # edx-api-doc-tools -edx-ace==1.11.2 +edx-ace==1.11.3 # via -r requirements/edx/base.txt edx-api-doc-tools==2.0.0 # via diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt index 4f26caa9582a..77b3896967fb 100644 --- a/requirements/edx/testing.txt +++ b/requirements/edx/testing.txt @@ -505,7 +505,7 @@ drf-yasg==1.21.7 # -r requirements/edx/base.txt # django-user-tasks # edx-api-doc-tools -edx-ace==1.11.2 +edx-ace==1.11.3 # via -r requirements/edx/base.txt edx-api-doc-tools==2.0.0 # via