Пример #1
0
    def compare_date_fields(self, details, encoded, context, field):
        if details[field] is not None:
            date = Date()
            if field in encoded and encoded[field] is not None:
                encoded_encoded = date.from_json(encoded[field])
                dt1 = CourseDetailsViewTest.struct_to_datetime(encoded_encoded)

                if isinstance(details[field], datetime.datetime):
                    dt2 = details[field]
                else:
                    details_encoded = date.from_json(details[field])
                    dt2 = CourseDetailsViewTest.struct_to_datetime(details_encoded)

                expected_delta = datetime.timedelta(0)
                self.assertEqual(dt1 - dt2, expected_delta, str(dt1) + "!=" + str(dt2) + " at " + context)
            else:
                self.fail(field + " missing from encoded but in details at " + context)
        elif field in encoded and encoded[field] is not None:
            self.fail(field + " included in encoding but missing from details at " + context)
    def compare_date_fields(self, details, encoded, context, field):
        if details[field] is not None:
            date = Date()
            if field in encoded and encoded[field] is not None:
                dt1 = date.from_json(encoded[field])
                dt2 = details[field]

                self.assertEqual(dt1, dt2, msg="{} != {} at {}".format(dt1, dt2, context))
            else:
                self.fail(field + " missing from encoded but in details at " + context)
        elif field in encoded and encoded[field] is not None:
            self.fail(field + " included in encoding but missing from details at " + context)
Пример #3
0
    def compare_date_fields(self, details, encoded, context, field):
        if details[field] is not None:
            date = Date()
            if field in encoded and encoded[field] is not None:
                dt1 = date.from_json(encoded[field])
                dt2 = details[field]

                expected_delta = datetime.timedelta(0)
                self.assertEqual(dt1 - dt2, expected_delta, str(dt1) + "!=" + str(dt2) + " at " + context)
            else:
                self.fail(field + " missing from encoded but in details at " + context)
        elif field in encoded and encoded[field] is not None:
            self.fail(field + " included in encoding but missing from details at " + context)
    def compare_date_fields(self, details, encoded, context, field):
        """
        Compare the given date fields between the before and after doing json deserialization
        """
        if details[field] is not None:
            date = Date()
            if field in encoded and encoded[field] is not None:
                dt1 = date.from_json(encoded[field])
                dt2 = details[field]

                self.assertEqual(dt1, dt2, msg="{} != {} at {}".format(dt1, dt2, context))
            else:
                self.fail(field + " missing from encoded but in details at " + context)
        elif field in encoded and encoded[field] is not None:
            self.fail(field + " included in encoding but missing from details at " + context)
Пример #5
0
    def compare_date_fields(self, details, encoded, context, field):
        """
        Compare the given date fields between the before and after doing json deserialization
        """
        if details[field] is not None:
            date = Date()
            if field in encoded and encoded[field] is not None:
                dt1 = date.from_json(encoded[field])
                dt2 = details[field]

                self.assertEqual(dt1, dt2, msg="{} != {} at {}".format(dt1, dt2, context))
            else:
                self.fail(field + " missing from encoded but in details at " + context)
        elif field in encoded and encoded[field] is not None:
            self.fail(field + " included in encoding but missing from details at " + context)
    def compare_date_fields(self, details, encoded, context, field):
        if details[field] is not None:
            date = Date()
            if field in encoded and encoded[field] is not None:
                encoded_encoded = date.from_json(encoded[field])
                dt1 = CourseDetailsViewTest.struct_to_datetime(encoded_encoded)

                if isinstance(details[field], datetime.datetime):
                    dt2 = details[field]
                else:
                    details_encoded = date.from_json(details[field])
                    dt2 = CourseDetailsViewTest.struct_to_datetime(
                        details_encoded)

                expected_delta = datetime.timedelta(0)
                self.assertEqual(dt1 - dt2, expected_delta,
                                 str(dt1) + "!=" + str(dt2) + " at " + context)
            else:
                self.fail(field + " missing from encoded but in details at " +
                          context)
        elif field in encoded and encoded[field] is not None:
            self.fail(field +
                      " included in encoding but missing from details at " +
                      context)
