From b3a731550cabc5e0f06cb5c0b1d3ceb21f8024de Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Fri, 7 Jun 2019 15:42:06 -0400 Subject: [PATCH] Fix problems that drf-yasg uncovered. These were originally fixed individually, but had to be reverted, and are now combined in one commit. The originals were: 7b9040f6b0 This enum was backwards 8774ff1f9b Use ref_name to disambiguate serializers that drf-yasg would otherwise assume are the same. 8a44397139 Is this field missing because it is None? 4a1154a7ca Give a safer buffer for clearing the rate limiting 64c47856dd DRF 3.7.4 changed how you delegate to another view, so don't 7359ca4fb2 Is this right? It fixes two tests fdd66e5390 Adjust the expected error message for DRF 3.7.7 9257f68fd8 The default TIME_ZONE should be UTC --- cms/djangoapps/api/v1/serializers/course_runs.py | 2 +- cms/envs/common.py | 2 +- common/djangoapps/course_modes/api/serializers.py | 4 ++++ .../entitlements/api/v1/tests/test_serializers.py | 1 - lms/djangoapps/commerce/api/v1/serializers.py | 6 ++++++ lms/djangoapps/discussion/rest_api/api.py | 10 ++++++---- .../rest_api/v1/tests/test_grading_policy_view.py | 2 +- lms/djangoapps/mobile_api/users/serializers.py | 2 ++ .../program_enrollments/api/v1/tests/test_views.py | 4 ++-- lms/djangoapps/shoppingcart/tests/test_views.py | 8 ++++---- lms/envs/common.py | 2 +- openedx/core/djangoapps/enrollments/serializers.py | 4 ++++ .../djangoapps/user_api/accounts/tests/test_views.py | 2 +- .../djangoapps/user_api/preferences/tests/test_api.py | 2 +- openedx/core/djangoapps/user_api/serializers.py | 2 ++ .../features/content_type_gating/tests/test_models.py | 6 +++--- .../course_duration_limits/tests/test_models.py | 6 +++--- 17 files changed, 42 insertions(+), 23 deletions(-) diff --git a/cms/djangoapps/api/v1/serializers/course_runs.py b/cms/djangoapps/api/v1/serializers/course_runs.py index 44f722e79e..c49659ab92 100644 --- a/cms/djangoapps/api/v1/serializers/course_runs.py +++ b/cms/djangoapps/api/v1/serializers/course_runs.py @@ -125,7 +125,7 @@ class CourseRunImageSerializer(serializers.Serializer): class CourseRunSerializerCommonFieldsMixin(serializers.Serializer): schedule = CourseRunScheduleSerializer(source='*', required=False) pacing_type = CourseRunPacingTypeField(source='self_paced', required=False, - choices=(('instructor_paced', False), ('self_paced', True),)) + choices=((False, 'instructor_paced'), (True, 'self_paced'),)) class CourseRunSerializer(CourseRunSerializerCommonFieldsMixin, CourseRunTeamSerializerMixin, serializers.Serializer): diff --git a/cms/envs/common.py b/cms/envs/common.py index fa41067a51..fe2899bace 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -680,7 +680,7 @@ STATICFILES_DIRS = [ # Locale/Internationalization CELERY_TIMEZONE = 'UTC' -TIME_ZONE = 'America/New_York' # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name +TIME_ZONE = 'UTC' LANGUAGE_CODE = 'en' # http://www.i18nguy.com/unicode/language-identifiers.html LANGUAGES_BIDI = lms.envs.common.LANGUAGES_BIDI diff --git a/common/djangoapps/course_modes/api/serializers.py b/common/djangoapps/course_modes/api/serializers.py index 2df0493b79..7c287ac54d 100644 --- a/common/djangoapps/course_modes/api/serializers.py +++ b/common/djangoapps/course_modes/api/serializers.py @@ -28,6 +28,10 @@ class CourseModeSerializer(serializers.Serializer): sku = serializers.CharField(required=False) bulk_sku = serializers.CharField(required=False) + class Meta(object): + # For disambiguating within the drf-yasg swagger schema + ref_name = 'course_modes.CourseMode' + def create(self, validated_data): """ This method must be implemented for use in our diff --git a/common/djangoapps/entitlements/api/v1/tests/test_serializers.py b/common/djangoapps/entitlements/api/v1/tests/test_serializers.py index 2f487a94ea..f282dc9116 100644 --- a/common/djangoapps/entitlements/api/v1/tests/test_serializers.py +++ b/common/djangoapps/entitlements/api/v1/tests/test_serializers.py @@ -30,7 +30,6 @@ class EntitlementsSerializerTests(ModuleStoreTestCase): 'course_uuid': str(entitlement.course_uuid), 'mode': entitlement.mode, 'refund_locked': False, - 'enrollment_course_run': None, 'order_number': entitlement.order_number, 'created': entitlement.created.strftime('%Y-%m-%dT%H:%M:%S.%fZ'), 'modified': entitlement.modified.strftime('%Y-%m-%dT%H:%M:%S.%fZ'), diff --git a/lms/djangoapps/commerce/api/v1/serializers.py b/lms/djangoapps/commerce/api/v1/serializers.py index 6c66dfecf3..2eefe85428 100644 --- a/lms/djangoapps/commerce/api/v1/serializers.py +++ b/lms/djangoapps/commerce/api/v1/serializers.py @@ -36,6 +36,8 @@ class CourseModeSerializer(serializers.ModelSerializer): class Meta(object): model = CourseMode fields = ('name', 'currency', 'price', 'sku', 'bulk_sku', 'expires') + # For disambiguating within the drf-yasg swagger schema + ref_name = 'commerce.CourseMode' def validate_course_id(course_id): @@ -77,6 +79,10 @@ class CourseSerializer(serializers.Serializer): verification_deadline = PossiblyUndefinedDateTimeField(format=None, allow_null=True, required=False) modes = CourseModeSerializer(many=True) + class Meta(object): + # For disambiguating within the drf-yasg swagger schema + ref_name = 'commerce.Course' + def validate(self, attrs): """ Ensure the verification deadline occurs AFTER the course mode enrollment deadlines. """ verification_deadline = attrs.get('verification_deadline', None) diff --git a/lms/djangoapps/discussion/rest_api/api.py b/lms/djangoapps/discussion/rest_api/api.py index 27b9bdc04e..1b7a9aa870 100644 --- a/lms/djangoapps/discussion/rest_api/api.py +++ b/lms/djangoapps/discussion/rest_api/api.py @@ -61,6 +61,7 @@ from openedx.core.djangoapps.django_comment_common.signals import ( thread_voted ) from openedx.core.djangoapps.django_comment_common.utils import get_course_discussion_settings +from openedx.core.djangoapps.user_api.accounts.api import get_account_settings from openedx.core.djangoapps.user_api.accounts.views import AccountViewSet from openedx.core.lib.exceptions import CourseNotFoundError, DiscussionNotFoundError, PageNotFoundError @@ -365,10 +366,11 @@ def _get_user_profile_dict(request, usernames): A dict with username as key and user profile details as value. """ - request.GET = request.GET.copy() # Make a mutable copy of the GET parameters. - request.GET['username'] = usernames - user_profile_details = AccountViewSet.as_view({'get': 'list'})(request).data - + if usernames: + username_list = usernames.split(",") + else: + username_list = [] + user_profile_details = get_account_settings(request, username_list) return {user['username']: user for user in user_profile_details} diff --git a/lms/djangoapps/grades/rest_api/v1/tests/test_grading_policy_view.py b/lms/djangoapps/grades/rest_api/v1/tests/test_grading_policy_view.py index 09c56136a5..975e3f2399 100644 --- a/lms/djangoapps/grades/rest_api/v1/tests/test_grading_policy_view.py +++ b/lms/djangoapps/grades/rest_api/v1/tests/test_grading_policy_view.py @@ -122,7 +122,7 @@ class GradingPolicyTestMixin(object): """ The view should return HTTP status 401 if user is unauthenticated. """ - self.assert_get_for_course(expected_status_code=401, HTTP_AUTHORIZATION=None) + self.assert_get_for_course(expected_status_code=401, HTTP_AUTHORIZATION="") def test_staff_authorized(self): """ diff --git a/lms/djangoapps/mobile_api/users/serializers.py b/lms/djangoapps/mobile_api/users/serializers.py index e6814350e0..da0a7395ed 100644 --- a/lms/djangoapps/mobile_api/users/serializers.py +++ b/lms/djangoapps/mobile_api/users/serializers.py @@ -147,3 +147,5 @@ class UserSerializer(serializers.ModelSerializer): model = User fields = ('id', 'username', 'email', 'name', 'course_enrollments') lookup_field = 'username' + # For disambiguating within the drf-yasg swagger schema + ref_name = 'mobile_api.User' diff --git a/lms/djangoapps/program_enrollments/api/v1/tests/test_views.py b/lms/djangoapps/program_enrollments/api/v1/tests/test_views.py index ec25886b45..adfa913096 100644 --- a/lms/djangoapps/program_enrollments/api/v1/tests/test_views.py +++ b/lms/djangoapps/program_enrollments/api/v1/tests/test_views.py @@ -1738,8 +1738,8 @@ class ProgramCourseEnrollmentOverviewViewTests(ProgramCacheTestCaseMixin, Shared course_run_overview = response.data['course_runs'][0] - self.assertEqual(course_run_overview['start_date'], '2018-12-31T05:00:00Z') - self.assertEqual(course_run_overview['end_date'], '2019-01-02T05:00:00Z') + self.assertEqual(course_run_overview['start_date'], '2018-12-31T00:00:00Z') + self.assertEqual(course_run_overview['end_date'], '2019-01-02T00:00:00Z') # course run end date may not exist self.course_overview.end = None diff --git a/lms/djangoapps/shoppingcart/tests/test_views.py b/lms/djangoapps/shoppingcart/tests/test_views.py index 0a5cd7bf99..dd18a89a7e 100644 --- a/lms/djangoapps/shoppingcart/tests/test_views.py +++ b/lms/djangoapps/shoppingcart/tests/test_views.py @@ -1749,8 +1749,8 @@ class RegistrationCodeRedemptionCourseEnrollment(SharedModuleStoreTestCase): response = self.client.post(url) self.assertEquals(response.status_code, 403) - # now reset the time to 5 mins from now in future in order to unblock - reset_time = datetime.now(UTC) + timedelta(seconds=300) + # now reset the time to 6 mins from now in future in order to unblock + reset_time = datetime.now(UTC) + timedelta(seconds=361) with freeze_time(reset_time): response = self.client.post(url) self.assertEquals(response.status_code, 404) @@ -1773,8 +1773,8 @@ class RegistrationCodeRedemptionCourseEnrollment(SharedModuleStoreTestCase): response = self.client.get(url) self.assertEquals(response.status_code, 403) - # now reset the time to 5 mins from now in future in order to unblock - reset_time = datetime.now(UTC) + timedelta(seconds=300) + # now reset the time to 6 mins from now in future in order to unblock + reset_time = datetime.now(UTC) + timedelta(seconds=361) with freeze_time(reset_time): response = self.client.get(url) self.assertEquals(response.status_code, 404) diff --git a/lms/envs/common.py b/lms/envs/common.py index 85d003f063..b859c7d3dc 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -997,7 +997,7 @@ MEDIA_URL = '/media/' # Locale/Internationalization CELERY_TIMEZONE = 'UTC' -TIME_ZONE = 'America/New_York' # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name +TIME_ZONE = 'UTC' LANGUAGE_CODE = 'en' # http://www.i18nguy.com/unicode/language-identifiers.html # these languages display right to left LANGUAGES_BIDI = ("he", "ar", "fa", "ur", "fa-ir", "rtl") diff --git a/openedx/core/djangoapps/enrollments/serializers.py b/openedx/core/djangoapps/enrollments/serializers.py index 5e946527f9..2b7a3a6b55 100644 --- a/openedx/core/djangoapps/enrollments/serializers.py +++ b/openedx/core/djangoapps/enrollments/serializers.py @@ -45,6 +45,10 @@ class CourseSerializer(serializers.Serializer): # pylint: disable=abstract-meth invite_only = serializers.BooleanField(source="invitation_only") course_modes = serializers.SerializerMethodField() + class Meta(object): + # For disambiguating within the drf-yasg swagger schema + ref_name = 'enrollment.Course' + def __init__(self, *args, **kwargs): self.include_expired = kwargs.pop("include_expired", False) super(CourseSerializer, self).__init__(*args, **kwargs) diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_views.py b/openedx/core/djangoapps/user_api/accounts/tests/test_views.py index b4d2d7dd6d..6b24bd66b8 100644 --- a/openedx/core/djangoapps/user_api/accounts/tests/test_views.py +++ b/openedx/core/djangoapps/user_api/accounts/tests/test_views.py @@ -551,7 +551,7 @@ class TestAccountsAPI(CacheIsolationTestCase, UserAPITestCase): ("level_of_education", "none", u"ȻħȺɍłɇs", u'"ȻħȺɍłɇs" is not a valid choice.'), ("country", "GB", "XY", u'"XY" is not a valid choice.'), ("year_of_birth", 2009, "not_an_int", u"A valid integer is required."), - ("name", "bob", "z" * 256, u"Ensure this value has at most 255 characters (it has 256)."), + ("name", "bob", "z" * 256, u"Ensure this field has no more than 255 characters."), ("name", u"ȻħȺɍłɇs", " ", u"The name field must be at least 1 character long."), ("goals", "Smell the roses"), ("mailing_address", "Sesame Street"), diff --git a/openedx/core/djangoapps/user_api/preferences/tests/test_api.py b/openedx/core/djangoapps/user_api/preferences/tests/test_api.py index 092c05aee0..7f8f719994 100644 --- a/openedx/core/djangoapps/user_api/preferences/tests/test_api.py +++ b/openedx/core/djangoapps/user_api/preferences/tests/test_api.py @@ -470,7 +470,7 @@ def get_expected_validation_developer_message(preference_key, preference_value): preference_key=preference_key, preference_value=preference_value, error={ - "key": [u"Ensure this value has at most 255 characters (it has 256)."] + "key": [u"Ensure this field has no more than 255 characters."] } ) diff --git a/openedx/core/djangoapps/user_api/serializers.py b/openedx/core/djangoapps/user_api/serializers.py index ae515de0ae..76dcdb7fe5 100644 --- a/openedx/core/djangoapps/user_api/serializers.py +++ b/openedx/core/djangoapps/user_api/serializers.py @@ -34,6 +34,8 @@ class UserSerializer(serializers.HyperlinkedModelSerializer): # This list is the minimal set required by the notification service fields = ("id", "url", "email", "name", "username", "preferences") read_only_fields = ("id", "email", "username") + # For disambiguating within the drf-yasg swagger schema + ref_name = 'user_api.User' class UserPreferenceSerializer(serializers.HyperlinkedModelSerializer): diff --git a/openedx/features/content_type_gating/tests/test_models.py b/openedx/features/content_type_gating/tests/test_models.py index 59e3bc8c71..e7cb035d6b 100644 --- a/openedx/features/content_type_gating/tests/test_models.py +++ b/openedx/features/content_type_gating/tests/test_models.py @@ -207,7 +207,7 @@ class TestContentTypeGatingConfig(CacheIsolationTestCase): all_configs[CourseLocator('7-True', 'test_course', 'run-None')], { 'enabled': (True, Provenance.org), - 'enabled_as_of': (datetime(2018, 1, 1, 5, tzinfo=pytz.UTC), Provenance.run), + 'enabled_as_of': (datetime(2018, 1, 1, 0, tzinfo=pytz.UTC), Provenance.run), 'studio_override_enabled': (None, Provenance.default), } ) @@ -215,7 +215,7 @@ class TestContentTypeGatingConfig(CacheIsolationTestCase): all_configs[CourseLocator('7-True', 'test_course', 'run-False')], { 'enabled': (False, Provenance.run), - 'enabled_as_of': (datetime(2018, 1, 1, 5, tzinfo=pytz.UTC), Provenance.run), + 'enabled_as_of': (datetime(2018, 1, 1, 0, tzinfo=pytz.UTC), Provenance.run), 'studio_override_enabled': (None, Provenance.default), } ) @@ -223,7 +223,7 @@ class TestContentTypeGatingConfig(CacheIsolationTestCase): all_configs[CourseLocator('7-None', 'test_course', 'run-None')], { 'enabled': (True, Provenance.site), - 'enabled_as_of': (datetime(2018, 1, 1, 5, tzinfo=pytz.UTC), Provenance.run), + 'enabled_as_of': (datetime(2018, 1, 1, 0, tzinfo=pytz.UTC), Provenance.run), 'studio_override_enabled': (None, Provenance.default), } ) diff --git a/openedx/features/course_duration_limits/tests/test_models.py b/openedx/features/course_duration_limits/tests/test_models.py index fb5b22c083..1ec9898b0a 100644 --- a/openedx/features/course_duration_limits/tests/test_models.py +++ b/openedx/features/course_duration_limits/tests/test_models.py @@ -236,21 +236,21 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase): all_configs[CourseLocator('7-True', 'test_course', 'run-None')], { 'enabled': (True, Provenance.org), - 'enabled_as_of': (datetime(2018, 1, 1, 5, tzinfo=pytz.UTC), Provenance.run), + 'enabled_as_of': (datetime(2018, 1, 1, 0, tzinfo=pytz.UTC), Provenance.run), } ) self.assertEqual( all_configs[CourseLocator('7-True', 'test_course', 'run-False')], { 'enabled': (False, Provenance.run), - 'enabled_as_of': (datetime(2018, 1, 1, 5, tzinfo=pytz.UTC), Provenance.run), + 'enabled_as_of': (datetime(2018, 1, 1, 0, tzinfo=pytz.UTC), Provenance.run), } ) self.assertEqual( all_configs[CourseLocator('7-None', 'test_course', 'run-None')], { 'enabled': (True, Provenance.site), - 'enabled_as_of': (datetime(2018, 1, 1, 5, tzinfo=pytz.UTC), Provenance.run), + 'enabled_as_of': (datetime(2018, 1, 1, 0, tzinfo=pytz.UTC), Provenance.run), } )