Skip to content

Commit 62652c7

Browse files
authored
enhancement: add support for caching programs for one site (openedx#32380) (openedx#32506)
* perf!: add support for caching programs on per site bases
1 parent 2817dd1 commit 62652c7

File tree

2 files changed

+72
-18
lines changed

2 files changed

+72
-18
lines changed

openedx/core/djangoapps/catalog/management/commands/cache_programs.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,17 @@ class Command(BaseCommand):
4545
"""
4646
help = "Rebuild the LMS' cache of program data."
4747

48+
def add_arguments(self, parser):
49+
parser.add_argument(
50+
'--domain',
51+
dest='domain',
52+
type=str,
53+
help='Help in caching the programs for one site'
54+
)
55+
4856
# lint-amnesty, pylint: disable=bad-option-value, unicode-format-string
4957
def handle(self, *args, **options): # lint-amnesty, pylint: disable=too-many-statements
58+
domain = options.get('domain', '')
5059
failure = False
5160
logger.info('populate-multitenant-programs switch is ON')
5261

@@ -68,7 +77,9 @@ def handle(self, *args, **options): # lint-amnesty, pylint: disable=too-many-st
6877
programs_by_type = {}
6978
programs_by_type_slug = {}
7079
organizations = {}
71-
for site in Site.objects.all():
80+
81+
sites = Site.objects.filter(domain=domain) if domain else Site.objects.all()
82+
for site in sites:
7283
site_config = getattr(site, 'configuration', None)
7384
if site_config is None or not site_config.get_value('COURSE_CATALOG_API_URL'):
7485
logger.info(f'Skipping site {site.domain}. No configuration.')

openedx/core/djangoapps/catalog/management/commands/tests/test_cache_programs.py

Lines changed: 60 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -48,29 +48,53 @@ def setUp(self):
4848
}
4949
)
5050

51+
self.site_domain2 = 'testsite2.com'
52+
self.site2 = self.set_up_site(
53+
self.site_domain2,
54+
{
55+
'COURSE_CATALOG_API_URL': self.catalog_integration.get_internal_api_url().rstrip('/')
56+
}
57+
)
58+
5159
self.list_url = self.catalog_integration.get_internal_api_url().rstrip('/') + '/programs/'
5260
self.detail_tpl = self.list_url.rstrip('/') + '/{uuid}/'
5361
self.pathway_url = self.catalog_integration.get_internal_api_url().rstrip('/') + '/pathways/'
5462

5563
self.programs = ProgramFactory.create_batch(3)
64+
self.programs2 = ProgramFactory.create_batch(3)
5665
self.pathways = PathwayFactory.create_batch(3)
66+
self.pathways2 = PathwayFactory.create_batch(3)
5767
self.child_program = ProgramFactory.create()
68+
self.child_program2 = ProgramFactory.create()
5869

5970
self.programs[0]['curricula'][0]['programs'].append(self.child_program)
6071
self.programs.append(self.child_program)
61-
6272
self.programs[0]['authoring_organizations'] = OrganizationFactory.create_batch(2)
6373

74+
self.programs2[0]['curricula'][0]['programs'].append(self.child_program2)
75+
self.programs2.append(self.child_program2)
76+
self.programs2[0]['authoring_organizations'] = OrganizationFactory.create_batch(2)
77+
6478
for pathway in self.pathways:
6579
self.programs += pathway['programs']
6680

67-
self.uuids = [program['uuid'] for program in self.programs]
81+
for pathway in self.pathways2:
82+
self.programs2 += pathway['programs']
83+
84+
self.uuids = {
85+
f"{self.site_domain}": [program["uuid"] for program in self.programs],
86+
f"{self.site_domain2}": [program["uuid"] for program in self.programs2],
87+
}
6888

6989
# add some of the previously created programs to some pathways
7090
self.pathways[0]['programs'].extend([self.programs[0], self.programs[1]])
7191
self.pathways[1]['programs'].append(self.programs[0])
7292

73-
def mock_list(self):
93+
# add some of the previously created programs to some pathways
94+
self.pathways2[0]['programs'].extend([self.programs2[0], self.programs2[1]])
95+
self.pathways2[1]['programs'].append(self.programs2[0])
96+
97+
def mock_list(self, site=""):
7498
""" Mock the data returned by the program listing API endpoint. """
7599
# pylint: disable=unused-argument
76100
def list_callback(request, uri, headers):
@@ -81,8 +105,8 @@ def list_callback(request, uri, headers):
81105
'uuids_only': ['1']
82106
}
83107
assert request.querystring == expected
84-
85-
return (200, headers, json.dumps(self.uuids))
108+
uuids = self.uuids[self.site_domain2] if site else self.uuids[self.site_domain]
109+
return (200, headers, json.dumps(uuids))
86110

87111
httpretty.register_uri(
88112
httpretty.GET,
@@ -130,17 +154,36 @@ def pathways_callback(request, uri, headers): # pylint: disable=unused-argument
130154

131155
return (200, headers, json.dumps(body))
132156

133-
# NOTE: httpretty does not properly match against query strings here (using match_querystring arg)
134-
# as such, it does not actually look at the query parameters (for page num), but returns items in a LIFO order.
135-
# this means that for multiple pages, you must call this function starting from the last page.
136-
# we do assert the page number query param above, however
137157
httpretty.register_uri(
138158
httpretty.GET,
139-
self.pathway_url,
159+
self.pathway_url + f'?exclude_utm=1&page={page_number}',
140160
body=pathways_callback,
141161
content_type='application/json',
162+
match_querystring=True,
142163
)
143164

165+
def test_handle_domain(self):
166+
"""
167+
Verify that the command argument is working correct or not.
168+
"""
169+
UserFactory(username=self.catalog_integration.service_username)
170+
171+
programs = {
172+
PROGRAM_CACHE_KEY_TPL.format(uuid=program['uuid']): program for program in self.programs2
173+
}
174+
175+
self.mock_list(self.site2)
176+
self.mock_pathways(self.pathways2)
177+
178+
for uuid in self.uuids[self.site_domain2]:
179+
program = programs[PROGRAM_CACHE_KEY_TPL.format(uuid=uuid)]
180+
self.mock_detail(uuid, program)
181+
182+
call_command('cache_programs', f'--domain={self.site_domain2}')
183+
184+
cached_uuids = cache.get(SITE_PROGRAM_UUIDS_CACHE_KEY_TPL.format(domain=self.site_domain2))
185+
assert set(cached_uuids) == set(self.uuids[self.site_domain2])
186+
144187
def test_handle_programs(self):
145188
"""
146189
Verify that the command requests and caches program UUIDs and details.
@@ -158,14 +201,14 @@ def test_handle_programs(self):
158201
self.mock_list()
159202
self.mock_pathways(self.pathways)
160203

161-
for uuid in self.uuids:
204+
for uuid in self.uuids[self.site_domain]:
162205
program = programs[PROGRAM_CACHE_KEY_TPL.format(uuid=uuid)]
163206
self.mock_detail(uuid, program)
164207

165208
call_command('cache_programs')
166209

167210
cached_uuids = cache.get(SITE_PROGRAM_UUIDS_CACHE_KEY_TPL.format(domain=self.site_domain))
168-
assert set(cached_uuids) == set(self.uuids)
211+
assert set(cached_uuids) == set(self.uuids[self.site_domain])
169212

170213
program_keys = list(programs.keys())
171214
cached_programs = cache.get_many(program_keys)
@@ -228,7 +271,7 @@ def test_handle_pathways(self):
228271
self.mock_list()
229272
self.mock_pathways(self.pathways)
230273

231-
for uuid in self.uuids:
274+
for uuid in self.uuids[self.site_domain]:
232275
program = programs[PROGRAM_CACHE_KEY_TPL.format(uuid=uuid)]
233276
self.mock_detail(uuid, program)
234277

@@ -267,7 +310,7 @@ def test_pathways_multiple_pages(self):
267310
}
268311