Пример #7
0
    def compare_date_fields(self, details, encoded, context, field):
        if details[field] is not None:
            date = Date()
            if field in encoded and encoded[field] is not None:
                dt1 = date.from_json(encoded[field])
                dt2 = details[field]

                expected_delta = datetime.timedelta(0)
                self.assertEqual(dt1 - dt2, expected_delta,
                                 str(dt1) + "!=" + str(dt2) + " at " + context)
            else:
                self.fail(field + " missing from encoded but in details at " +
                          context)
        elif field in encoded and encoded[field] is not None:
            self.fail(field +
                      " included in encoding but missing from details at " +
                      context)
def move_overrides_to_edx_when(apps, schema_editor):
    from xmodule.fields import Date
    from edx_when import api
    date_field = Date()
    StudentFieldOverride = apps.get_model('courseware', 'StudentFieldOverride')
    log = logging.getLogger(__name__)
    for override in StudentFieldOverride.objects.filter(field='due'):
        try:
            abs_date = date_field.from_json(json.loads(override.value))
            api.set_date_for_block(
                override.course_id,
                override.location,
                'due',
                abs_date,
                user=override.student)
        except Exception:  # pylint: disable=broad-except
            log.exception("migrating %d %r: %r", override.id, override.location, override.value)
def move_overrides_to_edx_when(apps, schema_editor):
    from xmodule.fields import Date
    from edx_when import api
    date_field = Date()
    StudentFieldOverride = apps.get_model('courseware', 'StudentFieldOverride')
    log = logging.getLogger(__name__)
    for override in StudentFieldOverride.objects.filter(field='due'):
        try:
            abs_date = date_field.from_json(json.loads(override.value))
            api.set_date_for_block(
                override.course_id,
                override.location,
                'due',
                abs_date,
                user=override.student)
        except Exception:  # pylint: disable=broad-except
            log.exception("migrating %d %r: %r", override.id, override.location, override.value)
Пример #10
0
    def update_from_json(cls, course_key, jsondict, user):
        """
        Decode the json into CourseDetails and save any changed attrs to the db
        """
        module_store = modulestore('direct')
        descriptor = module_store.get_course(course_key)

        dirty = False

        # In the descriptor's setter, the date is converted to JSON using Date's to_json method.
        # Calling to_json on something that is already JSON doesn't work. Since reaching directly
        # into the model is nasty, convert the JSON Date to a Python date, which is what the
        # setter expects as input.
        date = Date()

        if 'start_date' in jsondict:
            converted = date.from_json(jsondict['start_date'])
        else:
            converted = None
        if converted != descriptor.start:
            dirty = True
            descriptor.start = converted

        if 'end_date' in jsondict:
            converted = date.from_json(jsondict['end_date'])
        else:
            converted = None

        if converted != descriptor.end:
            dirty = True
            descriptor.end = converted

        if 'enrollment_start' in jsondict:
            converted = date.from_json(jsondict['enrollment_start'])
        else:
            converted = None

        if converted != descriptor.enrollment_start:
            dirty = True
            descriptor.enrollment_start = converted

        if 'enrollment_end' in jsondict:
            converted = date.from_json(jsondict['enrollment_end'])
        else:
            converted = None

        if converted != descriptor.enrollment_end:
            dirty = True
            descriptor.enrollment_end = converted

        if 'course_image_name' in jsondict and jsondict['course_image_name'] != descriptor.course_image:
            descriptor.course_image = jsondict['course_image_name']
            dirty = True

        if dirty:
            module_store.update_item(descriptor, user.id)

        # NOTE: below auto writes to the db w/o verifying that any of the fields actually changed
        # to make faster, could compare against db or could have client send over a list of which fields changed.
        for about_type in ['syllabus', 'overview', 'effort', 'short_description']:
            cls.update_about_item(course_key, about_type, jsondict[about_type], descriptor, user)

        recomposed_video_tag = CourseDetails.recompose_video_tag(jsondict['intro_video'])
        cls.update_about_item(course_key, 'video', recomposed_video_tag, descriptor, user)

        # Could just return jsondict w/o doing any db reads, but I put the reads in as a means to confirm
        # it persisted correctly
        return CourseDetails.fetch(course_key)
