Skip to content

Commit 759e8f0

Browse files
authored
feat: add ability to override middlewares for recurring nudges (#790)
* feat: add ability to override middlewares for recurring nudges * feat: add ability to run command for all sites
1 parent d5a76ed commit 759e8f0

File tree

4 files changed

+58
-12
lines changed

4 files changed

+58
-12
lines changed

openedx/core/djangoapps/schedules/management/commands/__init__.py

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,28 @@ def add_arguments(self, parser):
2929
'--override-recipient-email',
3030
help='Send all emails to this address instead of the actual recipient'
3131
)
32-
parser.add_argument('site_domain_name')
32+
parser.add_argument(
33+
'site_domain_name',
34+
nargs='?',
35+
default=None,
36+
help=(
37+
'Domain name for the site to use. '
38+
'Do not provide a domain if you wish to run this for all sites'
39+
)
40+
)
3341
parser.add_argument(
3442
'--weeks',
3543
type=int,
3644
help='Number of weekly emails to be sent',
3745
)
46+
parser.add_argument(
47+
'--override-middlewares',
48+
action='append',
49+
help=(
50+
'Use this middleware when emulating http requests. '
51+
'To use multiple middlewares, provide this argument multiple times'
52+
)
53+
)
3854

3955
def handle(self, *args, **options):
4056
self.log_debug('Args = %r', options)
@@ -49,19 +65,26 @@ def handle(self, *args, **options):
4965
tzinfo=pytz.UTC
5066
)
5167
self.log_debug('Current date = %s', current_date.isoformat())
68+
override_recipient_email = options.get('override_recipient_email')
69+
override_middlewares = options.get('override_middlewares')
5270

53-
site = Site.objects.get(domain__iexact=options['site_domain_name'])
54-
self.log_debug('Running for site %s', site.domain)
71+
site_domain_name = options['site_domain_name']
72+
sites = Site.objects.filter(domain__iexact=site_domain_name) if site_domain_name else Site.objects.all()
5573

56-
override_recipient_email = options.get('override_recipient_email')
57-
self.send_emails(site, current_date, override_recipient_email)
74+
if sites:
75+
for site in sites:
76+
self.log_debug('Running for site %s', site.domain)
77+
self.send_emails(site, current_date, override_recipient_email, override_middlewares)
78+
else:
79+
self.log_info("No matching site found")
5880

59-
def enqueue(self, day_offset, site, current_date, override_recipient_email=None):
81+
def enqueue(self, day_offset, site, current_date, override_recipient_email=None, override_middlewares=None):
6082
self.async_send_task.enqueue(
6183
site,
6284
current_date,
6385
day_offset,
6486
override_recipient_email,
87+
override_middlewares,
6588
)
6689

6790
def send_emails(self, *args, **kwargs):

openedx/core/djangoapps/schedules/management/commands/tests/test_send_email_base_command.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import ddt
1111
import pytz
1212
from django.conf import settings
13+
from django.contrib.sites.models import Site
1314

1415
from openedx.core.djangoapps.schedules.management.commands import SendEmailBaseCommand
1516
from openedx.core.djangoapps.site_configuration.tests.factories import SiteConfigurationFactory, SiteFactory
@@ -33,9 +34,23 @@ def test_handle(self):
3334
send_emails.assert_called_once_with(
3435
self.site,
3536
datetime.datetime(2017, 9, 29, tzinfo=pytz.UTC),
37+
None,
3638
None
3739
)
3840

41+
def test_handle_all_sites(self):
42+
with patch.object(self.command, 'send_emails') as send_emails:
43+
self.command.handle(site_domain_name=None, date='2017-09-29')
44+
expected_sites = Site.objects.all()
45+
for expected_site in expected_sites:
46+
send_emails.assert_any_call(
47+
expected_site,
48+
datetime.datetime(2017, 9, 29, tzinfo=pytz.UTC),
49+
None,
50+
None
51+
)
52+
assert send_emails.call_count == len(expected_sites)
53+
3954
def test_weeks_option(self):
4055
with patch.object(self.command, 'enqueue') as enqueue:
4156
self.command.handle(site_domain_name=self.site.domain, date='2017-09-29', weeks=12)

openedx/core/djangoapps/schedules/tasks.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
set_custom_attribute
2121
)
2222
from eventtracking import tracker
23+
from importlib import import_module
2324
from opaque_keys.edx.keys import CourseKey
2425

2526
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
@@ -103,7 +104,7 @@ class BinnedScheduleMessageBaseTask(ScheduleMessageBaseTask):
103104
task_instance = None
104105

105106
@classmethod
106-
def enqueue(cls, site, current_date, day_offset, override_recipient_email=None): # lint-amnesty, pylint: disable=missing-function-docstring
107+
def enqueue(cls, site, current_date, day_offset, override_recipient_email=None, override_middlewares=None): # lint-amnesty, pylint: disable=missing-function-docstring
107108
set_code_owner_attribute_from_module(__name__)
108109
current_date = resolvers._get_datetime_beginning_of_day(current_date) # lint-amnesty, pylint: disable=protected-access
109110

@@ -120,6 +121,7 @@ def enqueue(cls, site, current_date, day_offset, override_recipient_email=None):
120121
day_offset,
121122
bin,
122123
override_recipient_email,
124+
override_middlewares,
123125
)
124126
cls.log_info('Launching task with args = %r', task_args)
125127
cls.task_instance.apply_async(
@@ -128,16 +130,17 @@ def enqueue(cls, site, current_date, day_offset, override_recipient_email=None):
128130
)
129131

130132
def run( # lint-amnesty, pylint: disable=arguments-differ
131-
self, site_id, target_day_str, day_offset, bin_num, override_recipient_email=None,
133+
self, site_id, target_day_str, day_offset, bin_num, override_recipient_email=None, override_middlewares=None,
132134
):
133135
set_code_owner_attribute_from_module(__name__)
134136
site = Site.objects.select_related('configuration').get(id=site_id)
135-
with emulate_http_request(site=site):
137+
middlewares = [self.class_from_classpath(cls) for cls in override_middlewares] if override_middlewares else None
138+
with emulate_http_request(site=site, middleware_classes=middlewares) as request:
136139
msg_type = self.make_message_type(day_offset)
137-
_annotate_for_monitoring(msg_type, site, bin_num, target_day_str, day_offset)
140+
_annotate_for_monitoring(msg_type, request.site, bin_num, target_day_str, day_offset)
138141
return self.resolver( # lint-amnesty, pylint: disable=not-callable
139142
self.async_send_task,
140-
site,
143+
request.site,
141144
deserialize(target_day_str),
142145
day_offset,
143146
bin_num,
@@ -147,6 +150,11 @@ def run( # lint-amnesty, pylint: disable=arguments-differ
147150
def make_message_type(self, day_offset):
148151
raise NotImplementedError
149152

153+
def class_from_classpath(self, class_path):
154+
module_name, klass = class_path.rsplit('.', 1)
155+
module = import_module(module_name)
156+
return getattr(module, klass)
157+
150158

151159
@shared_task(base=LoggedTask, ignore_result=True)
152160
@set_code_owner_attribute

openedx/core/lib/celery/task_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def emulate_http_request(site=None, user=None, middleware_classes=None):
4545
_run_method_if_implemented(middleware, 'process_request', request)
4646

4747
try:
48-
yield
48+
yield request
4949
except Exception as exc:
5050
for middleware in reversed(middleware_instances):
5151
_run_method_if_implemented(middleware, 'process_exception', request, exc)

0 commit comments

Comments
 (0)