From 4062b94a0fb07076af265069d639dbb00eb15812 Mon Sep 17 00:00:00 2001 From: Clinton Blackburn Date: Fri, 27 Feb 2015 09:57:37 -0500 Subject: [PATCH] Fixed Celery Serialization Bug CourseLocator is not JSON-serializable. Passing course keys as Unicode instead when scheduling tasks to update course structure. --- lms/djangoapps/course_structure_api/v0/tests.py | 2 +- lms/djangoapps/course_structure_api/v0/views.py | 2 +- .../commands/generate_course_structure.py | 2 +- .../djangoapps/content/course_structures/models.py | 13 ++++++++----- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/lms/djangoapps/course_structure_api/v0/tests.py b/lms/djangoapps/course_structure_api/v0/tests.py index 663f2f4bf5..1f05a07299 100644 --- a/lms/djangoapps/course_structure_api/v0/tests.py +++ b/lms/djangoapps/course_structure_api/v0/tests.py @@ -299,7 +299,7 @@ class CourseStructureTests(CourseDetailMixin, CourseViewTestsMixin, ModuleStoreT super(CourseStructureTests, self).setUp() # Ensure course structure exists for the course - update_course_structure(self.course.id) + update_course_structure(unicode(self.course.id)) def test_get(self): """ diff --git a/lms/djangoapps/course_structure_api/v0/views.py b/lms/djangoapps/course_structure_api/v0/views.py index e7bc54537f..f28d0dd141 100644 --- a/lms/djangoapps/course_structure_api/v0/views.py +++ b/lms/djangoapps/course_structure_api/v0/views.py @@ -192,7 +192,7 @@ class CourseStructure(CourseViewMixin, RetrieveAPIView): return super(CourseStructure, self).retrieve(request, *args, **kwargs) except models.CourseStructure.DoesNotExist: # If we don't have data stored, generate it and return a 503. - models.update_course_structure.delay(self.course.id) + models.update_course_structure.delay(unicode(self.course.id)) return Response(status=503, headers={'Retry-After': '120'}) def get_object(self, queryset=None): diff --git a/openedx/core/djangoapps/content/course_structures/management/commands/generate_course_structure.py b/openedx/core/djangoapps/content/course_structures/management/commands/generate_course_structure.py index 882a1a2981..b8b0eb4dc5 100644 --- a/openedx/core/djangoapps/content/course_structures/management/commands/generate_course_structure.py +++ b/openedx/core/djangoapps/content/course_structures/management/commands/generate_course_structure.py @@ -39,7 +39,7 @@ class Command(BaseCommand): for course_key in course_keys: try: - update_course_structure(course_key) + update_course_structure(unicode(course_key)) except Exception as e: logger.error('An error occurred while generating course structure for %s: %s', unicode(course_key), e) diff --git a/openedx/core/djangoapps/content/course_structures/models.py b/openedx/core/djangoapps/content/course_structures/models.py index 548aab9deb..7819aec3fc 100644 --- a/openedx/core/djangoapps/content/course_structures/models.py +++ b/openedx/core/djangoapps/content/course_structures/models.py @@ -4,7 +4,7 @@ import logging from celery.task import task from django.dispatch import receiver from model_utils.models import TimeStampedModel -from opaque_keys.edx.locator import CourseLocator +from opaque_keys.edx.keys import CourseKey from xmodule.modulestore.django import modulestore, SignalHandler from util.models import CompressedTextField @@ -60,7 +60,7 @@ def generate_course_structure(course_key): def listen_for_course_publish(sender, course_key, **kwargs): # Note: The countdown=0 kwarg is set to to ensure the method below does not attempt to access the course # before the signal emitter has finished all operations. This is also necessary to ensure all tests pass. - update_course_structure.delay(course_key, countdown=0) + update_course_structure.delay(unicode(course_key), countdown=0) @task() @@ -68,9 +68,12 @@ def update_course_structure(course_key): """ Regenerates and updates the course structure (in the database) for the specified course. """ - if not isinstance(course_key, CourseLocator): - logger.error('update_course_structure requires a CourseLocator. Given %s.', type(course_key)) - return + # Ideally we'd like to accept a CourseLocator; however, CourseLocator is not JSON-serializable (by default) so + # Celery's delayed tasks fail to start. For this reason, callers should pass the course key as a Unicode string. + if not isinstance(course_key, basestring): + raise ValueError('course_key must be a string. {} is not acceptable.'.format(type(course_key))) + + course_key = CourseKey.from_string(course_key) try: structure = generate_course_structure(course_key)