Пример #11
0
    def update_from_json(cls, course_key, jsondict, user):  # pylint: disable=too-many-statements
        """
        Decode the json into CourseDetails and save any changed attrs to the db
        """
        module_store = modulestore()
        descriptor = module_store.get_course(course_key)

        dirty = False

        # In the descriptor's setter, the date is converted to JSON
        # using Date's to_json method. Calling to_json on something that
        # is already JSON doesn't work. Since reaching directly into the
        # model is nasty, convert the JSON Date to a Python date, which
        # is what the setter expects as input.
        date = Date()

        if 'start_date' in jsondict:
            converted = date.from_json(jsondict['start_date'])
        else:
            converted = None
        if converted != descriptor.start:
            dirty = True
            descriptor.start = converted

        if 'end_date' in jsondict:
            converted = date.from_json(jsondict['end_date'])
        else:
            converted = None

        if converted != descriptor.end:
            dirty = True
            descriptor.end = converted

        if 'enrollment_start' in jsondict:
            converted = date.from_json(jsondict['enrollment_start'])
        else:
            converted = None

        if converted != descriptor.enrollment_start:
            dirty = True
            descriptor.enrollment_start = converted

        if 'enrollment_end' in jsondict:
            converted = date.from_json(jsondict['enrollment_end'])
        else:
            converted = None

        if converted != descriptor.enrollment_end:
            dirty = True
            descriptor.enrollment_end = converted

        if 'certificate_available_date' in jsondict:
            converted = date.from_json(jsondict['certificate_available_date'])
        else:
            converted = None

        if converted != descriptor.certificate_available_date:
            dirty = True
            descriptor.certificate_available_date = converted

        if 'course_image_name' in jsondict and jsondict[
                'course_image_name'] != descriptor.course_image:
            descriptor.course_image = jsondict['course_image_name']
            dirty = True

        if 'banner_image_name' in jsondict and jsondict[
                'banner_image_name'] != descriptor.banner_image:
            descriptor.banner_image = jsondict['banner_image_name']
            dirty = True

        if 'video_thumbnail_image_name' in jsondict \
                and jsondict['video_thumbnail_image_name'] != descriptor.video_thumbnail_image:
            descriptor.video_thumbnail_image = jsondict[
                'video_thumbnail_image_name']
            dirty = True

        if 'pre_requisite_courses' in jsondict \
                and sorted(jsondict['pre_requisite_courses']) != sorted(descriptor.pre_requisite_courses):
            descriptor.pre_requisite_courses = jsondict[
                'pre_requisite_courses']
            dirty = True

        if 'license' in jsondict:
            descriptor.license = jsondict['license']
            dirty = True

        if 'learning_info' in jsondict:
            descriptor.learning_info = jsondict['learning_info']
            dirty = True

        if 'instructor_info' in jsondict:
            descriptor.instructor_info = jsondict['instructor_info']
            dirty = True

        if 'language' in jsondict and jsondict[
                'language'] != descriptor.language:
            descriptor.language = jsondict['language']
            dirty = True

        if (SelfPacedConfiguration.current().enabled
                and descriptor.can_toggle_course_pacing
                and 'self_paced' in jsondict
                and jsondict['self_paced'] != descriptor.self_paced):
            descriptor.self_paced = jsondict['self_paced']
            dirty = True

        if dirty:
            module_store.update_item(descriptor, user.id)

        # NOTE: below auto writes to the db w/o verifying that any of
        # the fields actually changed to make faster, could compare
        # against db or could have client send over a list of which
        # fields changed.
        for attribute in ABOUT_ATTRIBUTES:
            if attribute in jsondict:
                cls.update_about_item(descriptor, attribute,
                                      jsondict[attribute], user.id)

        cls.update_about_video(descriptor, jsondict['intro_video'], user.id)

        # Could just return jsondict w/o doing any db reads, but I put
        # the reads in as a means to confirm it persisted correctly
        return CourseDetails.fetch(course_key)
