Merge pull request #19835 from edx/revert-19677-emma-green/REVEM-176/cache-course_runs-to-programs

Revert "WIP:Cache course runs to programs"
This commit is contained in:
Calen Pennington
2019-02-20 13:37:57 -05:00
committed by GitHub
15 changed files with 48 additions and 347 deletions

View File

@@ -958,10 +958,7 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase):
'width': 16, # 16x9
'height': 9
},
(
u'Recommended image resolution is {image_file_max_width}x{image_file_max_height}. '
u'The minimum resolution is {image_file_min_width}x{image_file_min_height}.'
).format(
u'Recommended image resolution is {image_file_max_width}x{image_file_max_height}. The minimum resolution is {image_file_min_width}x{image_file_min_height}.'.format(
image_file_max_width=settings.VIDEO_IMAGE_MAX_WIDTH,
image_file_max_height=settings.VIDEO_IMAGE_MAX_HEIGHT,
image_file_min_width=settings.VIDEO_IMAGE_MIN_WIDTH,
@@ -973,10 +970,7 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase):
'width': settings.VIDEO_IMAGE_MIN_WIDTH - 10,
'height': settings.VIDEO_IMAGE_MIN_HEIGHT
},
(
u'Recommended image resolution is {image_file_max_width}x{image_file_max_height}. '
u'The minimum resolution is {image_file_min_width}x{image_file_min_height}.'
).format(
u'Recommended image resolution is {image_file_max_width}x{image_file_max_height}. The minimum resolution is {image_file_min_width}x{image_file_min_height}.'.format(
image_file_max_width=settings.VIDEO_IMAGE_MAX_WIDTH,
image_file_max_height=settings.VIDEO_IMAGE_MAX_HEIGHT,
image_file_min_width=settings.VIDEO_IMAGE_MIN_WIDTH,

View File

@@ -695,7 +695,7 @@ def student_dashboard(request):
for program in inverted_programs.values():
try:
program_uuid = program[0]['uuid']
program_data = get_programs(uuid=program_uuid)
program_data = get_programs(request.site, uuid=program_uuid)
program_data = ProgramDataExtender(program_data, request.user).extend()
skus = program_data.get('skus')
checkout_page_url = ecommerce_service.get_checkout_page_url(*skus)

View File

@@ -15,8 +15,7 @@ class StubCatalogServiceHandler(StubHttpRequestHandler):
r'/api/v1/programs/$': self.program_list,
r'/api/v1/programs/([0-9a-f-]+)/$': self.program_detail,
r'/api/v1/program_types/$': self.program_types,
r'/api/v1/pathways/$': self.pathways,
r'/api/v1/course_runs/$': self.course_runs,
r'/api/v1/pathways/$': self.pathways
}
if self.match_pattern(pattern_handlers):
@@ -53,10 +52,6 @@ class StubCatalogServiceHandler(StubHttpRequestHandler):
pathways = self.server.config.get('catalog.pathways', [])
self.send_json_response(pathways)
def course_runs(self):
course_runs = self.server.config.get('catalog.course_runs', {'results': [], 'next': None})
self.send_json_response(course_runs)
class StubCatalogService(StubHttpService):
HANDLER_CLASS = StubCatalogServiceHandler

View File

@@ -13,8 +13,7 @@ class CatalogFixture(object):
"""
Interface to set up mock responses from the Catalog stub server.
"""
@classmethod
def install_programs(cls, programs):
def install_programs(self, programs):
"""
Stub the discovery service's program list and detail API endpoints.
@@ -41,8 +40,7 @@ class CatalogFixture(object):
data={key: json.dumps(uuids)},
)
@classmethod
def install_pathways(cls, pathways):
def install_pathways(self, pathways):
"""
Stub the discovery service's credit pathways API endpoint
@@ -54,8 +52,7 @@ class CatalogFixture(object):
data={'catalog.pathways': json.dumps({'results': pathways, 'next': None})}
)
@classmethod
def install_program_types(cls, program_types):
def install_program_types(self, program_types):
"""
Stub the discovery service's program type list API endpoints.
@@ -67,25 +64,10 @@ class CatalogFixture(object):
data={'catalog.programs_types': json.dumps(program_types)},
)
@classmethod
def install_course_runs(cls, course_runs):
"""
Stub the discovery service's course run list API endpoints.
Arguments:
course_runs (list): A list of course runs. List endpoint will be stubbed using data from this list.
"""
requests.put(
'{}/set_config'.format(CATALOG_STUB_URL),
data={'catalog.course_runs': json.dumps({'results': course_runs, 'next': None})}
)
class CatalogIntegrationMixin(object):
"""Mixin providing a method used to configure the catalog integration."""
@classmethod
def set_catalog_integration(cls, is_enabled=False, service_username=None):
def set_catalog_integration(self, is_enabled=False, service_username=None):
"""Use this to change the catalog integration config model during tests."""
ConfigModelFixture('/config/catalog', {
'enabled': is_enabled,

View File

@@ -11,8 +11,7 @@ from openedx.core.djangoapps.catalog.tests.factories import (
CourseRunFactory,
PathwayFactory,
ProgramFactory,
ProgramTypeFactory,
ProgramDescriptionFactory,
ProgramTypeFactory
)
@@ -25,12 +24,6 @@ class ProgramPageBase(ProgramsConfigMixin, CatalogIntegrationMixin, UniqueCourse
self.programs = ProgramFactory.create_batch(3)
self.pathways = PathwayFactory.create_batch(3)
self.course_runs = [
CourseRunFactory.create(programs=[ProgramDescriptionFactory.from_program(program)], **course_run)
for program in self.programs
for course in program['courses']
for course_run in course['course_runs']
]
for pathway in self.pathways:
self.programs += pathway['programs']
@@ -58,28 +51,18 @@ class ProgramPageBase(ProgramsConfigMixin, CatalogIntegrationMixin, UniqueCourse
program_type = ProgramTypeFactory()
return ProgramFactory(courses=[course], type=program_type['name'])
def stub_catalog_api(self, programs=None, pathways=None, course_runs=None):
def stub_catalog_api(self, programs, pathways):
"""
Stub the discovery service's program list and detail API endpoints, as well as
the credit pathway list endpoint.
"""
self.set_catalog_integration(is_enabled=True, service_username=self.username)
if programs is None:
programs = self.programs
CatalogFixture.install_programs(programs)
CatalogFixture().install_programs(programs)
program_types = [program['type'] for program in programs]
CatalogFixture.install_program_types(program_types)
CatalogFixture().install_program_types(program_types)
if pathways is None:
pathways = self.pathways
CatalogFixture.install_pathways(pathways)
if course_runs is None:
course_runs = self.course_runs
CatalogFixture.install_course_runs(course_runs)
CatalogFixture().install_pathways(pathways)
def cache_programs(self):
"""
@@ -101,7 +84,7 @@ class ProgramListingPageTest(ProgramPageBase):
def test_no_enrollments(self):
"""Verify that no cards appear when the user has no enrollments."""
self.auth(enroll=False)
self.stub_catalog_api()
self.stub_catalog_api(self.programs, self.pathways)
self.cache_programs()
self.listing_page.visit()
@@ -115,7 +98,7 @@ class ProgramListingPageTest(ProgramPageBase):
but none are included in an active program.
"""
self.auth()
self.stub_catalog_api()
self.stub_catalog_api(self.programs, self.pathways)
self.cache_programs()
self.listing_page.visit()
@@ -143,15 +126,7 @@ class ProgramListingPageA11yTest(ProgramPageBase):
]
})
self.auth(enroll=False)
self.stub_catalog_api(
programs=[self.program],
pathways=[],
course_runs=[
CourseRunFactory.create(programs=[ProgramDescriptionFactory.from_program(self.program)], **course_run)
for course in self.program['courses']
for course_run in course['course_runs']
]
)
self.stub_catalog_api(programs=[self.program], pathways=[])
self.cache_programs()
self.listing_page.visit()
@@ -168,15 +143,7 @@ class ProgramListingPageA11yTest(ProgramPageBase):
]
})
self.auth()
self.stub_catalog_api(
programs=[self.program],
pathways=[],
course_runs=[
CourseRunFactory.create(programs=[ProgramDescriptionFactory.from_program(self.program)], **course_run)
for course in self.program['courses']
for course_run in course['course_runs']
]
)
self.stub_catalog_api(programs=[self.program], pathways=[])
self.cache_programs()
self.listing_page.visit()
@@ -206,15 +173,7 @@ class ProgramDetailsPageA11yTest(ProgramPageBase):
]
})
self.auth()
self.stub_catalog_api(
programs=[self.program],
pathways=[],
course_runs=[
CourseRunFactory.create(programs=[ProgramDescriptionFactory.from_program(self.program)], **course_run)
for course in self.program['courses']
for course_run in course['course_runs']
]
)
self.stub_catalog_api(programs=[self.program], pathways=[])
self.cache_programs()
self.details_page.visit()

View File

@@ -814,7 +814,7 @@ class TestGetHtmlMethod(BaseTestXmodule):
mocked_get_video.side_effect = side_effect
source_xml = """
SOURCE_XML = """
<video show_captions="true"
display_name="A Name"
sub="a_sub_file.srt.sjson" source="{source}"
@@ -875,7 +875,7 @@ class TestGetHtmlMethod(BaseTestXmodule):
initial_context['metadata']['duration'] = None
for data in cases:
DATA = source_xml.format(
DATA = SOURCE_XML.format(
download_video=data['download_video'],
source=data['source'],
sources=data['sources'],

View File

@@ -900,7 +900,7 @@ def program_marketing(request, program_uuid):
"""
Display the program marketing page.
"""
program_data = get_programs(uuid=program_uuid)
program_data = get_programs(request.site, uuid=program_uuid)
if not program_data:
raise Http404

View File

@@ -9,6 +9,3 @@ PATHWAY_CACHE_KEY_TPL = 'pathway-{id}'
# Cache key used to locate an item containing a list of all credit pathway ids for a site.
SITE_PATHWAY_IDS_CACHE_KEY_TPL = 'pathway-ids-{domain}'
# Template used to create cache keys for individual courses to program uuids.
COURSE_PROGRAMS_CACHE_KEY_TPL = 'course-programs-{course_run_id}'

View File

@@ -4,14 +4,13 @@ import sys
from django.contrib.auth import get_user_model
from django.contrib.sites.models import Site
from django.core.cache import cache
from django.core.management import BaseCommand, CommandError
from django.core.management import BaseCommand
from openedx.core.djangoapps.catalog.cache import (
COURSE_PROGRAMS_CACHE_KEY_TPL,
PATHWAY_CACHE_KEY_TPL,
PROGRAM_CACHE_KEY_TPL,
SITE_PATHWAY_IDS_CACHE_KEY_TPL,
SITE_PROGRAM_UUIDS_CACHE_KEY_TPL,
SITE_PROGRAM_UUIDS_CACHE_KEY_TPL
)
from openedx.core.djangoapps.catalog.models import CatalogIntegration
from openedx.core.djangoapps.catalog.utils import create_catalog_api_client
@@ -47,7 +46,6 @@ class Command(BaseCommand):
programs = {}
pathways = {}
courses = {}
for site in Site.objects.all():
site_config = getattr(site, 'configuration', None)
if site_config is None or not site_config.get_value('COURSE_CATALOG_API_URL'):
@@ -62,19 +60,12 @@ class Command(BaseCommand):
new_pathways, pathways_failed = self.get_pathways(client, site)
new_pathways, new_programs, pathway_processing_failed = self.process_pathways(site, new_pathways,
new_programs)
new_courses, courses_failed = self.get_courses(client, site)
failure = any([
program_uuids_failed,
program_details_failed,
pathways_failed,
pathway_processing_failed,
courses_failed,
])
if program_uuids_failed or program_details_failed or pathways_failed or pathway_processing_failed:
failure = True
programs.update(new_programs)
pathways.update(new_pathways)
courses.update(new_courses)
logger.info(u'Caching UUIDs for {total} programs for site {site_name}.'.format(
total=len(uuids),
@@ -99,15 +90,10 @@ class Command(BaseCommand):
successful_pathways=successful_pathways))
cache.set_many(pathways, None)
successful_courses = len(courses)
logger.info('Caching programs uuids for {successful_courses} courses.'.format(
successful_courses=successful_courses))
cache.set_many(courses, None)
if failure:
# This will fail a Jenkins job running this command, letting site
# operators know that there was a problem.
raise CommandError("Caching program information failed")
sys.exit(1)
def get_site_program_uuids(self, client, site):
failure = False
@@ -202,31 +188,3 @@ class Command(BaseCommand):
logger.exception(u'Failed to process pathways for {domain}'.format(domain=site.domain))
failure = True
return processed_pathways, programs, failure
def get_courses(self, client, site):
"""
Get all courses for the current client
"""
failure = False
courses = {}
try:
logger.info('Requesting courses for {domain}.'.format(domain=site.domain))
next_page = 1
while next_page:
new_courses = client.course_runs.get(exclude_utm=1, page=next_page)
courses.update({
COURSE_PROGRAMS_CACHE_KEY_TPL.format(course_run_id=cr['key']):
[pu['uuid'] for pu in cr['programs']]
for cr in new_courses['results']
})
next_page = next_page + 1 if new_courses['next'] else None
except: # pylint: disable=bare-except
logger.exception('Failed to retrieve courses for site: {domain}.'.format(domain=site.domain))
failure = True
logger.info('Received {total} courses for site {domain}'.format(
total=len(courses),
domain=site.domain
))
return courses, failure

View File

@@ -2,10 +2,9 @@ import json
import httpretty
from django.core.cache import cache
from django.core.management import CommandError, call_command
from django.core.management import call_command
from openedx.core.djangoapps.catalog.cache import (
COURSE_PROGRAMS_CACHE_KEY_TPL,
PATHWAY_CACHE_KEY_TPL,
PROGRAM_CACHE_KEY_TPL,
SITE_PATHWAY_IDS_CACHE_KEY_TPL,
@@ -17,12 +16,10 @@ from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
from student.tests.factories import UserFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
@skip_unless_lms
@httpretty.activate
class TestCachePrograms(CatalogIntegrationMixin, CacheIsolationTestCase, ModuleStoreTestCase, SiteMixin):
class TestCachePrograms(CatalogIntegrationMixin, CacheIsolationTestCase, SiteMixin):
ENABLED_CACHES = ['default']
def setUp(self):
@@ -42,25 +39,10 @@ class TestCachePrograms(CatalogIntegrationMixin, CacheIsolationTestCase, ModuleS
self.list_url = self.catalog_integration.get_internal_api_url().rstrip('/') + '/programs/'
self.detail_tpl = self.list_url.rstrip('/') + '/{uuid}/'
self.pathway_url = self.catalog_integration.get_internal_api_url().rstrip('/') + '/pathways/'
self.course_run_url = self.catalog_integration.get_internal_api_url().rstrip('/') + '/course_runs/'
self.programs = ProgramFactory.create_batch(3)
self.pathways = PathwayFactory.create_batch(3)
# Build the course run list to look like the course run api. We want to start from the program
# list so that the courses are associated with the programs.
course_run_dict = {}
for program in self.programs:
program_uuid = program['uuid']
for course in program['courses']:
for course_run in course['course_runs']:
course_run_key = course_run['key']
if course_run_key in course_run_dict:
course_run_dict[course_run_key]['programs'] += {'uuid': program_uuid}
else:
course_run_dict[course_run_key] = {'key': course_run_key, 'programs': [{'uuid': program_uuid}]}
self.course_runs = course_run_dict.values()
for pathway in self.pathways:
self.programs += pathway['programs']
@@ -139,36 +121,6 @@ class TestCachePrograms(CatalogIntegrationMixin, CacheIsolationTestCase, ModuleS
content_type='application/json',
)
def mock_courses(self, course_runs, page_number=1, final=True):
"""
Mock the data for discovery's course_run endpoint
"""
def course_run_callback(request, uri, headers): # pylint: disable=unused-argument
"""
Mocks response
"""
expected = {
'exclude_utm': ['1'],
'page': [str(page_number)],
}
self.assertEqual(request.querystring, expected)
body = {
'count': len(course_runs),
'next': None if final else 'more', # we don't actually parse this value
'prev': None,
'results': course_runs
}
return (200, headers, json.dumps(body))
httpretty.register_uri(
httpretty.GET,
self.course_run_url,
body=course_run_callback,
content_type='application/json',
)
def test_handle_programs(self):
"""
Verify that the command requests and caches program UUIDs and details.
@@ -185,7 +137,6 @@ class TestCachePrograms(CatalogIntegrationMixin, CacheIsolationTestCase, ModuleS
self.mock_list()
self.mock_pathways(self.pathways)
self.mock_courses(self.course_runs)
for uuid in self.uuids:
program = programs[PROGRAM_CACHE_KEY_TPL.format(uuid=uuid)]
@@ -233,7 +184,6 @@ class TestCachePrograms(CatalogIntegrationMixin, CacheIsolationTestCase, ModuleS
self.mock_list()
self.mock_pathways(self.pathways)
self.mock_courses(self.course_runs)
for uuid in self.uuids:
program = programs[PROGRAM_CACHE_KEY_TPL.format(uuid=uuid)]
@@ -280,7 +230,6 @@ class TestCachePrograms(CatalogIntegrationMixin, CacheIsolationTestCase, ModuleS
}
self.mock_list()
self.mock_courses(self.course_runs)
for uuid in self.uuids:
program = programs[PROGRAM_CACHE_KEY_TPL.format(uuid=uuid)]
self.mock_detail(uuid, program)
@@ -320,89 +269,6 @@ class TestCachePrograms(CatalogIntegrationMixin, CacheIsolationTestCase, ModuleS
self.assertEqual(pathway, pathways_dict[key])
def test_handle_courses(self):
"""
Verify that the command requests and caches course to program uuids
"""
UserFactory(username=self.catalog_integration.service_username)
programs = {
PROGRAM_CACHE_KEY_TPL.format(uuid=program['uuid']): program for program in self.programs
}
courses = {
COURSE_PROGRAMS_CACHE_KEY_TPL.format(course_run_id=course['key']):
[pu['uuid'] for pu in course['programs']]
for course in self.course_runs
}
self.mock_list()
self.mock_pathways(self.pathways)
self.mock_courses(self.course_runs)
for uuid in self.uuids:
program = programs[PROGRAM_CACHE_KEY_TPL.format(uuid=uuid)]
self.mock_detail(uuid, program)
call_command('cache_programs')
cached_courses = cache.get_many(list(courses.keys()))
self.assertEqual(
set(cached_courses),
set(courses)
)
# We can't use a set comparison here because these values are dictionaries
# and aren't hashable. We've already verified that all pathways came out
# of the cache above, so all we need to do here is verify the accuracy of
# the data itself.
for key, course in cached_courses.items():
self.assertEqual(course, courses[key])
def test_handle_courses_multiple_pages(self):
"""
Verify that the command requests and caches course to program uuids
"""
UserFactory(username=self.catalog_integration.service_username)
programs = {
PROGRAM_CACHE_KEY_TPL.format(uuid=program['uuid']): program for program in self.programs
}
courses = {
COURSE_PROGRAMS_CACHE_KEY_TPL.format(course_run_id=course['key']):
[pu['uuid'] for pu in course['programs']]
for course in self.course_runs
}
self.mock_list()
self.mock_pathways(self.pathways)
# mock 3 pages of course_runs, starting at the last
self.mock_courses(self.course_runs[20:], page_number=3, final=True)
self.mock_courses(self.course_runs[10:20], page_number=2, final=False)
self.mock_courses(self.course_runs[:10], page_number=1, final=False)
for uuid in self.uuids:
program = programs[PROGRAM_CACHE_KEY_TPL.format(uuid=uuid)]
self.mock_detail(uuid, program)
call_command('cache_programs')
cached_courses = cache.get_many(list(courses.keys()))
self.assertEqual(
set(cached_courses),
set(courses)
)
# We can't use a set comparison here because these values are dictionaries
# and aren't hashable. We've already verified that all pathways came out
# of the cache above, so all we need to do here is verify the accuracy of
# the data itself.
for key, course in cached_courses.items():
self.assertEqual(course, courses[key])
def test_handle_missing_service_user(self):
"""
Verify that the command raises an exception when run without a service
@@ -421,9 +287,9 @@ class TestCachePrograms(CatalogIntegrationMixin, CacheIsolationTestCase, ModuleS
"""
UserFactory(username=self.catalog_integration.service_username)
with self.assertRaises(CommandError) as context:
with self.assertRaises(SystemExit) as context:
call_command('cache_programs')
self.assertEqual(str(context.exception), "Caching program information failed")
self.assertEqual(context.exception.code, 1)
cached_uuids = cache.get(SITE_PROGRAM_UUIDS_CACHE_KEY_TPL.format(domain=self.site_domain))
self.assertEqual(cached_uuids, [])
@@ -444,9 +310,9 @@ class TestCachePrograms(CatalogIntegrationMixin, CacheIsolationTestCase, ModuleS
program = programs[PROGRAM_CACHE_KEY_TPL.format(uuid=uuid)]
self.mock_detail(uuid, program)
with self.assertRaises(CommandError) as context:
with self.assertRaises(SystemExit) as context:
call_command('cache_programs')
self.assertEqual(str(context.exception), "Caching program information failed")
self.assertEqual(context.exception.code, 1)
cached_pathways = cache.get(SITE_PATHWAY_IDS_CACHE_KEY_TPL.format(domain=self.site_domain))
self.assertEqual(cached_pathways, [])
@@ -472,10 +338,10 @@ class TestCachePrograms(CatalogIntegrationMixin, CacheIsolationTestCase, ModuleS
program = partial_programs[PROGRAM_CACHE_KEY_TPL.format(uuid=uuid)]
self.mock_detail(uuid, program)
with self.assertRaises(CommandError) as context:
with self.assertRaises(SystemExit) as context:
call_command('cache_programs')
self.assertEqual(str(context.exception), "Caching program information failed")
self.assertEqual(context.exception.code, 1)
cached_uuids = cache.get(SITE_PROGRAM_UUIDS_CACHE_KEY_TPL.format(domain=self.site_domain))
self.assertEqual(
@@ -495,18 +361,3 @@ class TestCachePrograms(CatalogIntegrationMixin, CacheIsolationTestCase, ModuleS
# cached programs have a pathways field added to them, remove before comparing
del program['pathway_ids']
self.assertEqual(program, partial_programs[key])
def test_handle_missing_course_runs(self):
"""
Verify that the command raises an exception when it fails to retrieve course runs.
"""
UserFactory(username=self.catalog_integration.service_username)
self.mock_list()
# Only mock out the first page of courses. Pages 2 and 3 will be missing.
self.mock_courses(self.course_runs[:10], page_number=1, final=False)
with self.assertRaises(CommandError) as context:
call_command('cache_programs')
self.assertEqual(str(context.exception), "Caching program information failed")

View File

@@ -211,26 +211,6 @@ class ProgramFactory(DictFactoryBase):
weeks_to_complete = fake.random_int(1, 45)
class ProgramDescriptionFactory(DictFactoryBase):
uuid = factory.Faker('uuid4')
title = factory.Faker('catch_phrase')
type = factory.Faker('word')
marketing_slug = factory.Faker('slug')
marketing_url = factory.Faker('url')
number_of_courses = fake.random_int(1, 10) # pylint: disable=no-member
@classmethod
def from_program(cls, program):
return cls.create(
uuid=program['uuid'],
title=program['title'],
type=program['type'],
marketing_slug=program['marketing_slug'],
marketing_url=program['marketing_url'],
number_of_courses=len(program['courses']),
)
class ProgramTypeFactory(DictFactoryBase):
name = factory.Faker('word')
logo_image = factory.LazyFunction(generate_sized_stdimage)

View File

@@ -167,7 +167,7 @@ class TestGetPrograms(CacheIsolationTestCase):
expected_program = ProgramFactory()
expected_uuid = expected_program['uuid']
self.assertEqual(get_programs(uuid=expected_uuid), None)
self.assertEqual(get_programs(self.site, uuid=expected_uuid), None)
mock_warning.assert_called_once_with(
u'Failed to get details for program {uuid} from the cache.'.format(uuid=expected_uuid)
)
@@ -179,7 +179,7 @@ class TestGetPrograms(CacheIsolationTestCase):
None
)
actual_program = get_programs(uuid=expected_uuid)
actual_program = get_programs(self.site, uuid=expected_uuid)
self.assertEqual(actual_program, expected_program)
self.assertFalse(mock_warning.called)

View File

@@ -13,13 +13,9 @@ from pytz import UTC
from entitlements.utils import is_course_run_entitlement_fulfillable
from openedx.core.constants import COURSE_PUBLISHED
from openedx.core.djangoapps.catalog.cache import (
COURSE_PROGRAMS_CACHE_KEY_TPL,
PATHWAY_CACHE_KEY_TPL,
PROGRAM_CACHE_KEY_TPL,
SITE_PATHWAY_IDS_CACHE_KEY_TPL,
SITE_PROGRAM_UUIDS_CACHE_KEY_TPL
)
from openedx.core.djangoapps.catalog.cache import (PATHWAY_CACHE_KEY_TPL, PROGRAM_CACHE_KEY_TPL,
SITE_PATHWAY_IDS_CACHE_KEY_TPL,
SITE_PROGRAM_UUIDS_CACHE_KEY_TPL)
from openedx.core.djangoapps.catalog.models import CatalogIntegration
from openedx.core.djangoapps.oauth_dispatch.jwt import create_jwt_for_user
from openedx.core.lib.edx_api_utils import get_edx_api_data
@@ -79,23 +75,21 @@ def check_catalog_integration_and_get_user(error_message_field):
return None, catalog_integration
def get_programs(site=None, uuid=None, course=None): # pylint: disable=redefined-outer-name
def get_programs(site, uuid=None):
"""Read programs from the cache.
The cache is populated by a management command, cache_programs.
Keyword Arguments:
Arguments:
site (Site): django.contrib.sites.models object
Keyword Arguments:
uuid (string): UUID identifying a specific program to read from the cache.
course (string): course id identifying a specific course run to read from the cache.
Returns:
list of dict, representing programs.
dict, if a specific program is requested.
"""
if len([arg for arg in (site, uuid, course) if arg is not None]) != 1:
raise TypeError('get_programs takes exactly one argument')
missing_details_msg_tpl = u'Failed to get details for program {uuid} from the cache.'
if uuid:
@@ -104,14 +98,9 @@ def get_programs(site=None, uuid=None, course=None): # pylint: disable=redefine
logger.warning(missing_details_msg_tpl.format(uuid=uuid))
return program
elif course:
uuids = cache.get(COURSE_PROGRAMS_CACHE_KEY_TPL.format(course_run_id=course))
if not uuids:
logger.warning(missing_details_msg_tpl.format(course=course))
else:
uuids = cache.get(SITE_PROGRAM_UUIDS_CACHE_KEY_TPL.format(domain=site.domain), [])
if not uuids:
logger.warning(u'Failed to get program UUIDs from the cache for site {}.'.format(site.domain))
uuids = cache.get(SITE_PROGRAM_UUIDS_CACHE_KEY_TPL.format(domain=site.domain), [])
if not uuids:
logger.warning(u'Failed to get program UUIDs from the cache for site {}.'.format(site.domain))
programs = cache.get_many([PROGRAM_CACHE_KEY_TPL.format(uuid=uuid) for uuid in uuids])
programs = list(programs.values())

View File

@@ -109,7 +109,7 @@ class ProgramProgressMeter(object):
self.course_grade_factory = CourseGradeFactory()
if uuid:
self.programs = [get_programs(uuid=uuid)]
self.programs = [get_programs(self.site, uuid=uuid)]
else:
self.programs = attach_program_detail_url(get_programs(self.site), self.mobile_only)

View File

@@ -99,8 +99,4 @@ class Command(BaseCommand):
end += chunk_size
sleep(sleep_time_secs)
print('Finished! Updated {} total preferences from {} to {}'.format(
updated_count,
old_lang_code,
new_lang_code
))
print('Finished! Updated {} total preferences from {} to {}'.format(updated_count, old_lang_code, new_lang_code))