refactor: ran pyupgrade on lms/djangoapps/course_api (#26735)

This commit is contained in:
Usama Sadiq
2021-03-09 12:54:42 +05:00
committed by GitHub
parent 7e275ec3ca
commit afa102e125
32 changed files with 205 additions and 217 deletions

View File

@@ -3,32 +3,30 @@ Course API
"""
import logging
from edx_django_utils.monitoring import function_trace
from edx_when.api import get_dates_for_course
import search
from django.conf import settings
from django.contrib.auth.models import AnonymousUser, User # lint-amnesty, pylint: disable=imported-auth-user
from django.urls import reverse
from edx_django_utils.monitoring import function_trace
from edx_when.api import get_dates_for_course
from opaque_keys.edx.django.models import CourseKeyField
from rest_framework.exceptions import PermissionDenied
import search
import six
from common.djangoapps.student.models import CourseAccessRole
from common.djangoapps.student.roles import GlobalStaff
from lms.djangoapps.courseware.access import has_access
from lms.djangoapps.courseware.courses import (
get_course_overview_with_access,
get_courses,
get_permission_for_course_about
)
from opaque_keys.edx.django.models import CourseKeyField # lint-amnesty, pylint: disable=wrong-import-order
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.lib.api.view_utils import LazySequence
from common.djangoapps.student.models import CourseAccessRole
from common.djangoapps.student.roles import GlobalStaff
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
from .permissions import can_view_courses_for_username
logger = logging.getLogger(__name__) # pylint: disable=invalid-name
@@ -100,7 +98,7 @@ def _filter_by_search(course_queryset, search_term):
return LazySequence(
(
course for course in course_queryset
if six.text_type(course.id) in search_courses_ids
if str(course.id) in search_courses_ids
),
est_len=len(course_queryset)
)
@@ -235,12 +233,12 @@ def get_due_dates(request, course_key, user):
store = modulestore()
due_dates = []
for (block_key, date_type), date in six.iteritems(dates):
for (block_key, date_type), date in dates.items():
if date_type == 'due':
try:
block_display_name = store.get_item(block_key).display_name
except ItemNotFoundError:
logger.exception('Failed to get block for due date item with key: {}'.format(block_key))
logger.exception(f'Failed to get block for due date item with key: {block_key}')
block_display_name = UNKNOWN_BLOCK_DISPLAY_NAME
# get url to the block in the course

View File

@@ -48,7 +48,7 @@ class BlockListGetForm(Form):
try:
return int(value)
except ValueError:
raise ValidationError("'{}' is not a valid depth value.".format(value)) # lint-amnesty, pylint: disable=raise-missing-from
raise ValidationError(f"'{value}' is not a valid depth value.") # lint-amnesty, pylint: disable=raise-missing-from
def clean_requested_fields(self):
"""
@@ -83,7 +83,7 @@ class BlockListGetForm(Form):
"""
Return cleaned data, including additional requested fields.
"""
cleaned_data = super(BlockListGetForm, self).clean() # lint-amnesty, pylint: disable=super-with-arguments
cleaned_data = super().clean()
# Add additional requested_fields that are specified as separate
# parameters, if they were requested.

View File

@@ -4,13 +4,15 @@ Encapsulates permissions checks for Course Blocks API
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
from opaque_keys.edx.keys import CourseKey
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.roles import CourseStaffRole
from lms.djangoapps.courseware.access import has_access
from lms.djangoapps.courseware.access_response import AccessResponse
from lms.djangoapps.courseware.access_utils import ACCESS_DENIED, ACCESS_GRANTED, check_public_access
from lms.djangoapps.courseware.exceptions import CourseRunNotFound
from lms.djangoapps.courseware.courses import get_course
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.roles import CourseStaffRole
from lms.djangoapps.courseware.exceptions import CourseRunNotFound
from openedx.core.djangoapps.content.course_overviews.models import \
CourseOverview # lint-amnesty, pylint: disable=unused-import
from xmodule.course_module import COURSE_VISIBILITY_PUBLIC

View File

@@ -3,7 +3,6 @@ Serializers for Course Blocks related return objects.
"""
import six
from django.conf import settings
from rest_framework import serializers
from rest_framework.reverse import reverse
@@ -12,13 +11,13 @@ from lms.djangoapps.course_blocks.transformers.visibility import VisibilityTrans
from .transformers.block_completion import BlockCompletionTransformer
from .transformers.block_counts import BlockCountsTransformer
from .transformers.extra_fields import ExtraFieldsTransformer
from .transformers.milestones import MilestonesAndSpecialExamsTransformer
from .transformers.navigation import BlockNavigationTransformer
from .transformers.student_view import StudentViewTransformer
from .transformers.extra_fields import ExtraFieldsTransformer
class SupportedFieldType(object):
class SupportedFieldType:
"""
Metadata about fields supported by different transformers
"""
@@ -141,16 +140,16 @@ class BlockSerializer(serializers.Serializer): # pylint: disable=abstract-metho
authorization_denial_message = block_structure.get_xblock_field(block_key, 'authorization_denial_message')
data = {
'id': six.text_type(block_key),
'block_id': six.text_type(block_key.block_id),
'id': str(block_key),
'block_id': str(block_key.block_id),
'lms_web_url': reverse(
'jump_to',
kwargs={'course_id': six.text_type(block_key.course_key), 'location': six.text_type(block_key)},
kwargs={'course_id': str(block_key.course_key), 'location': str(block_key)},
request=self.context['request'],
),
'student_view_url': reverse(
'render_xblock',
kwargs={'usage_key_string': six.text_type(block_key)},
kwargs={'usage_key_string': str(block_key)},
request=self.context['request'],
),
}
@@ -158,7 +157,7 @@ class BlockSerializer(serializers.Serializer): # pylint: disable=abstract-metho
if settings.FEATURES.get("ENABLE_LTI_PROVIDER") and 'lti_url' in self.context['requested_fields']:
data['lti_url'] = reverse(
'lti_provider_launch',
kwargs={'course_id': six.text_type(block_key.course_key), 'usage_id': six.text_type(block_key)},
kwargs={'course_id': str(block_key.course_key), 'usage_id': str(block_key)},
request=self.context['request'],
)
@@ -178,7 +177,7 @@ class BlockSerializer(serializers.Serializer): # pylint: disable=abstract-metho
if 'children' in self.context['requested_fields']:
children = block_structure.get_children(block_key)
if children:
data['children'] = [six.text_type(child) for child in children]
data['children'] = [str(child) for child in children]
if authorization_denial_reason and authorization_denial_message:
data['authorization_denial_reason'] = authorization_denial_reason
@@ -205,6 +204,6 @@ class BlockDictSerializer(serializers.Serializer): # pylint: disable=abstract-m
Serialize to a dictionary of blocks keyed by the block's usage_key.
"""
return {
six.text_type(block_key): BlockSerializer(block_key, context=self.context).data
str(block_key): BlockSerializer(block_key, context=self.context).data
for block_key in structure
}

View File

@@ -4,20 +4,19 @@ Tests for Blocks api.py
from itertools import product
from unittest.mock import patch
import ddt
import six
from django.test.client import RequestFactory
from edx_toggles.toggles.testutils import override_waffle_switch
from mock import patch
from common.djangoapps.student.tests.factories import UserFactory
from openedx.core.djangoapps.content.block_structure.api import clear_course_from_cache
from openedx.core.djangoapps.content.block_structure.config import STORAGE_BACKING_FOR_CACHE
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import SampleCourseFactory, check_mongo_calls
from xmodule.modulestore.tests.sample_courses import BlockInfo
from openedx.core.djangoapps.content.block_structure.api import clear_course_from_cache
from openedx.core.djangoapps.content.block_structure.config import STORAGE_BACKING_FOR_CACHE
from ..api import get_blocks
@@ -29,7 +28,7 @@ class TestGetBlocks(SharedModuleStoreTestCase):
@classmethod
def setUpClass(cls):
super(TestGetBlocks, cls).setUpClass()
super().setUpClass()
with cls.store.default_store(ModuleStoreEnum.Type.split):
cls.course = SampleCourseFactory.create()
@@ -39,22 +38,22 @@ class TestGetBlocks(SharedModuleStoreTestCase):
cls.store.update_item(cls.html_block, ModuleStoreEnum.UserID.test)
def setUp(self):
super(TestGetBlocks, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.user = UserFactory.create()
self.request = RequestFactory().get("/dummy")
self.request.user = self.user
def test_basic(self):
blocks = get_blocks(self.request, self.course.location, self.user)
assert blocks['root'] == six.text_type(self.course.location)
assert blocks['root'] == str(self.course.location)
# subtract for (1) the orphaned course About block and (2) the hidden Html block
assert len(blocks['blocks']) == (len(self.store.get_items(self.course.id)) - 2)
assert six.text_type(self.html_block.location) not in blocks['blocks']
assert str(self.html_block.location) not in blocks['blocks']
def test_no_user(self):
blocks = get_blocks(self.request, self.course.location)
assert six.text_type(self.html_block.location) in blocks['blocks']
assert str(self.html_block.location) in blocks['blocks']
def test_access_before_api_transformer_order(self):
"""
@@ -65,16 +64,16 @@ class TestGetBlocks(SharedModuleStoreTestCase):
vertical_block = self.store.get_item(self.course.id.make_usage_key('vertical', 'vertical_x1a'))
problem_block = self.store.get_item(self.course.id.make_usage_key('problem', 'problem_x1a_1'))
vertical_descendants = blocks['blocks'][six.text_type(vertical_block.location)]['descendants']
vertical_descendants = blocks['blocks'][str(vertical_block.location)]['descendants']
assert six.text_type(problem_block.location) in vertical_descendants
assert six.text_type(self.html_block.location) not in vertical_descendants
assert str(problem_block.location) in vertical_descendants
assert str(self.html_block.location) not in vertical_descendants
def test_sub_structure(self):
sequential_block = self.store.get_item(self.course.id.make_usage_key('sequential', 'sequential_y1'))
blocks = get_blocks(self.request, sequential_block.location, self.user)
assert blocks['root'] == six.text_type(sequential_block.location)
assert blocks['root'] == str(sequential_block.location)
assert len(blocks['blocks']) == 5
for block_type, block_name, is_inside_of_structure in (
@@ -85,9 +84,9 @@ class TestGetBlocks(SharedModuleStoreTestCase):
):
block = self.store.get_item(self.course.id.make_usage_key(block_type, block_name))
if is_inside_of_structure:
assert six.text_type(block.location) in blocks['blocks']
assert str(block.location) in blocks['blocks']
else:
assert six.text_type(block.location) not in blocks['blocks']
assert str(block.location) not in blocks['blocks']
def test_filtering_by_block_types(self):
sequential_block = self.store.get_item(self.course.id.make_usage_key('sequential', 'sequential_y1'))
@@ -96,7 +95,7 @@ class TestGetBlocks(SharedModuleStoreTestCase):
blocks = get_blocks(self.request, sequential_block.location, self.user, requested_fields=['type'])
assert len(blocks['blocks']) == 5
found_not_problem = False
for block in six.itervalues(blocks['blocks']):
for block in blocks['blocks'].values():
if block['type'] != 'problem':
found_not_problem = True
assert found_not_problem
@@ -105,7 +104,7 @@ class TestGetBlocks(SharedModuleStoreTestCase):
blocks = get_blocks(self.request, sequential_block.location, self.user,
block_types_filter=['problem'], requested_fields=['type'])
assert len(blocks['blocks']) == 3
for block in six.itervalues(blocks['blocks']):
for block in blocks['blocks'].values():
assert block['type'] == 'problem'
@@ -118,7 +117,7 @@ class TestGetBlocksMobileHack(SharedModuleStoreTestCase):
@classmethod
def setUpClass(cls):
super(TestGetBlocksMobileHack, cls).setUpClass()
super().setUpClass()
with cls.store.default_store(ModuleStoreEnum.Type.split):
cls.course = SampleCourseFactory.create(
block_info_tree=[
@@ -139,7 +138,7 @@ class TestGetBlocksMobileHack(SharedModuleStoreTestCase):
)
def setUp(self):
super(TestGetBlocksMobileHack, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.user = UserFactory.create()
self.request = RequestFactory().get("/dummy")
self.request.user = self.user
@@ -151,9 +150,9 @@ class TestGetBlocksMobileHack(SharedModuleStoreTestCase):
def test_empty_containers(self, is_mobile, container_type):
with patch('lms.djangoapps.course_api.blocks.api.is_request_from_mobile_app', return_value=is_mobile):
blocks = get_blocks(self.request, self.course.location)
full_container_key = self.course.id.make_usage_key(container_type, 'full_{}'.format(container_type))
full_container_key = self.course.id.make_usage_key(container_type, f'full_{container_type}')
assert str(full_container_key) in blocks['blocks']
empty_container_key = self.course.id.make_usage_key(container_type, 'empty_{}'.format(container_type))
empty_container_key = self.course.id.make_usage_key(container_type, f'empty_{container_type}')
assert_containment = self.assertNotIn if is_mobile else self.assertIn
assert_containment(str(empty_container_key), blocks['blocks'])
@@ -180,7 +179,7 @@ class TestGetBlocksMobileHack(SharedModuleStoreTestCase):
)
video_block_key = str(self.course.id.make_usage_key('video', 'sample_video'))
video_block_data = blocks['blocks'][video_block_key]
for video_data in six.itervalues(video_block_data['student_view_data']['encoded_videos']):
for video_data in video_block_data['student_view_data']['encoded_videos'].values():
assert 'cloudfront' not in video_data['url']
@@ -193,7 +192,7 @@ class TestGetBlocksQueryCountsBase(SharedModuleStoreTestCase):
ENABLED_SIGNALS = ['course_published']
def setUp(self):
super(TestGetBlocksQueryCountsBase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.user = UserFactory.create()
self.request = RequestFactory().get("/dummy")

View File

@@ -3,17 +3,17 @@ Tests for Course Blocks forms
"""
from urllib.parse import urlencode
import ddt
import pytest
import six
from six.moves.urllib.parse import urlencode
from django.http import Http404, QueryDict
from opaque_keys.edx.locator import CourseLocator
from rest_framework.exceptions import PermissionDenied
from openedx.core.djangoapps.util.test_forms import FormTestMixin
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
from openedx.core.djangoapps.util.test_forms import FormTestMixin
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
@@ -30,12 +30,12 @@ class TestBlockListGetForm(FormTestMixin, SharedModuleStoreTestCase):
@classmethod
def setUpClass(cls):
super(TestBlockListGetForm, cls).setUpClass()
super().setUpClass()
cls.course = CourseFactory.create()
def setUp(self):
super(TestBlockListGetForm, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.student = UserFactory.create()
self.student2 = UserFactory.create()
@@ -49,7 +49,7 @@ class TestBlockListGetForm(FormTestMixin, SharedModuleStoreTestCase):
self.form_data = QueryDict(
urlencode({
'username': self.student.username,
'usage_key': six.text_type(usage_key),
'usage_key': str(usage_key),
}),
mutable=True,
)

View File

@@ -3,13 +3,12 @@ Tests for Course Blocks serializers
"""
import six
from mock import MagicMock
from unittest.mock import MagicMock
from lms.djangoapps.course_blocks.api import get_course_block_access_transformers, get_course_blocks
from openedx.core.djangoapps.content.block_structure.transformers import BlockStructureTransformers
from common.djangoapps.student.roles import CourseStaffRole
from common.djangoapps.student.tests.factories import UserFactory
from lms.djangoapps.course_blocks.api import get_course_block_access_transformers, get_course_blocks
from openedx.core.djangoapps.content.block_structure.transformers import BlockStructureTransformers
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import ToyCourseFactory
@@ -26,7 +25,7 @@ class TestBlockSerializerBase(SharedModuleStoreTestCase):
@classmethod
def setUpClass(cls):
super(TestBlockSerializerBase, cls).setUpClass()
super().setUpClass()
cls.course = ToyCourseFactory.create()
@@ -37,7 +36,7 @@ class TestBlockSerializerBase(SharedModuleStoreTestCase):
cls.store.update_item(cls.html_block, ModuleStoreEnum.UserID.test)
def setUp(self):
super(TestBlockSerializerBase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.user = UserFactory.create()
@@ -65,7 +64,7 @@ class TestBlockSerializerBase(SharedModuleStoreTestCase):
"""
block_key = deserialize_usage_key(block_key_string, self.course.id)
assert self.block_structure.get_xblock_field(block_key, 'category') == serialized_block['type']
assert set(six.iterkeys(serialized_block)) == {'id', 'block_id', 'type', 'lms_web_url', 'student_view_url'}
assert set(serialized_block.keys()) == {'id', 'block_id', 'type', 'lms_web_url', 'student_view_url'}
def add_additional_requested_fields(self, context=None):
"""
@@ -90,7 +89,7 @@ class TestBlockSerializerBase(SharedModuleStoreTestCase):
Verifies the given serialized_block when additional fields are requested.
"""
assert {'id', 'type', 'lms_web_url', 'student_view_url', 'display_name', 'graded', 'student_view_multi_device',
'lti_url', 'visible_to_staff_only'} <= set(six.iterkeys(serialized_block))
'lti_url', 'visible_to_staff_only'} <= set(serialized_block.keys())
# video blocks should have student_view_data
if serialized_block['type'] == 'video':
@@ -131,7 +130,7 @@ class TestBlockSerializerBase(SharedModuleStoreTestCase):
"""
Test fields accessed by a staff user
"""
if serialized_block['id'] == six.text_type(self.html_block.location):
if serialized_block['id'] == str(self.html_block.location):
assert serialized_block['visible_to_staff_only']
else:
assert not serialized_block['visible_to_staff_only']
@@ -197,10 +196,10 @@ class TestBlockDictSerializer(TestBlockSerializerBase):
serializer = self.create_serializer()
# verify root
assert serializer.data['root'] == six.text_type(self.block_structure.root_block_usage_key)
assert serializer.data['root'] == str(self.block_structure.root_block_usage_key)
# verify blocks
for block_key_string, serialized_block in six.iteritems(serializer.data['blocks']):
for block_key_string, serialized_block in serializer.data['blocks'].items():
assert serialized_block['id'] == block_key_string
self.assert_basic_block(block_key_string, serialized_block)
assert len(serializer.data['blocks']) == 28
@@ -208,7 +207,7 @@ class TestBlockDictSerializer(TestBlockSerializerBase):
def test_additional_requested_fields(self):
self.add_additional_requested_fields()
serializer = self.create_serializer()
for serialized_block in six.itervalues(serializer.data['blocks']):
for serialized_block in serializer.data['blocks'].values():
self.assert_extended_block(serialized_block)
assert len(serializer.data['blocks']) == 28
@@ -219,7 +218,7 @@ class TestBlockDictSerializer(TestBlockSerializerBase):
context = self.create_staff_context()
self.add_additional_requested_fields(context)
serializer = self.create_serializer(context)
for serialized_block in six.itervalues(serializer.data['blocks']):
for serialized_block in serializer.data['blocks'].values():
self.assert_extended_block(serialized_block)
self.assert_staff_fields(serialized_block)
assert len(serializer.data['blocks']) == 29

View File

@@ -8,10 +8,10 @@ from unittest import mock
from unittest.mock import Mock
from urllib.parse import urlencode, urlunparse
from completion.test_utils import CompletionWaffleTestMixin, submit_completions_for_testing
from django.conf import settings
from django.urls import reverse
from opaque_keys.edx.locator import CourseLocator
from completion.test_utils import CompletionWaffleTestMixin, submit_completions_for_testing
from common.djangoapps.student.models import CourseEnrollment
from common.djangoapps.student.tests.factories import AdminFactory, CourseEnrollmentFactory, UserFactory
@@ -30,7 +30,7 @@ class TestBlocksView(SharedModuleStoreTestCase):
@classmethod
def setUpClass(cls):
super(TestBlocksView, cls).setUpClass()
super().setUpClass()
# create a toy course
cls.course = ToyCourseFactory.create(
@@ -40,15 +40,15 @@ class TestBlocksView(SharedModuleStoreTestCase):
cls.course_key = cls.course.id
cls.course_usage_key = cls.store.make_course_usage_key(cls.course_key)
cls.non_orphaned_block_usage_keys = set(
cls.non_orphaned_block_usage_keys = {
str(item.location)
for item in cls.store.get_items(cls.course_key)
# remove all orphaned items in the course, except for the root 'course' block
if cls.store.get_parent_location(item.location) or item.category == 'course'
)
}
def setUp(self):
super(TestBlocksView, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# create and enroll user in the toy course
self.user = UserFactory.create()
@@ -126,7 +126,7 @@ class TestBlocksView(SharedModuleStoreTestCase):
if xblock.has_children:
self.assertSetEqual(
set(str(child.location) for child in xblock.get_children()),
{str(child.location) for child in xblock.get_children()},
set(block_data['children']),
)
@@ -390,16 +390,16 @@ class TestBlocksInCourseView(TestBlocksView, CompletionWaffleTestMixin): # pyli
"""
def setUp(self):
super(TestBlocksInCourseView, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.url = reverse('blocks_in_course')
self.query_params['course_id'] = str(self.course_key)
self.override_waffle_switch(True)
self.non_orphaned_raw_block_usage_keys = set(
self.non_orphaned_raw_block_usage_keys = {
item.location
for item in self.store.get_items(self.course_key)
# remove all orphaned items in the course, except for the root 'course' block
if self.store.get_parent_location(item.location) or item.category == 'course'
)
}
def test_no_course_id(self):
self.query_params.pop('course_id')

View File

@@ -5,7 +5,7 @@ Toggles for Course API.
from edx_toggles.toggles import LegacyWaffleFlag, LegacyWaffleFlagNamespace
COURSE_BLOCKS_API_NAMESPACE = LegacyWaffleFlagNamespace(name=u'course_blocks_api')
COURSE_BLOCKS_API_NAMESPACE = LegacyWaffleFlagNamespace(name='course_blocks_api')
# .. toggle_name: course_blocks_api.hide_access_denials
# .. toggle_implementation: WaffleFlag
@@ -18,6 +18,6 @@ COURSE_BLOCKS_API_NAMESPACE = LegacyWaffleFlagNamespace(name=u'course_blocks_api
# .. toggle_tickets: None
HIDE_ACCESS_DENIALS_FLAG = LegacyWaffleFlag(
waffle_namespace=COURSE_BLOCKS_API_NAMESPACE,
flag_name=u'hide_access_denials',
flag_name='hide_access_denials',
module_name=__name__,
)

View File

@@ -7,10 +7,10 @@ from openedx.core.djangoapps.content.block_structure.transformer import BlockStr
from .block_counts import BlockCountsTransformer
from .block_depth import BlockDepthTransformer
from .extra_fields import ExtraFieldsTransformer
from .navigation import BlockNavigationTransformer
from .student_view import StudentViewTransformer
from .video_urls import VideoBlockURLTransformer
from .extra_fields import ExtraFieldsTransformer
class BlocksAPITransformer(BlockStructureTransformer):

View File

@@ -5,14 +5,13 @@ Milestones Transformer
import logging
import six
from django.conf import settings
from edx_proctoring.api import get_attempt_status_summary
from edx_proctoring.exceptions import ProctoredExamNotFoundException
from openedx.core.djangoapps.content.block_structure.transformer import BlockStructureTransformer
from common.djangoapps.student.models import EntranceExamConfiguration
from common.djangoapps.util import milestones_helpers
from openedx.core.djangoapps.content.block_structure.transformer import BlockStructureTransformer
log = logging.getLogger(__name__)
@@ -102,8 +101,8 @@ class MilestonesAndSpecialExamsTransformer(BlockStructureTransformer):
them from accessing this block.
"""
return bool(milestones_helpers.get_course_content_milestones(
six.text_type(block_key.course_key),
six.text_type(block_key),
str(block_key.course_key),
str(block_key),
'requires',
usage_info.user.id
))
@@ -120,8 +119,8 @@ class MilestonesAndSpecialExamsTransformer(BlockStructureTransformer):
# This will return None, if (user, course_id, content_id) is not applicable.
special_exam_attempt_context = get_attempt_status_summary(
usage_info.user.id,
six.text_type(block_key.course_key),
six.text_type(block_key)
str(block_key.course_key),
str(block_key)
)
except ProctoredExamNotFoundException as ex:
log.exception(ex)
@@ -169,7 +168,7 @@ class MilestonesAndSpecialExamsTransformer(BlockStructureTransformer):
if not required_content:
return False
if block_key.block_type == 'chapter' and six.text_type(block_key) not in required_content:
if block_key.block_type == 'chapter' and str(block_key) not in required_content:
return True
return False

View File

@@ -3,14 +3,12 @@ TODO
"""
import six
from openedx.core.djangoapps.content.block_structure.transformer import BlockStructureTransformer
from .block_depth import BlockDepthTransformer
class DescendantList(object):
class DescendantList:
"""
Contain
"""
@@ -76,7 +74,7 @@ class BlockNavigationTransformer(BlockStructureTransformer):
# add self to parent's descendants
for parent_desc_list in parents_descendants_list:
if parent_desc_list is not None:
parent_desc_list.items.append(six.text_type(block_key))
parent_desc_list.items.append(str(block_key))
if BlockDepthTransformer.get_block_depth(block_structure, block_key) > self.nav_depth:
children_descendants_list = parents_descendants_list

View File

@@ -8,10 +8,10 @@ from completion.test_utils import CompletionWaffleTestMixin
from xblock.completable import CompletableXBlockMixin, XBlockCompletionMode
from xblock.core import XBlock
from common.djangoapps.student.tests.factories import UserFactory
from lms.djangoapps.course_api.blocks.transformers.block_completion import BlockCompletionTransformer
from lms.djangoapps.course_blocks.api import get_course_blocks
from lms.djangoapps.course_blocks.transformers.tests.helpers import ModuleStoreTestCase, TransformerRegistryTestMixin
from common.djangoapps.student.tests.factories import UserFactory
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
@@ -47,7 +47,7 @@ class BlockCompletionTransformerTestCase(TransformerRegistryTestMixin, Completio
COMPLETION_TEST_VALUE = 0.4
def setUp(self):
super(BlockCompletionTransformerTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.user = UserFactory.create(password='test')
# Set ENABLE_COMPLETION_TRACKING waffle switch to True
self.override_waffle_switch(True)

View File

@@ -18,7 +18,7 @@ class TestBlockCountsTransformer(ModuleStoreTestCase):
"""
def setUp(self):
super(TestBlockCountsTransformer, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course_key = SampleCourseFactory.create().id
self.course_usage_key = self.store.make_course_usage_key(self.course_key)
self.block_structure = BlockStructureFactory.create_from_modulestore(self.course_usage_key, self.store)

View File

@@ -30,7 +30,7 @@ class TestExtraFieldsTransformer(ModuleStoreTestCase):
}
def setUp(self):
super(TestExtraFieldsTransformer, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course = SampleCourseFactory.create(
other_course_settings=self.OTHER_COURSE_SETTINGS_DEFAULT

View File

@@ -3,17 +3,17 @@ Tests for ProctoredExamTransformer.
"""
import ddt
import six
from milestones.tests.utils import MilestonesTestCaseMixin
from mock import Mock, patch
from unittest.mock import Mock, patch
from lms.djangoapps.gating import api as lms_gating_api
import ddt
from milestones.tests.utils import MilestonesTestCaseMixin
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory
from lms.djangoapps.course_blocks.api import get_course_blocks
from lms.djangoapps.course_blocks.transformers.tests.helpers import CourseStructureTestCase
from lms.djangoapps.gating import api as lms_gating_api
from openedx.core.djangoapps.content.block_structure.transformers import BlockStructureTransformers
from openedx.core.lib.gating import api as gating_api
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory
from ..milestones import MilestonesAndSpecialExamsTransformer
@@ -30,7 +30,7 @@ class MilestonesTransformerTestCase(CourseStructureTestCase, MilestonesTestCaseM
"""
Setup course structure and create user for split test transformer test.
"""
super(MilestonesTransformerTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# Build course.
self.course_hierarchy = self.get_course_hierarchy()
@@ -49,7 +49,7 @@ class MilestonesTransformerTestCase(CourseStructureTestCase, MilestonesTestCaseM
gated_block: The block that should be inaccessible until gating_block is completed
gating_block: The block that must be completed before access is granted
"""
gating_api.add_prerequisite(self.course.id, six.text_type(gating_block.location))
gating_api.add_prerequisite(self.course.id, str(gating_block.location))
gating_api.set_required_content(self.course.id, gated_block.location, gating_block.location, 100, 0)
ALL_BLOCKS = (

View File

@@ -7,7 +7,6 @@ Tests for BlockNavigationTransformer.
from unittest import TestCase
import ddt
import six
from lms.djangoapps.course_api.blocks.transformers.block_depth import BlockDepthTransformer
from lms.djangoapps.course_api.blocks.transformers.navigation import BlockNavigationTransformer
@@ -57,7 +56,7 @@ class BlockNavigationTransformerTestCase(TestCase, ChildrenMapTestMixin):
for block_key, expected_nav in enumerate(expected_nav_map):
self.assertSetEqual(
set(six.text_type(block) for block in expected_nav),
{str(block) for block in expected_nav},
set(
block_structure.get_transformer_block_field(
block_key,
@@ -114,7 +113,7 @@ class BlockNavigationTransformerCourseTestCase(ModuleStoreTestCase):
course_key.make_usage_key('vertical', 'vertical_y1a'),
course_key.make_usage_key('problem', 'problem_y1a_1'),
]:
assert six.text_type(block_key) in course_descendants
assert str(block_key) in course_descendants
# chapter_x and its descendants should not be included
for block_key in [
@@ -123,4 +122,4 @@ class BlockNavigationTransformerCourseTestCase(ModuleStoreTestCase):
course_key.make_usage_key('vertical', 'vertical_x1a'),
course_key.make_usage_key('problem', 'problem_x1a_1'),
]:
assert six.text_type(block_key) not in course_descendants
assert str(block_key) not in course_descendants

View File

@@ -20,7 +20,7 @@ class TestStudentViewTransformer(ModuleStoreTestCase):
"""
def setUp(self):
super(TestStudentViewTransformer, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course_key = ToyCourseFactory.create().id
self.course_usage_key = self.store.make_course_usage_key(self.course_key)
self.block_structure = BlockStructureFactory.create_from_modulestore(self.course_usage_key, self.store)

View File

@@ -3,8 +3,7 @@ Tests for VideoBlockURLTransformer.
"""
import mock
import six
from unittest import mock
from openedx.core.djangoapps.content.block_structure.factory import BlockStructureFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
@@ -20,7 +19,7 @@ class TestVideoBlockURLTransformer(ModuleStoreTestCase):
"""
def setUp(self):
super(TestVideoBlockURLTransformer, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course_key = ToyCourseFactory.create().id
self.course_usage_key = self.store.make_course_usage_key(self.course_key)
self.block_structure = BlockStructureFactory.create_from_modulestore(self.course_usage_key, self.store)
@@ -37,7 +36,7 @@ class TestVideoBlockURLTransformer(ModuleStoreTestCase):
Relocate url data in new dictionary for pre & post transformation data comparison.
"""
video_urls = {}
for video_format, video_data in six.iteritems(encoded_videos):
for video_format, video_data in encoded_videos.items():
video_urls[video_format] = video_data['url']
return video_urls
@@ -91,7 +90,7 @@ class TestVideoBlockURLTransformer(ModuleStoreTestCase):
post_transform_data = self.get_post_transform_data(video_block_key)
post_transform_data = self.change_encoded_videos_presentation(post_transform_data['encoded_videos'])
for video_format, video_url in six.iteritems(post_transform_data):
for video_format, video_url in post_transform_data.items():
assert pre_transform_data[video_format] != video_url
@mock.patch('xmodule.video_module.VideoBlock.student_view_data')
@@ -121,7 +120,7 @@ class TestVideoBlockURLTransformer(ModuleStoreTestCase):
post_transform_data = self.get_post_transform_data(video_block_key)
post_transform_data = self.change_encoded_videos_presentation(post_transform_data['encoded_videos'])
for video_format, video_url in six.iteritems(post_transform_data):
for video_format, video_url in post_transform_data.items():
assert pre_transform_data[video_format] == video_url
@mock.patch('xmodule.video_module.VideoBlock.student_view_data')

View File

@@ -3,11 +3,10 @@ Video block URL Transformer
"""
import six
from django.conf import settings
from xmodule.video_module.video_utils import rewrite_video_url
from openedx.core.djangoapps.content.block_structure.transformer import BlockStructureTransformer
from xmodule.video_module.video_utils import rewrite_video_url
from .student_view import StudentViewTransformer
@@ -52,7 +51,7 @@ class VideoBlockURLTransformer(BlockStructureTransformer):
if only_on_web:
continue
encoded_videos = student_view_data.get('encoded_videos')
for video_format, video_data in six.iteritems(encoded_videos):
for video_format, video_data in encoded_videos.items():
if video_format in self.VIDEO_FORMAT_EXCEPTIONS:
continue
video_data['url'] = rewrite_video_url(self.CDN_URL, video_data['url'])

View File

@@ -11,7 +11,7 @@ from .views import BlocksInCourseView, BlocksView
urlpatterns = [
# This endpoint requires the usage_key for the starting block.
url(
r'^v1/blocks/{}'.format(settings.USAGE_KEY_PATTERN),
fr'^v1/blocks/{settings.USAGE_KEY_PATTERN}',
BlocksView.as_view(),
kwargs={'hide_access_denials': True},
name="blocks_in_block_tree"
@@ -26,7 +26,7 @@ urlpatterns = [
),
# This endpoint requires the usage_key for the starting block.
url(
r'^v2/blocks/{}'.format(settings.USAGE_KEY_PATTERN),
fr'^v2/blocks/{settings.USAGE_KEY_PATTERN}',
BlocksView.as_view(),
name="blocks_in_block_tree"
),

View File

@@ -3,7 +3,6 @@ CourseBlocks API views
"""
import six
from django.core.exceptions import ValidationError
from django.db import transaction
from django.http import Http404
@@ -13,7 +12,6 @@ from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
from rest_framework.generics import ListAPIView
from rest_framework.response import Response
from six import text_type
from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin, view_auth_classes
from xmodule.modulestore.django import modulestore
@@ -238,7 +236,7 @@ class BlocksView(DeveloperErrorViewMixin, ListAPIView):
patch_response_headers(response)
return response
except ItemNotFoundError as exception:
raise Http404(u"Block not found: {}".format(text_type(exception))) # lint-amnesty, pylint: disable=raise-missing-from
raise Http404("Block not found: {}".format(str(exception))) # lint-amnesty, pylint: disable=raise-missing-from
@view_auth_classes(is_authenticated=False)
@@ -301,9 +299,9 @@ class BlocksInCourseView(BlocksView):
course_key = CourseKey.from_string(course_key_string)
course_usage_key = modulestore().make_course_usage_key(course_key)
except InvalidKeyError:
raise ValidationError(u"'{}' is not a valid course key.".format(six.text_type(course_key_string))) # lint-amnesty, pylint: disable=raise-missing-from
raise ValidationError("'{}' is not a valid course key.".format(str(course_key_string))) # lint-amnesty, pylint: disable=raise-missing-from
response = super().list(request, course_usage_key,
hide_access_denials=hide_access_denials) # lint-amnesty, pylint: disable=super-with-arguments
hide_access_denials=hide_access_denials)
calculate_completion = any('completion' in param
for param in request.query_params.getlist('requested_fields', []))
@@ -323,7 +321,7 @@ class BlocksInCourseView(BlocksView):
course_blocks = response.data['blocks']
if not root:
raise ValueError("Unable to find course block in {}".format(course_key_string))
raise ValueError(f"Unable to find course block in {course_key_string}")
recurse_mark_complete(root, course_blocks)
return response

View File

@@ -5,7 +5,6 @@ Course API forms
from collections import namedtuple
import six
from django.core.exceptions import ValidationError
from django.forms import CharField, Form
from opaque_keys import InvalidKeyError
@@ -14,7 +13,7 @@ from opaque_keys.edx.keys import CourseKey
from openedx.core.djangoapps.util.forms import ExtendedNullBooleanField
class UsernameValidatorMixin(object):
class UsernameValidatorMixin:
"""
Mixin class for validating the username parameter.
"""
@@ -43,7 +42,7 @@ class CourseDetailGetForm(UsernameValidatorMixin, Form):
try:
return CourseKey.from_string(course_key_string)
except InvalidKeyError:
raise ValidationError(u"'{}' is not a valid course key.".format(six.text_type(course_key_string))) # lint-amnesty, pylint: disable=raise-missing-from
raise ValidationError("'{}' is not a valid course key.".format(str(course_key_string))) # lint-amnesty, pylint: disable=raise-missing-from
class CourseListGetForm(UsernameValidatorMixin, Form):
@@ -65,7 +64,7 @@ class CourseListGetForm(UsernameValidatorMixin, Form):
"""
Return cleaned data, including additional filters.
"""
cleaned_data = super(CourseListGetForm, self).clean() # lint-amnesty, pylint: disable=super-with-arguments
cleaned_data = super().clean()
# create a filter for all supported filter fields
filter_ = dict()

View File

@@ -3,11 +3,14 @@ Course API Serializers. Representing course catalog data
"""
from edx_django_utils import monitoring as monitoring_utils
import six.moves.urllib.parse # lint-amnesty, pylint: disable=wrong-import-order
import urllib
from django.urls import reverse
from edx_django_utils import monitoring as monitoring_utils
from rest_framework import serializers
from openedx.core.djangoapps.content.course_overviews.models import \
CourseOverview # lint-amnesty, pylint: disable=unused-import
from openedx.core.djangoapps.models.course_details import CourseDetails
from openedx.core.lib.api.fields import AbsoluteURLField
@@ -18,7 +21,7 @@ class _MediaSerializer(serializers.Serializer): # pylint: disable=abstract-meth
"""
def __init__(self, uri_attribute, *args, **kwargs):
super(_MediaSerializer, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
super().__init__(*args, **kwargs)
self.uri_attribute = uri_attribute
uri = serializers.SerializerMethodField(source='*')
@@ -128,7 +131,7 @@ class CourseSerializer(serializers.Serializer): # pylint: disable=abstract-meth
"""
base_url = '?'.join([
reverse('blocks_in_course'),
six.moves.urllib.parse.urlencode({'course_id': course_overview.id}),
urllib.parse.urlencode({'course_id': course_overview.id}),
])
return self.context['request'].build_absolute_uri(base_url)

View File

@@ -8,10 +8,10 @@ from datetime import datetime
from common.djangoapps.student.tests.factories import UserFactory
from xmodule.modulestore.tests.factories import ToyCourseFactory
TEST_PASSWORD = u'edx'
TEST_PASSWORD = 'edx'
class CourseApiFactoryMixin(object):
class CourseApiFactoryMixin:
"""
Mixin to allow creation of test courses and users.
"""
@@ -37,7 +37,7 @@ class CourseApiFactoryMixin(object):
"""
return UserFactory(
username=username,
email=u'{}@example.com'.format(username),
email=f'{username}@example.com',
password=TEST_PASSWORD,
is_staff=is_staff
)

View File

@@ -4,11 +4,11 @@ Test for course API
from datetime import datetime, timedelta
from hashlib import md5
from unittest import mock
import pytest
from django.contrib.auth.models import AnonymousUser
from django.http import Http404
import mock
from opaque_keys.edx.keys import CourseKey
from rest_framework.exceptions import PermissionDenied
from rest_framework.request import Request
@@ -30,11 +30,11 @@ class CourseApiTestMixin(CourseApiFactoryMixin):
@classmethod
def setUpClass(cls):
super(CourseApiTestMixin, cls).setUpClass()
super().setUpClass()
cls.request_factory = APIRequestFactory()
CourseOverview.get_all_courses() # seed the CourseOverview table
def verify_course(self, course, course_id=u'edX/toy/2012_Fall'):
def verify_course(self, course, course_id='edX/toy/2012_Fall'):
"""
Ensure that the returned course is the course we just created
"""
@@ -65,9 +65,9 @@ class TestGetCourseDetail(CourseDetailTestMixin, SharedModuleStoreTestCase):
@classmethod
def setUpClass(cls):
super(TestGetCourseDetail, cls).setUpClass()
super().setUpClass()
cls.course = cls.create_course()
cls.hidden_course = cls.create_course(course=u'hidden', visible_to_staff_only=True)
cls.hidden_course = cls.create_course(course='hidden', visible_to_staff_only=True)
cls.honor_user = cls.create_user('honor', is_staff=False)
cls.staff_user = cls.create_user('staff', is_staff=True)
@@ -76,7 +76,7 @@ class TestGetCourseDetail(CourseDetailTestMixin, SharedModuleStoreTestCase):
self.verify_course(course)
def test_get_nonexistent_course(self):
course_key = CourseKey.from_string(u'edX/toy/nope')
course_key = CourseKey.from_string('edX/toy/nope')
with pytest.raises(Http404):
self._make_api_call(self.honor_user, self.honor_user, course_key)
@@ -86,7 +86,7 @@ class TestGetCourseDetail(CourseDetailTestMixin, SharedModuleStoreTestCase):
def test_hidden_course_for_staff(self):
course = self._make_api_call(self.staff_user, self.staff_user, self.hidden_course.id)
self.verify_course(course, course_id=u'edX/hidden/2012_Fall')
self.verify_course(course, course_id='edX/hidden/2012_Fall')
def test_hidden_course_for_staff_as_honor(self):
with pytest.raises(Http404):
@@ -124,7 +124,7 @@ class TestGetCourseList(CourseListTestMixin, SharedModuleStoreTestCase):
@classmethod
def setUpClass(cls):
super(TestGetCourseList, cls).setUpClass()
super().setUpClass()
cls.course = cls.create_course()
cls.staff_user = cls.create_user("staff", is_staff=True)
cls.honor_user = cls.create_user("honor", is_staff=False)
@@ -165,7 +165,7 @@ class TestGetCourseListMultipleCourses(CourseListTestMixin, ModuleStoreTestCase)
ENABLED_SIGNALS = ['course_published']
def setUp(self):
super(TestGetCourseListMultipleCourses, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course = self.create_course(mobile_available=False)
self.staff_user = self.create_user("staff", is_staff=True)
self.honor_user = self.create_user("honor", is_staff=False)
@@ -217,7 +217,7 @@ class TestGetCourseListExtras(CourseListTestMixin, ModuleStoreTestCase):
@classmethod
def setUpClass(cls):
super(TestGetCourseListExtras, cls).setUpClass()
super().setUpClass()
cls.staff_user = cls.create_user("staff", is_staff=True)
cls.honor_user = cls.create_user("honor", is_staff=False)

View File

@@ -3,22 +3,21 @@ Tests for Course API forms.
"""
from itertools import product
from urllib.parse import urlencode
import ddt
import six
from six.moves.urllib.parse import urlencode
from django.contrib.auth.models import AnonymousUser
from django.http import QueryDict
from openedx.core.djangoapps.util.test_forms import FormTestMixin
from common.djangoapps.student.tests.factories import UserFactory
from openedx.core.djangoapps.util.test_forms import FormTestMixin
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
from ..forms import CourseDetailGetForm, CourseIdListGetForm, CourseListGetForm
class UsernameTestMixin(object):
class UsernameTestMixin:
"""
Tests the username Form field.
"""
@@ -43,12 +42,12 @@ class TestCourseListGetForm(FormTestMixin, UsernameTestMixin, SharedModuleStoreT
@classmethod
def setUpClass(cls):
super(TestCourseListGetForm, cls).setUpClass()
super().setUpClass()
cls.course = CourseFactory.create()
def setUp(self):
super(TestCourseListGetForm, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.student = UserFactory.create()
self.set_up_data(self.student)
@@ -105,12 +104,12 @@ class TestCourseIdListGetForm(FormTestMixin, UsernameTestMixin, SharedModuleStor
@classmethod
def setUpClass(cls):
super(TestCourseIdListGetForm, cls).setUpClass()
super().setUpClass()
cls.course = CourseFactory.create()
def setUp(self):
super(TestCourseIdListGetForm, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.student = UserFactory.create()
self.set_up_data(self.student)
@@ -144,12 +143,12 @@ class TestCourseDetailGetForm(FormTestMixin, UsernameTestMixin, SharedModuleStor
@classmethod
def setUpClass(cls):
super(TestCourseDetailGetForm, cls).setUpClass()
super().setUpClass()
cls.course = CourseFactory.create()
def setUp(self):
super(TestCourseDetailGetForm, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.student = UserFactory.create()
self.set_up_data(self.student)
@@ -162,7 +161,7 @@ class TestCourseDetailGetForm(FormTestMixin, UsernameTestMixin, SharedModuleStor
self.form_data = QueryDict(
urlencode({
'username': user.username,
'course_key': six.text_type(self.course.id),
'course_key': str(self.course.id),
}),
mutable=True,
)

View File

@@ -20,7 +20,7 @@ class ViewCoursesForUsernameTestCase(CourseApiFactoryMixin, TestCase):
@classmethod
def setUpClass(cls):
super(ViewCoursesForUsernameTestCase, cls).setUpClass()
super().setUpClass()
cls.staff_user = cls.create_user('staff', is_staff=True)
cls.honor_user = cls.create_user('honor', is_staff=False)
cls.anonymous_user = AnonymousUser()

View File

@@ -7,10 +7,10 @@ from datetime import datetime
from unittest import TestCase
import ddt
from opaque_keys.edx.locator import CourseLocator
from rest_framework.request import Request
from rest_framework.test import APIRequestFactory
from xblock.core import XBlock
from opaque_keys.edx.locator import CourseLocator
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.models.course_details import CourseDetails
@@ -34,22 +34,22 @@ class TestCourseSerializer(CourseApiFactoryMixin, ModuleStoreTestCase):
ENABLED_SIGNALS = ['course_published']
def setUp(self):
super(TestCourseSerializer, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.staff_user = self.create_user('staff', is_staff=True)
self.honor_user = self.create_user('honor', is_staff=False)
self.request_factory = APIRequestFactory()
course_id = u'edX/toy/2012_Fall'
banner_image_uri = u'/c4x/edX/toy/asset/images_course_image.jpg'
banner_image_absolute_uri = u'http://testserver' + banner_image_uri
image_path = u'/c4x/edX/toy/asset/just_a_test.jpg'
image_url = u'http://testserver' + image_path
course_id = 'edX/toy/2012_Fall'
banner_image_uri = '/c4x/edX/toy/asset/images_course_image.jpg'
banner_image_absolute_uri = 'http://testserver' + banner_image_uri
image_path = '/c4x/edX/toy/asset/just_a_test.jpg'
image_url = 'http://testserver' + image_path
self.expected_data = {
'id': course_id,
'name': u'Toy Course',
'number': u'toy',
'org': u'edX',
'short_description': u'A course about toys.',
'name': 'Toy Course',
'number': 'toy',
'org': 'edX',
'short_description': 'A course about toys.',
'media': {
'banner_image': {
'uri': banner_image_uri,
@@ -59,7 +59,7 @@ class TestCourseSerializer(CourseApiFactoryMixin, ModuleStoreTestCase):
'uri': image_path,
},
'course_video': {
'uri': u'http://www.youtube.com/watch?v=test_youtube_id',
'uri': 'http://www.youtube.com/watch?v=test_youtube_id',
},
'image': {
'raw': image_url,
@@ -67,14 +67,14 @@ class TestCourseSerializer(CourseApiFactoryMixin, ModuleStoreTestCase):
'large': image_url,
}
},
'start': u'2015-07-17T12:00:00Z',
'start_type': u'timestamp',
'start_display': u'July 17, 2015',
'end': u'2015-09-19T18:00:00Z',
'enrollment_start': u'2015-06-15T00:00:00Z',
'enrollment_end': u'2015-07-15T00:00:00Z',
'blocks_url': u'http://testserver/api/courses/v2/blocks/?course_id=edX%2Ftoy%2F2012_Fall',
'effort': u'6 hours',
'start': '2015-07-17T12:00:00Z',
'start_type': 'timestamp',
'start_display': 'July 17, 2015',
'end': '2015-09-19T18:00:00Z',
'enrollment_start': '2015-06-15T00:00:00Z',
'enrollment_end': '2015-07-15T00:00:00Z',
'blocks_url': 'http://testserver/api/courses/v2/blocks/?course_id=edX%2Ftoy%2F2012_Fall',
'effort': '6 hours',
'pacing': 'instructor',
'mobile_available': True,
'hidden': False,
@@ -110,29 +110,29 @@ class TestCourseSerializer(CourseApiFactoryMixin, ModuleStoreTestCase):
def test_hidden(self):
course = self.create_course(
course=u'custom',
course='custom',
start=datetime(2015, 3, 15),
catalog_visibility=u'none'
catalog_visibility='none'
)
result = self._get_result(course)
assert result['hidden'] is True
def test_advertised_start(self):
course = self.create_course(
course=u'custom',
course='custom',
start=datetime(2015, 3, 15),
advertised_start=u'The Ides of March'
advertised_start='The Ides of March'
)
result = self._get_result(course)
assert result['course_id'] == u'edX/custom/2012_Fall'
assert result['start_type'] == u'string'
assert result['start_display'] == u'The Ides of March'
assert result['course_id'] == 'edX/custom/2012_Fall'
assert result['start_type'] == 'string'
assert result['start_display'] == 'The Ides of March'
def test_empty_start(self):
course = self.create_course(start=DEFAULT_START_DATE, course=u'custom')
course = self.create_course(start=DEFAULT_START_DATE, course='custom')
result = self._get_result(course)
assert result['course_id'] == u'edX/custom/2012_Fall'
assert result['start_type'] == u'empty'
assert result['course_id'] == 'edX/custom/2012_Fall'
assert result['start_type'] == 'empty'
assert result['start_display'] is None
@ddt.unpack
@@ -158,7 +158,7 @@ class TestCourseDetailSerializer(TestCourseSerializer): # lint-amnesty, pylint:
serializer_class = CourseDetailSerializer
def setUp(self):
super(TestCourseDetailSerializer, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# update the expected_data to include the 'overview' data.
about_descriptor = XBlock.load_class('about')

View File

@@ -7,13 +7,12 @@ from hashlib import md5
from unittest import TestCase
import pytest
import ddt
import six
from six.moves import range
from django.core.exceptions import ImproperlyConfigured
from django.test import RequestFactory
from django.test.utils import override_settings
from django.urls import reverse
from edx_django_utils.cache import RequestCache
from opaque_keys.edx.locator import LibraryLocator
from search.tests.test_course_discovery import DemoCourse
from search.tests.tests import TEST_INDEX_NAME
from search.tests.utils import SearcherMixin
@@ -21,13 +20,12 @@ from waffle.testutils import override_switch
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.course_modes.tests.factories import CourseModeFactory
from opaque_keys.edx.locator import LibraryLocator # lint-amnesty, pylint: disable=wrong-import-order
from openedx.core.lib.api.view_utils import LazySequence
from openedx.features.content_type_gating.models import ContentTypeGatingConfig
from openedx.features.course_duration_limits.models import CourseDurationLimitConfig
from common.djangoapps.student.auth import add_users
from common.djangoapps.student.roles import CourseInstructorRole, CourseStaffRole
from common.djangoapps.student.tests.factories import AdminFactory
from openedx.core.lib.api.view_utils import LazySequence
from openedx.features.content_type_gating.models import ContentTypeGatingConfig
from openedx.features.course_duration_limits.models import CourseDurationLimitConfig
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase, SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
@@ -78,7 +76,7 @@ class CourseListViewTestCase(CourseApiTestViewMixin, SharedModuleStoreTestCase):
@classmethod
def setUpClass(cls):
super(CourseListViewTestCase, cls).setUpClass()
super().setUpClass()
cls.course = cls.create_course()
cls.url = reverse('course-list')
cls.staff_user = cls.create_user(username='staff', is_staff=True)
@@ -159,7 +157,7 @@ class CourseListViewTestCaseMultipleCourses(CourseApiTestViewMixin, ModuleStoreT
ENABLED_SIGNALS = ['course_published']
def setUp(self):
super(CourseListViewTestCaseMultipleCourses, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course = self.create_course(mobile_available=False)
self.url = reverse('course-list')
self.staff_user = self.create_user(username='staff', is_staff=True)
@@ -201,7 +199,7 @@ class CourseListViewTestCaseMultipleCourses(CourseApiTestViewMixin, ModuleStoreT
if filter_:
params.update(filter_)
response = self.verify_response(params=params)
assert {course['course_id'] for course in response.data['results']} == {six.text_type(course.id) for course in expected_courses}, f'testing course_api.views.CourseListView with filter_={filter_}' # pylint: disable=line-too-long
assert {course['course_id'] for course in response.data['results']} == {str(course.id) for course in expected_courses}, f'testing course_api.views.CourseListView with filter_={filter_}' # pylint: disable=line-too-long
class CourseDetailViewTestCase(CourseApiTestViewMixin, SharedModuleStoreTestCase):
@@ -211,9 +209,9 @@ class CourseDetailViewTestCase(CourseApiTestViewMixin, SharedModuleStoreTestCase
@classmethod
def setUpClass(cls):
super(CourseDetailViewTestCase, cls).setUpClass()
super().setUpClass()
cls.course = cls.create_course()
cls.hidden_course = cls.create_course(course=u'hidden', visible_to_staff_only=True)
cls.hidden_course = cls.create_course(course='hidden', visible_to_staff_only=True)
cls.url = reverse('course-detail', kwargs={'course_key_string': cls.course.id})
cls.hidden_url = reverse('course-detail', kwargs={'course_key_string': cls.hidden_course.id})
cls.nonexistent_url = reverse('course-detail', kwargs={'course_key_string': 'edX/nope/Fall_2014'})
@@ -289,7 +287,7 @@ class CourseListSearchViewTest(CourseApiTestViewMixin, ModuleStoreTestCase, Sear
ENABLED_CACHES = ModuleStoreTestCase.ENABLED_CACHES + ['configuration']
def setUp(self):
super(CourseListSearchViewTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
DemoCourse.reset_count()
self.searcher.destroy()
@@ -316,7 +314,7 @@ class CourseListSearchViewTest(CourseApiTestViewMixin, ModuleStoreTestCase, Sear
'run': '2010',
'number': 'DemoZ',
# Using the slash separated course ID bcuz `DemoCourse` isn't updated yet to new locator.
'id': '{org_code}/DemoZ/2010'.format(org_code=org_code),
'id': f'{org_code}/DemoZ/2010',
'content': {
'short_description': short_description,
},
@@ -377,10 +375,10 @@ class CourseListSearchViewTest(CourseApiTestViewMixin, ModuleStoreTestCase, Sear
# Create 300 courses across 30 organizations
for org_num in range(10):
org_id = 'org{}'.format(org_num)
org_id = f'org{org_num}'
for course_num in range(30):
course_name = 'course{}.{}'.format(org_num, course_num)
course_run_name = 'run{}.{}'.format(org_num, course_num)
course_name = f'course{org_num}.{course_num}'
course_run_name = f'run{org_num}.{course_num}'
course = CourseFactory.create(org=org_id, number=course_name, run=course_run_name, emit_signals=True)
CourseModeFactory.create(course_id=course.id, mode_slug=CourseMode.AUDIT)
CourseModeFactory.create(course_id=course.id, mode_slug=CourseMode.VERIFIED)
@@ -412,7 +410,7 @@ class CourseIdListViewTestCase(CourseApiTestViewMixin, ModuleStoreTestCase):
ENABLED_SIGNALS = ['course_published']
def setUp(self):
super(CourseIdListViewTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course = self.create_course()
self.url = reverse('course-id-list')
self.staff_user = self.create_user(username='staff', is_staff=True)
@@ -528,7 +526,7 @@ class CourseIdListViewTestCase(CourseApiTestViewMixin, ModuleStoreTestCase):
class LazyPageNumberPaginationTestCase(TestCase): # lint-amnesty, pylint: disable=missing-class-docstring
def test_lazy_page_number_pagination(self):
number_sequence = range(20)
number_sequence = range(20) # lint-amnesty, pylint: disable=range-builtin-not-iterating
even_numbers_lazy_sequence = LazySequence(
(
number for number in number_sequence
@@ -557,7 +555,7 @@ class LazyPageNumberPaginationTestCase(TestCase): # lint-amnesty, pylint: disab
self.assertDictEqual(expected_response, paginated_response.data)
def test_not_found_error_for_invalid_page(self):
number_sequence = range(20)
number_sequence = range(20) # lint-amnesty, pylint: disable=range-builtin-not-iterating
even_numbers_lazy_sequence = LazySequence(
(
number for number in number_sequence

View File

@@ -10,7 +10,7 @@ from .views import CourseDetailView, CourseIdListView, CourseListView
urlpatterns = [
url(r'^v1/courses/$', CourseListView.as_view(), name="course-list"),
url(r'^v1/courses/{}'.format(settings.COURSE_KEY_PATTERN), CourseDetailView.as_view(), name="course-detail"),
url(fr'^v1/courses/{settings.COURSE_KEY_PATTERN}', CourseDetailView.as_view(), name="course-detail"),
url(r'^v1/course_ids/$', CourseIdListView.as_view(), name="course-id-list"),
url(r'', include('lms.djangoapps.course_api.blocks.urls'))
]

View File

@@ -7,9 +7,9 @@ from django.core.exceptions import ValidationError
from django.core.paginator import InvalidPage
from edx_django_utils.monitoring import function_trace
from edx_rest_framework_extensions.paginators import NamespacedPageNumberPagination
from rest_framework.exceptions import NotFound
from rest_framework.generics import ListAPIView, RetrieveAPIView
from rest_framework.throttling import UserRateThrottle
from rest_framework.exceptions import NotFound
from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin, view_auth_classes
@@ -159,7 +159,7 @@ class CourseListUserThrottle(UserRateThrottle):
self.rate = self.get_rate()
self.num_requests, self.duration = self.parse_rate(self.rate)
return super(CourseListUserThrottle, self).allow_request(request, view) # lint-amnesty, pylint: disable=super-with-arguments
return super().allow_request(request, view)
class LazyPageNumberPagination(NamespacedPageNumberPagination):
@@ -193,7 +193,7 @@ class LazyPageNumberPagination(NamespacedPageNumberPagination):
self.page.number = self.page.paginator.num_pages
raise NotFound(msg) # lint-amnesty, pylint: disable=raise-missing-from
return super(LazyPageNumberPagination, self).get_paginated_response(data) # lint-amnesty, pylint: disable=super-with-arguments
return super().get_paginated_response(data)
@function_trace('pagination_paginate_queryset')
def paginate_queryset(self, queryset, request, view=None):
@@ -347,7 +347,7 @@ class CourseIdListUserThrottle(UserRateThrottle):
self.rate = self.get_rate()
self.num_requests, self.duration = self.parse_rate(self.rate)
return super(CourseIdListUserThrottle, self).allow_request(request, view) # lint-amnesty, pylint: disable=super-with-arguments
return super().allow_request(request, view)
@view_auth_classes()
@@ -434,7 +434,7 @@ class CourseIdListView(DeveloperErrorViewMixin, ListAPIView):
This should be called once per GET request.
"""
return super(CourseIdListView, self).paginate_queryset(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
return super().paginate_queryset(*args, **kwargs)
@function_trace('get_paginated_response')
def get_paginated_response(self, *args, **kwargs):
@@ -446,7 +446,7 @@ class CourseIdListView(DeveloperErrorViewMixin, ListAPIView):
means two GET requests and one function call per request. Otherwise, if
the whole response fits in one page, this function never gets called.
"""
return super(CourseIdListView, self).get_paginated_response(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
return super().get_paginated_response(*args, **kwargs)
@function_trace('filter_queryset')
def filter_queryset(self, *args, **kwargs):
@@ -456,7 +456,7 @@ class CourseIdListView(DeveloperErrorViewMixin, ListAPIView):
This should be called once per GET request.
"""
return super(CourseIdListView, self).filter_queryset(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
return super().filter_queryset(*args, **kwargs)
@function_trace('get_serializer')
def get_serializer(self, *args, **kwargs):
@@ -466,4 +466,4 @@ class CourseIdListView(DeveloperErrorViewMixin, ListAPIView):
This should be called once per GET request.
"""
return super(CourseIdListView, self).get_serializer(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
return super().get_serializer(*args, **kwargs)