Пример #12
0
    def update_from_json(cls, jsondict):
        """
        Decode the json into CourseDetails and save any changed attrs to the db
        """
        # TODO make it an error for this to be undefined & for it to not be retrievable from modulestore
        course_location = Location(jsondict['course_location'])
        # Will probably want to cache the inflight courses because every blur generates an update
        descriptor = get_modulestore(course_location).get_item(course_location)

        dirty = False

        # In the descriptor's setter, the date is converted to JSON using Date's to_json method.
        # Calling to_json on something that is already JSON doesn't work. Since reaching directly
        # into the model is nasty, convert the JSON Date to a Python date, which is what the
        # setter expects as input.
        date = Date()

        if 'start_date' in jsondict:
            converted = date.from_json(jsondict['start_date'])
        else:
            converted = None
        if converted != descriptor.start:
            dirty = True
            descriptor.start = converted

        if 'end_date' in jsondict:
            converted = date.from_json(jsondict['end_date'])
        else:
            converted = None

        if converted != descriptor.end:
            dirty = True
            descriptor.end = converted

        if 'enrollment_start' in jsondict:
            converted = date.from_json(jsondict['enrollment_start'])
        else:
            converted = None

        if converted != descriptor.enrollment_start:
            dirty = True
            descriptor.enrollment_start = converted

        if 'enrollment_end' in jsondict:
            converted = date.from_json(jsondict['enrollment_end'])
        else:
            converted = None

        if converted != descriptor.enrollment_end:
            dirty = True
            descriptor.enrollment_end = converted

        if dirty:
            get_modulestore(course_location).update_metadata(
                course_location, own_metadata(descriptor))

        # NOTE: below auto writes to the db w/o verifying that any of the fields actually changed
        # to make faster, could compare against db or could have client send over a list of which fields changed.
        temploc = Location(course_location).replace(category='about',
                                                    name='syllabus')
        update_item(temploc, jsondict['syllabus'])

        temploc = temploc.replace(name='overview')
        update_item(temploc, jsondict['overview'])

        temploc = temploc.replace(name='effort')
        update_item(temploc, jsondict['effort'])

        temploc = temploc.replace(name='video')
        recomposed_video_tag = CourseDetails.recompose_video_tag(
            jsondict['intro_video'])
        update_item(temploc, recomposed_video_tag)

        # Could just generate and return a course obj w/o doing any db reads, but I put the reads in as a means to confirm
        # it persisted correctly
        return CourseDetails.fetch(course_location)
Пример #13
0
    def update_from_json(cls, course_locator, jsondict):
        """
        Decode the json into CourseDetails and save any changed attrs to the db
        """
        course_old_location = loc_mapper().translate_locator_to_location(
            course_locator)
        descriptor = get_modulestore(course_old_location).get_item(
            course_old_location)

        dirty = False

        # In the descriptor's setter, the date is converted to JSON using Date's to_json method.
        # Calling to_json on something that is already JSON doesn't work. Since reaching directly
        # into the model is nasty, convert the JSON Date to a Python date, which is what the
        # setter expects as input.
        date = Date()

        if 'start_date' in jsondict:
            converted = date.from_json(jsondict['start_date'])
        else:
            converted = None
        if converted != descriptor.start:
            dirty = True
            descriptor.start = converted

        if 'end_date' in jsondict:
            converted = date.from_json(jsondict['end_date'])
        else:
            converted = None

        if converted != descriptor.end:
            dirty = True
            descriptor.end = converted

        if 'enrollment_start' in jsondict:
            converted = date.from_json(jsondict['enrollment_start'])
        else:
            converted = None

        if converted != descriptor.enrollment_start:
            dirty = True
            descriptor.enrollment_start = converted

        if 'enrollment_end' in jsondict:
            converted = date.from_json(jsondict['enrollment_end'])
        else:
            converted = None

        if converted != descriptor.enrollment_end:
            dirty = True
            descriptor.enrollment_end = converted

        if 'course_image_name' in jsondict and jsondict[
                'course_image_name'] != descriptor.course_image:
            descriptor.course_image = jsondict['course_image_name']
            dirty = True

        if dirty:
            # Save the data that we've just changed to the underlying
            # MongoKeyValueStore before we update the mongo datastore.
            descriptor.save()

            get_modulestore(course_old_location).update_metadata(
                course_old_location, own_metadata(descriptor))

        # NOTE: below auto writes to the db w/o verifying that any of the fields actually changed
        # to make faster, could compare against db or could have client send over a list of which fields changed.
        temploc = Location(course_old_location).replace(category='about',
                                                        name='syllabus')
        update_item(temploc, jsondict['syllabus'])

        temploc = temploc.replace(name='overview')
        update_item(temploc, jsondict['overview'])

        temploc = temploc.replace(name='effort')
        update_item(temploc, jsondict['effort'])

        temploc = temploc.replace(name='video')
        recomposed_video_tag = CourseDetails.recompose_video_tag(
            jsondict['intro_video'])
        update_item(temploc, recomposed_video_tag)

        # Could just return jsondict w/o doing any db reads, but I put the reads in as a means to confirm
        # it persisted correctly
        return CourseDetails.fetch(course_locator)
