diff --git a/common/djangoapps/xmodule_django/models.py b/common/djangoapps/xmodule_django/models.py index fdf7294f77..854c957876 100644 --- a/common/djangoapps/xmodule_django/models.py +++ b/common/djangoapps/xmodule_django/models.py @@ -2,11 +2,14 @@ Useful django models for implementing XBlock infrastructure in django. """ import warnings +import logging from django.db import models from django.core.exceptions import ValidationError from opaque_keys.edx.keys import CourseKey, UsageKey, BlockTypeKey +log = logging.getLogger(__name__) + class NoneToEmptyManager(models.Manager): """ @@ -104,6 +107,16 @@ class OpaqueKeyField(models.CharField): return None if isinstance(value, basestring): + if value.endswith('\n'): + # An opaque key with a trailing newline has leaked into the DB. + # Log and strip the value. + log.warning(u'{}:{}:{}:to_python: Invalid key: {}. Removing trailing newline.'.format( + self.model._meta.db_table, # pylint: disable=protected-access + self.name, + self.KEY_CLASS.__name__, + repr(value) + )) + value = value.rstrip() return self.KEY_CLASS.from_string(value) else: return value @@ -123,7 +136,17 @@ class OpaqueKeyField(models.CharField): return '' # CharFields should use '' as their empty value, rather than None assert isinstance(value, self.KEY_CLASS), "%s is not an instance of %s" % (value, self.KEY_CLASS) - return unicode(_strip_value(value)) + serialized_key = unicode(_strip_value(value)) + if serialized_key.endswith('\n'): + # An opaque key object serialized to a string with a trailing newline. + # Log the value - but do not modify it. + log.warning(u'{}:{}:{}:get_prep_value: Invalid key: {}.'.format( + self.model._meta.db_table, # pylint: disable=protected-access + self.name, + self.KEY_CLASS.__name__, + repr(serialized_key) + )) + return serialized_key def validate(self, value, model_instance): """Validate Empty values, otherwise defer to the parent""" diff --git a/lms/djangoapps/courseware/features/navigation.feature b/lms/djangoapps/courseware/features/navigation.feature index 4c7320d7e5..1f40f8ee5e 100644 --- a/lms/djangoapps/courseware/features/navigation.feature +++ b/lms/djangoapps/courseware/features/navigation.feature @@ -4,10 +4,11 @@ Feature: LMS.Navigate Course In order to access courseware I want to be able to navigate through the content - Scenario: I can navigate to a section - Given I am viewing a course with multiple sections - When I navigate to a section - Then I see the content of the section +# This scenario is flaky: See TNL-5315 +# Scenario: I can navigate to a section +# Given I am viewing a course with multiple sections +# When I navigate to a section +# Then I see the content of the section Scenario: I can navigate to subsections Given I am viewing a section with multiple subsections diff --git a/lms/templates/ccx/coach_dashboard.html b/lms/templates/ccx/coach_dashboard.html index 4d0d942b3a..a7078a1eb2 100644 --- a/lms/templates/ccx/coach_dashboard.html +++ b/lms/templates/ccx/coach_dashboard.html @@ -89,9 +89,9 @@ from openedx.core.djangolib.js_utils import (