269312
self.mock_list()
270-
for uuid in self.uuids:
313+
for uuid in self.uuids[self.site_domain]:
271314
program = programs[PROGRAM_CACHE_KEY_TPL.format(uuid=uuid)]
272315
self.mock_detail(uuid, program)
273316

@@ -337,7 +380,7 @@ def test_handle_missing_pathways(self):
337380

338381
self.mock_list()
339382

340-
for uuid in self.uuids:
383+
for uuid in self.uuids[self.site_domain]:
341384
program = programs[PROGRAM_CACHE_KEY_TPL.format(uuid=uuid)]
342385
self.mock_detail(uuid, program)
343386

@@ -365,7 +408,7 @@ def test_handle_missing_programs(self):
365408

366409
self.mock_list()
367410

368-
for uuid in self.uuids[:2]:
411+
for uuid in self.uuids[self.site_domain][:2]:
369412
program = partial_programs[PROGRAM_CACHE_KEY_TPL.format(uuid=uuid)]
370413
self.mock_detail(uuid, program)
371414

@@ -375,7 +418,7 @@ def test_handle_missing_programs(self):
375418
assert context.value.code == 1
376419

377420
cached_uuids = cache.get(SITE_PROGRAM_UUIDS_CACHE_KEY_TPL.format(domain=self.site_domain))
378-
assert set(cached_uuids) == set(self.uuids)
421+
assert set(cached_uuids) == set(self.uuids[self.site_domain])
379422

380423
program_keys = list(all_programs.keys())
381424
cached_programs = cache.get_many(program_keys)

0 commit comments

Comments
 (0)