Пример #14
0
    def update_from_json(cls, jsondict):
        """
        Decode the json into CourseDetails and save any changed attrs to the db
        """
        # TODO make it an error for this to be undefined & for it to not be retrievable from modulestore
        course_location = jsondict['course_location']
        # Will probably want to cache the inflight courses because every blur generates an update
        descriptor = get_modulestore(course_location).get_item(course_location)

        dirty = False

        # In the descriptor's setter, the date is converted to JSON using Date's to_json method.
        # Calling to_json on something that is already JSON doesn't work. Since reaching directly
        # into the model is nasty, convert the JSON Date to a Python date, which is what the
        # setter expects as input.
        date = Date()

        if 'start_date' in jsondict:
            converted = date.from_json(jsondict['start_date'])
        else:
            converted = None
        if converted != descriptor.start:
            dirty = True
            descriptor.start = converted

        if 'end_date' in jsondict:
            converted = date.from_json(jsondict['end_date'])
        else:
            converted = None

        if converted != descriptor.end:
            dirty = True
            descriptor.end = converted

        if 'enrollment_start' in jsondict:
            converted = date.from_json(jsondict['enrollment_start'])
        else:
            converted = None

        if converted != descriptor.enrollment_start:
            dirty = True
            descriptor.enrollment_start = converted

        if 'enrollment_end' in jsondict:
            converted = date.from_json(jsondict['enrollment_end'])
        else:
            converted = None

        if converted != descriptor.enrollment_end:
            dirty = True
            descriptor.enrollment_end = converted

        if dirty:
            get_modulestore(course_location).update_metadata(course_location, own_metadata(descriptor))

        # NOTE: below auto writes to the db w/o verifying that any of the fields actually changed
        # to make faster, could compare against db or could have client send over a list of which fields changed.
        temploc = Location(course_location)._replace(category='about', name='syllabus')
        update_item(temploc, jsondict['syllabus'])

        temploc = temploc._replace(name='overview')
        update_item(temploc, jsondict['overview'])

        temploc = temploc._replace(name='effort')
        update_item(temploc, jsondict['effort'])

        temploc = temploc._replace(name='video')
        recomposed_video_tag = CourseDetails.recompose_video_tag(jsondict['intro_video'])
        update_item(temploc, recomposed_video_tag)

        # Could just generate and return a course obj w/o doing any db reads, but I put the reads in as a means to confirm
        # it persisted correctly
        return CourseDetails.fetch(course_location)
Пример #15
0
    def update_from_json(cls, course_key, jsondict, user):  # pylint: disable=too-many-statements
        """
        Decode the json into CourseDetails and save any changed attrs to the db
        """
        module_store = modulestore()
        descriptor = module_store.get_course(course_key)

        dirty = False

        # In the descriptor's setter, the date is converted to JSON
        # using Date's to_json method. Calling to_json on something that
        # is already JSON doesn't work. Since reaching directly into the
        # model is nasty, convert the JSON Date to a Python date, which
        # is what the setter expects as input.
        date = Date()

        if 'start_date' in jsondict:
            converted = date.from_json(jsondict['start_date'])
        else:
            converted = None
        if converted != descriptor.start:
            dirty = True
            descriptor.start = converted

        if 'end_date' in jsondict:
            converted = date.from_json(jsondict['end_date'])
        else:
            converted = None

        if converted != descriptor.end:
            dirty = True
            descriptor.end = converted

        if 'enrollment_start' in jsondict:
            converted = date.from_json(jsondict['enrollment_start'])
        else:
            converted = None

        if converted != descriptor.enrollment_start:
            dirty = True
            descriptor.enrollment_start = converted

        if 'enrollment_end' in jsondict:
            converted = date.from_json(jsondict['enrollment_end'])
        else:
            converted = None

        if converted != descriptor.enrollment_end:
            dirty = True
            descriptor.enrollment_end = converted

        if 'course_image_name' in jsondict and jsondict['course_image_name'] != descriptor.course_image:
            descriptor.course_image = jsondict['course_image_name']
            dirty = True

        if 'pre_requisite_courses' in jsondict \
                and sorted(jsondict['pre_requisite_courses']) != sorted(descriptor.pre_requisite_courses):
            descriptor.pre_requisite_courses = jsondict['pre_requisite_courses']
            dirty = True

        if 'license' in jsondict:
            descriptor.license = jsondict['license']
            dirty = True

        if 'language' in jsondict and jsondict['language'] != descriptor.language:
            descriptor.language = jsondict['language']
            dirty = True

        if (SelfPacedConfiguration.current().enabled
                and descriptor.can_toggle_course_pacing
                and 'self_paced' in jsondict
                and jsondict['self_paced'] != descriptor.self_paced):
            descriptor.self_paced = jsondict['self_paced']
            dirty = True

        if dirty:
            module_store.update_item(descriptor, user.id)

        # NOTE: below auto writes to the db w/o verifying that any of
        # the fields actually changed to make faster, could compare
        # against db or could have client send over a list of which
        # fields changed.
        for attribute in ABOUT_ATTRIBUTES:
            if attribute in jsondict:
                cls.update_about_item(descriptor, attribute, jsondict[attribute], user.id)

        cls.update_about_video(descriptor, jsondict['intro_video'], user.id)

        # Could just return jsondict w/o doing any db reads, but I put
        # the reads in as a means to confirm it persisted correctly
        return CourseDetails.fetch(course_key)
    def update_from_json(cls, course_key, jsondict, user):
        """
        Decode the json into CourseDetails and save any changed attrs to the db
        """
        module_store = modulestore()
        descriptor = module_store.get_course(course_key)

        dirty = False

        # In the descriptor's setter, the date is converted to JSON using Date's to_json method.
        # Calling to_json on something that is already JSON doesn't work. Since reaching directly
        # into the model is nasty, convert the JSON Date to a Python date, which is what the
        # setter expects as input.
        date = Date()

        if "start_date" in jsondict:
            converted = date.from_json(jsondict["start_date"])
        else:
            converted = None
        if converted != descriptor.start:
            dirty = True
            descriptor.start = converted

        if "end_date" in jsondict:
            converted = date.from_json(jsondict["end_date"])
        else:
            converted = None

        if converted != descriptor.end:
            dirty = True
            descriptor.end = converted

        if "enrollment_start" in jsondict:
            converted = date.from_json(jsondict["enrollment_start"])
        else:
            converted = None

        if converted != descriptor.enrollment_start:
            dirty = True
            descriptor.enrollment_start = converted

        if "enrollment_end" in jsondict:
            converted = date.from_json(jsondict["enrollment_end"])
        else:
            converted = None

        if converted != descriptor.enrollment_end:
            dirty = True
            descriptor.enrollment_end = converted

        if "course_image_name" in jsondict and jsondict["course_image_name"] != descriptor.course_image:
            descriptor.course_image = jsondict["course_image_name"]
            dirty = True

        if "pre_requisite_courses" in jsondict and sorted(jsondict["pre_requisite_courses"]) != sorted(
            descriptor.pre_requisite_courses
        ):
            descriptor.pre_requisite_courses = jsondict["pre_requisite_courses"]
            dirty = True

        if "license" in jsondict:
            descriptor.license = jsondict["license"]
            dirty = True

        if "language" in jsondict and jsondict["language"] != descriptor.language:
            descriptor.language = jsondict["language"]
            dirty = True

        if dirty:
            module_store.update_item(descriptor, user.id)

        # NOTE: below auto writes to the db w/o verifying that any of the fields actually changed
        # to make faster, could compare against db or could have client send over a list of which fields changed.
        for attribute in ABOUT_ATTRIBUTES:
            if attribute in jsondict:
                cls.update_about_item(course_key, attribute, jsondict[attribute], descriptor, user)

        recomposed_video_tag = CourseDetails.recompose_video_tag(jsondict["intro_video"])
        cls.update_about_item(course_key, "video", recomposed_video_tag, descriptor, user)

        # Could just return jsondict w/o doing any db reads, but I put the reads in as a means to confirm
        # it persisted correctly
        return CourseDetails.fetch(course_key)
Пример #17
0
    def update_from_json(cls, course_locator, jsondict):
        """
        Decode the json into CourseDetails and save any changed attrs to the db
        """
        course_old_location = loc_mapper().translate_locator_to_location(course_locator)
        descriptor = get_modulestore(course_old_location).get_item(course_old_location)

        dirty = False

        # In the descriptor's setter, the date is converted to JSON using Date's to_json method.
        # Calling to_json on something that is already JSON doesn't work. Since reaching directly
        # into the model is nasty, convert the JSON Date to a Python date, which is what the
        # setter expects as input.
        date = Date()

        if 'start_date' in jsondict:
            converted = date.from_json(jsondict['start_date'])
        else:
            converted = None
        if converted != descriptor.start:
            dirty = True
            descriptor.start = converted

        if 'end_date' in jsondict:
            converted = date.from_json(jsondict['end_date'])
        else:
            converted = None

        if converted != descriptor.end:
            dirty = True
            descriptor.end = converted

        if 'enrollment_start' in jsondict:
            converted = date.from_json(jsondict['enrollment_start'])
        else:
            converted = None

        if converted != descriptor.enrollment_start:
            dirty = True
            descriptor.enrollment_start = converted

        if 'enrollment_end' in jsondict:
            converted = date.from_json(jsondict['enrollment_end'])
        else:
            converted = None

        if converted != descriptor.enrollment_end:
            dirty = True
            descriptor.enrollment_end = converted

        if 'course_image_name' in jsondict and jsondict['course_image_name'] != descriptor.course_image:
            descriptor.course_image = jsondict['course_image_name']
            dirty = True

        if dirty:
            # Save the data that we've just changed to the underlying
            # MongoKeyValueStore before we update the mongo datastore.
            descriptor.save()

            get_modulestore(course_old_location).update_metadata(course_old_location, own_metadata(descriptor))

        # NOTE: below auto writes to the db w/o verifying that any of the fields actually changed
        # to make faster, could compare against db or could have client send over a list of which fields changed.
        temploc = Location(course_old_location).replace(category='about', name='syllabus')
        update_item(temploc, jsondict['syllabus'])

        temploc = temploc.replace(name='overview')
        update_item(temploc, jsondict['overview'])

        temploc = temploc.replace(name='tags')
        update_item(temploc, jsondict['tags'])

        temploc = temploc.replace(name='effort')
        update_item(temploc, jsondict['effort'])

        temploc = temploc.replace(name='video')
        recomposed_video_tag = CourseDetails.recompose_video_tag(jsondict['intro_video'])
        update_item(temploc, recomposed_video_tag)

        # Could just return jsondict w/o doing any db reads, but I put the reads in as a means to confirm
        # it persisted correctly
        return CourseDetails.fetch(course_locator)