From 5562944e87640b3e6df6d0dd8d0dc4f4f4daa6c7 Mon Sep 17 00:00:00 2001 From: "M. Zulqarnain" Date: Thu, 4 Mar 2021 15:06:07 +0500 Subject: [PATCH] BOM-2369 (A): pyupgrade on api,contentstore and cms_user_tasks apps under CMS (#26676) * pyupgrade on cms api,contentstore and cms_user_tasks apps --- .../api/v1/serializers/course_runs.py | 14 +- .../test_serializers/test_course_runs.py | 4 +- .../v1/tests/test_views/test_course_runs.py | 10 +- cms/djangoapps/api/v1/views/course_runs.py | 4 +- cms/djangoapps/cms_user_tasks/apps.py | 2 +- cms/djangoapps/cms_user_tasks/signals.py | 2 +- cms/djangoapps/cms_user_tasks/tasks.py | 10 +- cms/djangoapps/cms_user_tasks/tests.py | 12 +- cms/djangoapps/contentstore/api/tests/base.py | 4 +- .../contentstore/api/tests/test_import.py | 4 +- .../contentstore/api/tests/test_validation.py | 6 +- cms/djangoapps/contentstore/api/urls.py | 6 +- .../contentstore/api/views/course_import.py | 13 +- .../contentstore/api/views/course_quality.py | 17 ++- .../api/views/course_validation.py | 13 +- .../contentstore/api/views/utils.py | 2 +- cms/djangoapps/contentstore/apps.py | 2 +- cms/djangoapps/contentstore/config/waffle.py | 13 +- .../contentstore/course_group_config.py | 12 +- .../contentstore/courseware_index.py | 44 +++---- .../contentstore/debug_file_uploader.py | 2 +- .../contentstore/git_export_utils.py | 35 +++-- .../contentstore/migrations/0001_initial.py | 5 +- .../migrations/0002_add_assets_page_flag.py | 3 - .../0003_remove_assets_page_flag.py | 3 - ...ove_push_notification_configmodel_table.py | 1 - ...d_enable_checklists_quality_waffle_flag.py | 3 - cms/djangoapps/contentstore/models.py | 2 +- cms/djangoapps/contentstore/outlines.py | 2 +- cms/djangoapps/contentstore/proctoring.py | 23 ++-- .../rest_api/v1/tests/test_views.py | 4 +- .../contentstore/rest_api/v1/urls.py | 2 +- .../contentstore/rest_api/v1/views.py | 2 +- .../contentstore/signals/handlers.py | 21 ++- cms/djangoapps/contentstore/storage.py | 2 +- cms/djangoapps/contentstore/tasks.py | 122 +++++++++--------- cms/djangoapps/contentstore/toggles.py | 4 +- cms/djangoapps/contentstore/utils.py | 38 +++--- cms/djangoapps/contentstore/video_utils.py | 22 ++-- 39 files changed, 231 insertions(+), 259 deletions(-) diff --git a/cms/djangoapps/api/v1/serializers/course_runs.py b/cms/djangoapps/api/v1/serializers/course_runs.py index 8bd97eac4f..3ba33f1a07 100644 --- a/cms/djangoapps/api/v1/serializers/course_runs.py +++ b/cms/djangoapps/api/v1/serializers/course_runs.py @@ -1,10 +1,6 @@ """ Course run serializers. """ - - import logging -import time # pylint: disable=unused-import -import six from django.contrib.auth import get_user_model from django.db import transaction from django.utils.translation import ugettext_lazy as _ @@ -14,8 +10,8 @@ from rest_framework.fields import empty from cms.djangoapps.contentstore.views.assets import update_course_run_asset from cms.djangoapps.contentstore.views.course import create_new_course, get_course_and_check_access, rerun_course -from openedx.core.lib.courses import course_image_url from common.djangoapps.student.models import CourseAccessRole +from openedx.core.lib.courses import course_image_url from xmodule.modulestore.django import modulestore IMAGE_TYPES = { @@ -88,7 +84,7 @@ def image_is_jpeg_or_png(value): content_type = value.content_type if content_type not in list(IMAGE_TYPES.keys()): raise serializers.ValidationError( - u'Only JPEG and PNG image types are supported. {} is not valid'.format(content_type)) + f'Only JPEG and PNG image types are supported. {content_type} is not valid') class CourseRunImageField(serializers.ImageField): # lint-amnesty, pylint: disable=missing-class-docstring @@ -143,7 +139,7 @@ class CourseRunSerializer(CourseRunSerializerCommonFieldsMixin, CourseRunTeamSer with transaction.atomic(): self.update_team(instance, team) - for attr, value in six.iteritems(validated_data): + for attr, value in validated_data.items(): setattr(instance, attr, value) modulestore().update_item(instance, self.context['request'].user.id) @@ -183,11 +179,11 @@ class CourseRunRerunSerializer(CourseRunSerializerCommonFieldsMixin, CourseRunTe new_course_run_key = store.make_course_key(course_run_key.org, number, run) except InvalidKeyError: raise serializers.ValidationError( # lint-amnesty, pylint: disable=raise-missing-from - u'Invalid key supplied. Ensure there are no special characters in the Course Number.' + 'Invalid key supplied. Ensure there are no special characters in the Course Number.' ) if store.has_course(new_course_run_key, ignore_case=True): raise serializers.ValidationError( - {'run': u'Course run {key} already exists'.format(key=new_course_run_key)} + {'run': f'Course run {new_course_run_key} already exists'} ) return attrs diff --git a/cms/djangoapps/api/v1/tests/test_serializers/test_course_runs.py b/cms/djangoapps/api/v1/tests/test_serializers/test_course_runs.py index 3a6dbb1f6f..fd2fa0682c 100644 --- a/cms/djangoapps/api/v1/tests/test_serializers/test_course_runs.py +++ b/cms/djangoapps/api/v1/tests/test_serializers/test_course_runs.py @@ -7,9 +7,9 @@ import ddt import pytz from django.test import RequestFactory -from openedx.core.lib.courses import course_image_url from common.djangoapps.student.roles import CourseInstructorRole, CourseStaffRole from common.djangoapps.student.tests.factories import UserFactory +from openedx.core.lib.courses import course_image_url from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory @@ -21,7 +21,7 @@ from ..utils import serialize_datetime class CourseRunSerializerTests(ModuleStoreTestCase): # lint-amnesty, pylint: disable=missing-class-docstring def setUp(self): - super(CourseRunSerializerTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.course_start = datetime.datetime.now(pytz.UTC) self.course_end = self.course_start + datetime.timedelta(days=30) diff --git a/cms/djangoapps/api/v1/tests/test_views/test_course_runs.py b/cms/djangoapps/api/v1/tests/test_views/test_course_runs.py index 5d98818c04..7fccfcd5d8 100644 --- a/cms/djangoapps/api/v1/tests/test_views/test_course_runs.py +++ b/cms/djangoapps/api/v1/tests/test_views/test_course_runs.py @@ -2,20 +2,20 @@ import datetime +from unittest.mock import patch # lint-amnesty, pylint: disable=unused-import import ddt import pytz from django.core.files.uploadedfile import SimpleUploadedFile from django.test import RequestFactory, override_settings from django.urls import reverse -from mock import patch # lint-amnesty, pylint: disable=unused-import from opaque_keys.edx.keys import CourseKey from organizations.api import add_organization, get_course_organizations from rest_framework.test import APIClient -from openedx.core.lib.courses import course_image_url from common.djangoapps.student.models import CourseAccessRole from common.djangoapps.student.tests.factories import TEST_PASSWORD, AdminFactory, UserFactory +from openedx.core.lib.courses import course_image_url from xmodule.contentstore.content import StaticContent from xmodule.contentstore.django import contentstore from xmodule.exceptions import NotFoundError @@ -35,7 +35,7 @@ class CourseRunViewSetTests(ModuleStoreTestCase): list_url = reverse('api:v1:course_run-list') def setUp(self): - super(CourseRunViewSetTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.client = APIClient() user = AdminFactory() self.client.login(username=user.username, password=TEST_PASSWORD) @@ -388,7 +388,7 @@ class CourseRunViewSetTests(ModuleStoreTestCase): } response = self.client.post(url, data, format='json') assert response.status_code == 400 - assert response.data == {'run': [u'Course run {key} already exists'.format(key=course_run.id)]} + assert response.data == {'run': [f'Course run {course_run.id} already exists']} def test_rerun_invalid_number(self): course_run = ToyCourseFactory() @@ -400,5 +400,5 @@ class CourseRunViewSetTests(ModuleStoreTestCase): response = self.client.post(url, data, format='json') assert response.status_code == 400 assert response.data == {'non_field_errors': [ - u'Invalid key supplied. Ensure there are no special characters in the Course Number.' + 'Invalid key supplied. Ensure there are no special characters in the Course Number.' ]} diff --git a/cms/djangoapps/api/v1/views/course_runs.py b/cms/djangoapps/api/v1/views/course_runs.py index ebe1b63777..a0415d4e06 100644 --- a/cms/djangoapps/api/v1/views/course_runs.py +++ b/cms/djangoapps/api/v1/views/course_runs.py @@ -30,8 +30,8 @@ class CourseRunViewSet(viewsets.GenericViewSet): # lint-amnesty, pylint: disabl lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field assert lookup_url_kwarg in self.kwargs, ( - u'Expected view %s to be called with a URL keyword argument ' - u'named "%s". Fix your URL conf, or set the `.lookup_field` ' + 'Expected view %s to be called with a URL keyword argument ' + 'named "%s". Fix your URL conf, or set the `.lookup_field` ' 'attribute on the view correctly.' % (self.__class__.__name__, lookup_url_kwarg) ) diff --git a/cms/djangoapps/cms_user_tasks/apps.py b/cms/djangoapps/cms_user_tasks/apps.py index c8b3edb899..c4ac938c6e 100644 --- a/cms/djangoapps/cms_user_tasks/apps.py +++ b/cms/djangoapps/cms_user_tasks/apps.py @@ -11,7 +11,7 @@ class CmsUserTasksConfig(AppConfig): """ Application Configuration for cms_user_tasks. """ - name = u'cms.djangoapps.cms_user_tasks' + name = 'cms.djangoapps.cms_user_tasks' def ready(self): """ diff --git a/cms/djangoapps/cms_user_tasks/signals.py b/cms/djangoapps/cms_user_tasks/signals.py index 2d36ec821d..815d69f664 100644 --- a/cms/djangoapps/cms_user_tasks/signals.py +++ b/cms/djangoapps/cms_user_tasks/signals.py @@ -4,10 +4,10 @@ Receivers of signals sent from django-user-tasks import logging +from urllib.parse import urljoin from django.dispatch import receiver from django.urls import reverse -from six.moves.urllib.parse import urljoin from user_tasks.models import UserTaskArtifact from user_tasks.signals import user_task_stopped diff --git a/cms/djangoapps/cms_user_tasks/tasks.py b/cms/djangoapps/cms_user_tasks/tasks.py index cbb4171bac..01515182ac 100644 --- a/cms/djangoapps/cms_user_tasks/tasks.py +++ b/cms/djangoapps/cms_user_tasks/tasks.py @@ -4,8 +4,8 @@ Celery tasks used by cms_user_tasks from boto.exception import NoAuthHandlerFound -from celery.exceptions import MaxRetriesExceededError from celery import shared_task +from celery.exceptions import MaxRetriesExceededError from celery.utils.log import get_task_logger from django.conf import settings from django.core import mail @@ -45,10 +45,10 @@ def send_task_complete_email(self, task_name, task_state_text, dest_addr, detail try: mail.send_mail(subject, message, from_address, [dest_addr], fail_silently=False) - LOGGER.info(u"Task complete email has been sent to User %s", dest_addr) + LOGGER.info("Task complete email has been sent to User %s", dest_addr) except NoAuthHandlerFound: LOGGER.info( - u'Retrying sending email to user %s, attempt # %s of %s', + 'Retrying sending email to user %s, attempt # %s of %s', dest_addr, retries, TASK_COMPLETE_EMAIL_MAX_RETRIES @@ -57,14 +57,14 @@ def send_task_complete_email(self, task_name, task_state_text, dest_addr, detail self.retry(countdown=TASK_COMPLETE_EMAIL_TIMEOUT, max_retries=TASK_COMPLETE_EMAIL_MAX_RETRIES) except MaxRetriesExceededError: LOGGER.error( - u'Unable to send task completion email to user from "%s" to "%s"', + 'Unable to send task completion email to user from "%s" to "%s"', from_address, dest_addr, exc_info=True ) except Exception: # pylint: disable=broad-except LOGGER.exception( - u'Unable to send task completion email to user from "%s" to "%s"', + 'Unable to send task completion email to user from "%s" to "%s"', from_address, dest_addr, exc_info=True diff --git a/cms/djangoapps/cms_user_tasks/tests.py b/cms/djangoapps/cms_user_tasks/tests.py index 7015b7b4e2..bbc6cd098b 100644 --- a/cms/djangoapps/cms_user_tasks/tests.py +++ b/cms/djangoapps/cms_user_tasks/tests.py @@ -4,9 +4,9 @@ Unit tests for integration of the django-user-tasks app and its REST API. import logging +from unittest import mock from uuid import uuid4 -import mock from boto.exception import NoAuthHandlerFound from django.conf import settings from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user @@ -80,7 +80,7 @@ class TestUserTasks(APITestCase): cls.artifact = UserTaskArtifact.objects.create(status=cls.status, text='Lorem ipsum') def setUp(self): - super(TestUserTasks, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.status.refresh_from_db() self.client.force_authenticate(self.user) # pylint: disable=no-member @@ -152,7 +152,7 @@ class TestUserTaskStopped(APITestCase): total_steps=5) def setUp(self): - super(TestUserTaskStopped, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments + super().setUp() self.status.refresh_from_db() self.client.force_authenticate(self.user) # pylint: disable=no-member @@ -169,7 +169,7 @@ class TestUserTaskStopped(APITestCase): platform_name=settings.PLATFORM_NAME, studio_name=settings.STUDIO_NAME ) body_fragments = [ - "Your {task_name} task has completed with the status".format(task_name=self.status.name.lower()), + f"Your {self.status.name.lower()} task has completed with the status", "https://test.edx.org/", reverse('usertaskstatus-detail', args=[self.status.uuid]) ] @@ -202,7 +202,7 @@ class TestUserTaskStopped(APITestCase): platform_name=settings.PLATFORM_NAME, studio_name=settings.STUDIO_NAME ) fragments = [ - "Your {task_name} task has completed with the status".format(task_name=self.status.name.lower()), + f"Your {self.status.name.lower()} task has completed with the status", "Sign in to view the details of your task or download any files created." ] @@ -234,4 +234,4 @@ class TestUserTaskStopped(APITestCase): mock_delay.side_effect = NoAuthHandlerFound() user_task_stopped.send(sender=UserTaskStatus, status=self.status) self.assertTrue(mock_delay.called) - self.assertEqual(hdlr.messages['error'][0], u'Unable to queue send_task_complete_email') + self.assertEqual(hdlr.messages['error'][0], 'Unable to queue send_task_complete_email') diff --git a/cms/djangoapps/contentstore/api/tests/base.py b/cms/djangoapps/contentstore/api/tests/base.py index b125ff704f..10dc8bce64 100644 --- a/cms/djangoapps/contentstore/api/tests/base.py +++ b/cms/djangoapps/contentstore/api/tests/base.py @@ -6,8 +6,8 @@ Base test case for the course API views. from django.urls import reverse from rest_framework.test import APITestCase -from lms.djangoapps.courseware.tests.factories import StaffFactory from common.djangoapps.student.tests.factories import UserFactory +from lms.djangoapps.courseware.tests.factories import StaffFactory from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory @@ -22,7 +22,7 @@ class BaseCourseViewTest(SharedModuleStoreTestCase, APITestCase): @classmethod def setUpClass(cls): - super(BaseCourseViewTest, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create(display_name='test course', run="Testing_course") cls.course_key = cls.course.id diff --git a/cms/djangoapps/contentstore/api/tests/test_import.py b/cms/djangoapps/contentstore/api/tests/test_import.py index a989993590..a7a8246868 100644 --- a/cms/djangoapps/contentstore/api/tests/test_import.py +++ b/cms/djangoapps/contentstore/api/tests/test_import.py @@ -13,8 +13,8 @@ from rest_framework import status from rest_framework.test import APITestCase from user_tasks.models import UserTaskStatus -from lms.djangoapps.courseware.tests.factories import StaffFactory from common.djangoapps.student.tests.factories import UserFactory +from lms.djangoapps.courseware.tests.factories import StaffFactory from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory @@ -27,7 +27,7 @@ class CourseImportViewTest(SharedModuleStoreTestCase, APITestCase): @classmethod def setUpClass(cls): - super(CourseImportViewTest, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create(display_name='test course', run="Testing_course") cls.course_key = cls.course.id diff --git a/cms/djangoapps/contentstore/api/tests/test_validation.py b/cms/djangoapps/contentstore/api/tests/test_validation.py index cf570c7fcf..a269eb2a59 100644 --- a/cms/djangoapps/contentstore/api/tests/test_validation.py +++ b/cms/djangoapps/contentstore/api/tests/test_validation.py @@ -9,8 +9,8 @@ from django.urls import reverse from rest_framework import status from rest_framework.test import APITestCase -from lms.djangoapps.courseware.tests.factories import StaffFactory from common.djangoapps.student.tests.factories import UserFactory +from lms.djangoapps.courseware.tests.factories import StaffFactory from xmodule.modulestore.tests.django_utils import TEST_DATA_SPLIT_MODULESTORE, SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory @@ -23,7 +23,7 @@ class CourseValidationViewTest(SharedModuleStoreTestCase, APITestCase): @classmethod def setUpClass(cls): - super(CourseValidationViewTest, cls).setUpClass() + super().setUpClass() cls.course = CourseFactory.create(display_name='test course', run="Testing_course") cls.course_key = cls.course.id @@ -49,7 +49,7 @@ class CourseValidationViewTest(SharedModuleStoreTestCase, APITestCase): update_key.course_key, update_key.block_type, block_id=update_key.block_id, - fields=dict(data=u"
  1. Date

    Hello world!
"), + fields=dict(data="
  1. Date

    Hello world!
"), ) section = ItemFactory.create( diff --git a/cms/djangoapps/contentstore/api/urls.py b/cms/djangoapps/contentstore/api/urls.py index 21a60c0342..384b0b708e 100644 --- a/cms/djangoapps/contentstore/api/urls.py +++ b/cms/djangoapps/contentstore/api/urls.py @@ -9,10 +9,10 @@ from cms.djangoapps.contentstore.api.views import course_import, course_quality, app_name = 'contentstore' urlpatterns = [ - url(r'^v0/import/{course_id}/$'.format(course_id=settings.COURSE_ID_PATTERN,), + url(fr'^v0/import/{settings.COURSE_ID_PATTERN}/$', course_import.CourseImportView.as_view(), name='course_import'), - url(r'^v1/validation/{course_id}/$'.format(course_id=settings.COURSE_ID_PATTERN,), + url(fr'^v1/validation/{settings.COURSE_ID_PATTERN}/$', course_validation.CourseValidationView.as_view(), name='course_validation'), - url(r'^v1/quality/{course_id}/$'.format(course_id=settings.COURSE_ID_PATTERN,), + url(fr'^v1/quality/{settings.COURSE_ID_PATTERN}/$', course_quality.CourseQualityView.as_view(), name='course_quality'), ] diff --git a/cms/djangoapps/contentstore/api/views/course_import.py b/cms/djangoapps/contentstore/api/views/course_import.py index 9430aee2cd..13106b1d36 100644 --- a/cms/djangoapps/contentstore/api/views/course_import.py +++ b/cms/djangoapps/contentstore/api/views/course_import.py @@ -14,7 +14,6 @@ from rest_framework import status from rest_framework.exceptions import AuthenticationFailed from rest_framework.generics import GenericAPIView from rest_framework.response import Response -from six import text_type from user_tasks.models import UserTaskStatus from cms.djangoapps.contentstore.storage import course_import_export_storage @@ -35,7 +34,7 @@ class CourseImportExportViewMixin(DeveloperErrorViewMixin): """ Ensures that the user is authenticated (e.g. not an AnonymousUser) """ - super(CourseImportExportViewMixin, self).perform_authentication(request) # lint-amnesty, pylint: disable=super-with-arguments + super().perform_authentication(request) if request.user.is_anonymous: raise AuthenticationFailed @@ -135,18 +134,18 @@ class CourseImportView(CourseImportExportViewMixin, GenericAPIView): if not course_dir.isdir(): os.mkdir(course_dir) - log.debug(u'importing course to {0}'.format(temp_filepath)) + log.debug(f'importing course to {temp_filepath}') with open(temp_filepath, "wb+") as temp_file: for chunk in request.FILES['course_data'].chunks(): temp_file.write(chunk) - log.info(u"Course import %s: Upload complete", course_key) + log.info("Course import %s: Upload complete", course_key) with open(temp_filepath, 'rb') as local_file: django_file = File(local_file) - storage_path = course_import_export_storage.save(u'olx_import/' + filename, django_file) + storage_path = course_import_export_storage.save('olx_import/' + filename, django_file) async_result = import_olx.delay( - request.user.id, text_type(course_key), storage_path, filename, request.LANGUAGE_CODE) + request.user.id, str(course_key), storage_path, filename, request.LANGUAGE_CODE) return Response({ 'task_id': async_result.task_id }) @@ -166,7 +165,7 @@ class CourseImportView(CourseImportExportViewMixin, GenericAPIView): try: task_id = request.GET['task_id'] filename = request.GET['filename'] - args = {u'course_key_string': str(course_key), u'archive_name': filename} + args = {'course_key_string': str(course_key), 'archive_name': filename} name = CourseImportTask.generate_name(args) task_status = UserTaskStatus.objects.filter(name=name, task_id=task_id).first() return Response({ diff --git a/cms/djangoapps/contentstore/api/views/course_quality.py b/cms/djangoapps/contentstore/api/views/course_quality.py index b0dab122f2..7b2585f50f 100644 --- a/cms/djangoapps/contentstore/api/views/course_quality.py +++ b/cms/djangoapps/contentstore/api/views/course_quality.py @@ -3,7 +3,6 @@ import logging import time import numpy as np -import six from edxval.api import get_videos_for_course from rest_framework.generics import GenericAPIView from rest_framework.response import Response @@ -91,7 +90,7 @@ class CourseQualityView(DeveloperErrorViewMixin, GenericAPIView): if log_time: start_time = time.time() output = func(*args) - log.info(u'[%s] completed in [%f]', func.__name__, (time.time() - start_time)) + log.info('[%s] completed in [%f]', func.__name__, (time.time() - start_time)) else: output = func(*args) return output @@ -154,25 +153,25 @@ class CourseQualityView(DeveloperErrorViewMixin, GenericAPIView): def _subsections_quality(self, course, request): # lint-amnesty, pylint: disable=missing-function-docstring subsection_unit_dict = self._get_subsections_and_units(course, request) num_block_types_per_subsection_dict = {} - for subsection_key, unit_dict in six.iteritems(subsection_unit_dict): + for subsection_key, unit_dict in subsection_unit_dict.items(): leaf_block_types_in_subsection = ( unit_info['leaf_block_types'] - for unit_info in six.itervalues(unit_dict) + for unit_info in unit_dict.values() ) num_block_types_per_subsection_dict[subsection_key] = len(set().union(*leaf_block_types_in_subsection)) return dict( total_visible=len(num_block_types_per_subsection_dict), - num_with_one_block_type=list(six.itervalues(num_block_types_per_subsection_dict)).count(1), - num_block_types=self._stats_dict(list(six.itervalues(num_block_types_per_subsection_dict))), + num_with_one_block_type=list(num_block_types_per_subsection_dict.values()).count(1), + num_block_types=self._stats_dict(list(num_block_types_per_subsection_dict.values())), ) def _units_quality(self, course, request): # lint-amnesty, pylint: disable=missing-function-docstring subsection_unit_dict = self._get_subsections_and_units(course, request) num_leaf_blocks_per_unit = [ unit_info['num_leaf_blocks'] - for unit_dict in six.itervalues(subsection_unit_dict) - for unit_info in six.itervalues(unit_dict) + for unit_dict in subsection_unit_dict.values() + for unit_info in unit_dict.values() ] return dict( total_visible=len(num_leaf_blocks_per_unit), @@ -215,7 +214,7 @@ class CourseQualityView(DeveloperErrorViewMixin, GenericAPIView): leaf_blocks = cls._get_leaf_blocks(unit) unit_dict[unit.location] = dict( num_leaf_blocks=len(leaf_blocks), - leaf_block_types=set(block.location.block_type for block in leaf_blocks), + leaf_block_types={block.location.block_type for block in leaf_blocks}, ) subsection_dict[subsection.location] = unit_dict diff --git a/cms/djangoapps/contentstore/api/views/course_validation.py b/cms/djangoapps/contentstore/api/views/course_validation.py index a04b022288..1ab4e218ee 100644 --- a/cms/djangoapps/contentstore/api/views/course_validation.py +++ b/cms/djangoapps/contentstore/api/views/course_validation.py @@ -2,7 +2,6 @@ import logging import dateutil -import six from pytz import UTC from rest_framework.generics import GenericAPIView from rest_framework.response import Response @@ -118,7 +117,7 @@ class CourseValidationView(DeveloperErrorViewMixin, GenericAPIView): ] assignments_with_dates_before_start = ( [ - {'id': six.text_type(a.location), 'display_name': a.display_name} + {'id': str(a.location), 'display_name': a.display_name} for a in assignments_with_dates if a.due < course.start ] @@ -128,7 +127,7 @@ class CourseValidationView(DeveloperErrorViewMixin, GenericAPIView): assignments_with_dates_after_end = ( [ - {'id': six.text_type(a.location), 'display_name': a.display_name} + {'id': str(a.location), 'display_name': a.display_name} for a in assignments_with_dates if a.due > course.end ] @@ -144,7 +143,7 @@ class CourseValidationView(DeveloperErrorViewMixin, GenericAPIView): ] assignments_with_dates_before_start = ( [ - {'id': six.text_type(a.location), 'display_name': a.display_name} + {'id': str(a.location), 'display_name': a.display_name} for a in assignments_with_dates if a.due < course.start ] @@ -154,7 +153,7 @@ class CourseValidationView(DeveloperErrorViewMixin, GenericAPIView): assignments_with_dates_after_end = ( [ - {'id': six.text_type(a.location), 'display_name': a.display_name} + {'id': str(a.location), 'display_name': a.display_name} for a in assignments_with_dates if a.due > course.end ] @@ -175,14 +174,14 @@ class CourseValidationView(DeveloperErrorViewMixin, GenericAPIView): parent_unit = modulestore().get_item(ora.parent) parent_assignment = modulestore().get_item(parent_unit.parent) assignments_with_ora_dates_before_start.append({ - 'id': six.text_type(parent_assignment.location), + 'id': str(parent_assignment.location), 'display_name': parent_assignment.display_name }) if course.end and self._has_date_after_end(ora, course.end): parent_unit = modulestore().get_item(ora.parent) parent_assignment = modulestore().get_item(parent_unit.parent) assignments_with_ora_dates_after_end.append({ - 'id': six.text_type(parent_assignment.location), + 'id': str(parent_assignment.location), 'display_name': parent_assignment.display_name }) diff --git a/cms/djangoapps/contentstore/api/views/utils.py b/cms/djangoapps/contentstore/api/views/utils.py index 92e75ae1a2..36c83b211b 100644 --- a/cms/djangoapps/contentstore/api/views/utils.py +++ b/cms/djangoapps/contentstore/api/views/utils.py @@ -9,10 +9,10 @@ from opaque_keys.edx.keys import CourseKey from rest_framework import status from rest_framework.generics import GenericAPIView +from common.djangoapps.student.auth import has_course_author_access from openedx.core.djangoapps.util.forms import to_bool from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin, view_auth_classes from openedx.core.lib.cache_utils import request_cached -from common.djangoapps.student.auth import has_course_author_access from xmodule.modulestore.django import modulestore diff --git a/cms/djangoapps/contentstore/apps.py b/cms/djangoapps/contentstore/apps.py index d3a854f005..a1ff02e4ca 100644 --- a/cms/djangoapps/contentstore/apps.py +++ b/cms/djangoapps/contentstore/apps.py @@ -12,7 +12,7 @@ class ContentstoreConfig(AppConfig): """ Application Configuration for Contentstore. """ - name = u'cms.djangoapps.contentstore' + name = 'cms.djangoapps.contentstore' def ready(self): """ diff --git a/cms/djangoapps/contentstore/config/waffle.py b/cms/djangoapps/contentstore/config/waffle.py index f6947229be..24c13190b3 100644 --- a/cms/djangoapps/contentstore/config/waffle.py +++ b/cms/djangoapps/contentstore/config/waffle.py @@ -5,39 +5,40 @@ waffle switches for the contentstore app. from edx_toggles.toggles import LegacyWaffleFlag, LegacyWaffleFlagNamespace, LegacyWaffleSwitchNamespace + from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag # Namespace -WAFFLE_NAMESPACE = u'studio' +WAFFLE_NAMESPACE = 'studio' # Switches -ENABLE_ACCESSIBILITY_POLICY_PAGE = u'enable_policy_page' +ENABLE_ACCESSIBILITY_POLICY_PAGE = 'enable_policy_page' def waffle(): """ Returns the namespaced, cached, audited Waffle Switch class for Studio pages. """ - return LegacyWaffleSwitchNamespace(name=WAFFLE_NAMESPACE, log_prefix=u'Studio: ') + return LegacyWaffleSwitchNamespace(name=WAFFLE_NAMESPACE, log_prefix='Studio: ') def waffle_flags(): """ Returns the namespaced, cached, audited Waffle Flag class for Studio pages. """ - return LegacyWaffleFlagNamespace(name=WAFFLE_NAMESPACE, log_prefix=u'Studio: ') + return LegacyWaffleFlagNamespace(name=WAFFLE_NAMESPACE, log_prefix='Studio: ') # TODO: After removing this flag, add a migration to remove waffle flag in a follow-up deployment. ENABLE_CHECKLISTS_QUALITY = CourseWaffleFlag( waffle_namespace=waffle_flags(), - flag_name=u'enable_checklists_quality', + flag_name='enable_checklists_quality', module_name=__name__, ) SHOW_REVIEW_RULES_FLAG = CourseWaffleFlag( waffle_namespace=waffle_flags(), - flag_name=u'show_review_rules', + flag_name='show_review_rules', module_name=__name__, ) diff --git a/cms/djangoapps/contentstore/course_group_config.py b/cms/djangoapps/contentstore/course_group_config.py index a9ae47aecf..33ed74f722 100644 --- a/cms/djangoapps/contentstore/course_group_config.py +++ b/cms/djangoapps/contentstore/course_group_config.py @@ -10,9 +10,9 @@ from collections import defaultdict from django.utils.translation import ugettext as _ from cms.djangoapps.contentstore.utils import reverse_usage_url +from common.djangoapps.util.db import MYSQL_MAX_INT, generate_int_id from lms.lib.utils import get_parent_unit from openedx.core.djangoapps.course_groups.partition_scheme import get_cohorted_user_partition -from common.djangoapps.util.db import MYSQL_MAX_INT, generate_int_id from xmodule.partitions.partitions import MINIMUM_STATIC_PARTITION_ID, ReadOnlyUserPartitionError, UserPartition from xmodule.partitions.partitions_service import get_all_partitions_for_course from xmodule.split_test_module import get_split_user_partitions @@ -39,7 +39,7 @@ class GroupConfigurationsValidationError(Exception): pass # lint-amnesty, pylint: disable=unnecessary-pass -class GroupConfiguration(object): +class GroupConfiguration: """ Prepare Group Configuration for the course. """ @@ -102,7 +102,7 @@ class GroupConfiguration(object): """ Return a list of IDs that already in use. """ - return set([p.id for p in get_all_partitions_for_course(course)]) # lint-amnesty, pylint: disable=consider-using-set-comprehension + return {p.id for p in get_all_partitions_for_course(course)} def get_user_partition(self): """ @@ -144,7 +144,7 @@ class GroupConfiguration(object): course.location.course_key.make_usage_key(unit_for_url.location.block_type, unit_for_url.location.block_id) ) - usage_dict = {'label': u"{} / {}".format(unit.display_name, item.display_name), 'url': unit_url} + usage_dict = {'label': f"{unit.display_name} / {item.display_name}", 'url': unit_url} if scheme_name == RANDOM_SCHEME: validation_summary = item.general_validation_message() usage_dict.update({'validation': validation_summary.to_json() if validation_summary else None}) @@ -196,7 +196,7 @@ class GroupConfiguration(object): for split_test in split_tests: unit = split_test.get_parent() if not unit: - log.warning(u"Unable to find parent for split_test %s", split_test.location) + log.warning("Unable to find parent for split_test %s", split_test.location) # Make sure that this user_partition appears in the output even though it has no content usage_info[split_test.user_partition_id] = [] continue @@ -236,7 +236,7 @@ class GroupConfiguration(object): for item, partition_id, group_id in GroupConfiguration._iterate_items_and_group_ids(course, items): unit = item.get_parent() if not unit: - log.warning(u"Unable to find parent for component %s", item.location) + log.warning("Unable to find parent for component %s", item.location) continue usage_info[partition_id][group_id].append(GroupConfiguration._get_usage_dict( diff --git a/cms/djangoapps/contentstore/courseware_index.py b/cms/djangoapps/contentstore/courseware_index.py index e6b465797a..8bffd64a1f 100644 --- a/cms/djangoapps/contentstore/courseware_index.py +++ b/cms/djangoapps/contentstore/courseware_index.py @@ -11,7 +11,6 @@ from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy from eventtracking import tracker from search.search_engine_base import SearchEngine -from six import add_metaclass, string_types, text_type from cms.djangoapps.contentstore.course_group_config import GroupConfiguration from common.djangoapps.course_modes.models import CourseMode @@ -57,12 +56,11 @@ class SearchIndexingError(Exception): """ Indicates some error(s) occured during indexing """ def __init__(self, message, error_list): - super(SearchIndexingError, self).__init__(message) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(message) self.error_list = error_list -@add_metaclass(ABCMeta) -class SearchIndexerBase(object, metaclass=ABCMeta): +class SearchIndexerBase(metaclass=ABCMeta): """ Base class to perform indexing for courseware or library search from different modulestores """ @@ -192,22 +190,22 @@ class SearchIndexerBase(object, metaclass=ABCMeta): for split_test_child in item.get_children(): if split_partition: for group in split_partition.groups: - group_id = text_type(group.id) + group_id = str(group.id) child_location = item.group_id_to_child.get(group_id, None) if child_location == split_test_child.location: groups_usage_info.update({ - text_type(get_item_location(split_test_child)): [group_id], + str(get_item_location(split_test_child)): [group_id], }) for component in split_test_child.get_children(): groups_usage_info.update({ - text_type(get_item_location(component)): [group_id] + str(get_item_location(component)): [group_id] }) if groups_usage_info: item_location = get_item_location(item) - item_content_groups = groups_usage_info.get(text_type(item_location), None) + item_content_groups = groups_usage_info.get(str(item_location), None) - item_id = text_type(cls._id_modifier(item.scope_ids.usage_id)) + item_id = str(cls._id_modifier(item.scope_ids.usage_id)) indexed_items.add(item_id) if item.has_children: # determine if it's okay to skip adding the children herein based upon how recently any may have changed @@ -244,8 +242,8 @@ class SearchIndexerBase(object, metaclass=ABCMeta): return item_content_groups except Exception as err: # pylint: disable=broad-except # broad exception so that index operation does not fail on one item of many - log.warning(u'Could not index item: %s - %r', item.location, err) - error_list.append(_(u'Could not index item: {}').format(item.location)) + log.warning('Could not index item: %s - %r', item.location, err) + error_list.append(_('Could not index item: {}').format(item.location)) try: with modulestore.branch_setting(ModuleStoreEnum.RevisionOption.published_only): @@ -263,7 +261,7 @@ class SearchIndexerBase(object, metaclass=ABCMeta): except Exception as err: # pylint: disable=broad-except # broad exception so that index operation does not prevent the rest of the application from working log.exception( - u"Indexing error encountered, courseware index may be out of date %s - %r", + "Indexing error encountered, courseware index may be out of date %s - %r", structure_key, err ) @@ -365,7 +363,7 @@ class CoursewareSearchIndexer(SearchIndexerBase): @classmethod def _get_location_info(cls, normalized_structure_key): """ Builds location info dictionary """ - return {"course": text_type(normalized_structure_key), "org": normalized_structure_key.org} + return {"course": str(normalized_structure_key), "org": normalized_structure_key.org} @classmethod def do_course_reindex(cls, modulestore, course_key): @@ -405,7 +403,7 @@ class CoursewareSearchIndexer(SearchIndexerBase): for name, group in groups.items(): for module in group: view, args, kwargs = resolve(module['url']) # pylint: disable=unused-variable - usage_key_string = text_type(kwargs['usage_key_string']) + usage_key_string = str(kwargs['usage_key_string']) if groups_usage_dict.get(usage_key_string, None): groups_usage_dict[usage_key_string].append(name) else: @@ -434,7 +432,7 @@ class CoursewareSearchIndexer(SearchIndexerBase): while parent is not None: path_component_name = parent.display_name if not path_component_name: - path_component_name = text_type(cls.UNNAMED_MODULE_NAME) + path_component_name = str(cls.UNNAMED_MODULE_NAME) location_path.append(path_component_name) parent = parent.get_parent() location_path.reverse() @@ -469,7 +467,7 @@ class LibrarySearchIndexer(SearchIndexerBase): @classmethod def _get_location_info(cls, normalized_structure_key): """ Builds location info dictionary """ - return {"library": text_type(normalized_structure_key)} + return {"library": str(normalized_structure_key)} @classmethod def _id_modifier(cls, usage_id): @@ -484,7 +482,7 @@ class LibrarySearchIndexer(SearchIndexerBase): return cls._do_reindex(modulestore, library_key) -class AboutInfo(object): +class AboutInfo: """ About info structure to contain 1) Property name to use 2) Where to add in the index (using flags above) @@ -605,7 +603,7 @@ class CourseAboutSearchIndexer(CoursewareSearchIndexer): if not searcher: return - course_id = text_type(course.id) + course_id = str(course.id) course_info = { 'id': course_id, 'course': course_id, @@ -631,7 +629,7 @@ class CourseAboutSearchIndexer(CoursewareSearchIndexer): except: # pylint: disable=bare-except section_content = None log.warning( - u"Course discovery could not collect property %s for course %s", + "Course discovery could not collect property %s for course %s", about_information.property_name, course_id, exc_info=True, @@ -640,7 +638,7 @@ class CourseAboutSearchIndexer(CoursewareSearchIndexer): if section_content: if about_information.index_flags & AboutInfo.ANALYSE: analyse_content = section_content - if isinstance(section_content, string_types): + if isinstance(section_content, str): analyse_content = strip_html_content_to_text(section_content) course_info['content'][about_information.property_name] = analyse_content if about_information.index_flags & AboutInfo.PROPERTY: @@ -651,20 +649,20 @@ class CourseAboutSearchIndexer(CoursewareSearchIndexer): searcher.index([course_info]) except: log.exception( - u"Course discovery indexing error encountered, course discovery index may be out of date %s", + "Course discovery indexing error encountered, course discovery index may be out of date %s", course_id, ) raise log.debug( - u"Successfully added %s course to the course discovery index", + "Successfully added %s course to the course discovery index", course_id ) @classmethod def _get_location_info(cls, normalized_structure_key): """ Builds location info dictionary """ - return {"course": text_type(normalized_structure_key), "org": normalized_structure_key.org} + return {"course": str(normalized_structure_key), "org": normalized_structure_key.org} @classmethod def remove_deleted_items(cls, structure_key): # lint-amnesty, pylint: disable=arguments-differ diff --git a/cms/djangoapps/contentstore/debug_file_uploader.py b/cms/djangoapps/contentstore/debug_file_uploader.py index 77cbc59b71..e9546abd24 100644 --- a/cms/djangoapps/contentstore/debug_file_uploader.py +++ b/cms/djangoapps/contentstore/debug_file_uploader.py @@ -8,7 +8,7 @@ from django.core.files.uploadhandler import FileUploadHandler class DebugFileUploader(FileUploadHandler): # lint-amnesty, pylint: disable=missing-class-docstring def __init__(self, request=None): - super(DebugFileUploader, self).__init__(request) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(request) self.count = 0 def receive_data_chunk(self, raw_data, start): diff --git a/cms/djangoapps/contentstore/git_export_utils.py b/cms/djangoapps/contentstore/git_export_utils.py index bf39bbb901..67ff7a9709 100644 --- a/cms/djangoapps/contentstore/git_export_utils.py +++ b/cms/djangoapps/contentstore/git_export_utils.py @@ -7,13 +7,12 @@ committing and pushing the changes. import logging import os import subprocess +from urllib.parse import urlparse -import six from django.conf import settings from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user from django.utils import timezone from django.utils.translation import ugettext_lazy as _ -from six.moves.urllib.parse import urlparse from xmodule.contentstore.django import contentstore from xmodule.modulestore.django import modulestore @@ -32,9 +31,9 @@ class GitExportError(Exception): def __init__(self, message): # Force the lazy i18n values to turn into actual unicode objects - super().__init__(six.text_type(message)) + super().__init__(str(message)) - NO_EXPORT_DIR = _(u"GIT_REPO_EXPORT_DIR not set or path {0} doesn't exist, " + NO_EXPORT_DIR = _("GIT_REPO_EXPORT_DIR not set or path {0} doesn't exist, " "please create it, or configure a different path with " "GIT_REPO_EXPORT_DIR").format(GIT_REPO_EXPORT_DIR) URL_BAD = _('Non writable git url provided. Expecting something like:' @@ -61,9 +60,9 @@ def cmd_log(cmd, cwd): command doesn't return 0, and returns the command's output. """ output = subprocess.check_output(cmd, cwd=cwd, stderr=subprocess.STDOUT) - log.debug(u'Command was: {0!r}. ' - u'Working directory was: {1!r}'.format(' '.join(cmd), cwd)) - log.debug(u'Command output was: {0!r}'.format(output)) + log.debug('Command was: {!r}. ' + 'Working directory was: {!r}'.format(' '.join(cmd), cwd)) + log.debug(f'Command output was: {output!r}') return output @@ -92,11 +91,11 @@ def export_to_git(course_id, repo, user='', rdir=None): else: rdir = repo.rsplit('/', 1)[-1].rsplit('.git', 1)[0] - log.debug(u"rdir = %s", rdir) + log.debug("rdir = %s", rdir) # Pull or clone repo before exporting to xml # and update url in case origin changed. - rdirp = '{0}/{1}'.format(GIT_REPO_EXPORT_DIR, rdir) + rdirp = f'{GIT_REPO_EXPORT_DIR}/{rdir}' branch = None if os.path.exists(rdirp): log.info('Directory already exists, doing a git reset and pull ' @@ -107,13 +106,13 @@ def export_to_git(course_id, repo, user='', rdir=None): try: branch = cmd_log(cmd, cwd).decode('utf-8').strip('\n') except subprocess.CalledProcessError as ex: - log.exception(u'Failed to get branch: %r', ex.output) + log.exception('Failed to get branch: %r', ex.output) raise GitExportError(GitExportError.DETACHED_HEAD) from ex cmds = [ ['git', 'remote', 'set-url', 'origin', repo], ['git', 'fetch', 'origin'], - ['git', 'reset', '--hard', 'origin/{0}'.format(branch)], + ['git', 'reset', '--hard', f'origin/{branch}'], ['git', 'pull'], ['git', 'clean', '-d', '-f'], ] @@ -126,7 +125,7 @@ def export_to_git(course_id, repo, user='', rdir=None): try: cmd_log(cmd, cwd) except subprocess.CalledProcessError as ex: - log.exception(u'Failed to pull git repository: %r', ex.output) + log.exception('Failed to pull git repository: %r', ex.output) raise GitExportError(GitExportError.CANNOT_PULL) from ex # export course as xml before commiting and pushing @@ -135,7 +134,7 @@ def export_to_git(course_id, repo, user='', rdir=None): try: export_course_to_xml(modulestore(), contentstore(), course_id, root_dir, course_dir) - except (EnvironmentError, AttributeError): + except (OSError, AttributeError): log.exception('Failed export to xml') raise GitExportError(GitExportError.XML_EXPORT_FAIL) # lint-amnesty, pylint: disable=raise-missing-from @@ -145,7 +144,7 @@ def export_to_git(course_id, repo, user='', rdir=None): try: branch = cmd_log(cmd, os.path.abspath(rdirp)).decode('utf-8').strip('\n') except subprocess.CalledProcessError as ex: - log.exception(u'Failed to get branch from freshly cloned repo: %r', + log.exception('Failed to get branch from freshly cloned repo: %r', ex.output) raise GitExportError(GitExportError.MISSING_BRANCH) from ex @@ -161,23 +160,23 @@ def export_to_git(course_id, repo, user='', rdir=None): ident = GIT_EXPORT_DEFAULT_IDENT time_stamp = timezone.now() cwd = os.path.abspath(rdirp) - commit_msg = u"Export from Studio at {time_stamp}".format( + commit_msg = "Export from Studio at {time_stamp}".format( time_stamp=time_stamp, ) try: cmd_log(['git', 'config', 'user.email', ident['email']], cwd) cmd_log(['git', 'config', 'user.name', ident['name']], cwd) except subprocess.CalledProcessError as ex: - log.exception(u'Error running git configure commands: %r', ex.output) + log.exception('Error running git configure commands: %r', ex.output) raise GitExportError(GitExportError.CONFIG_ERROR) from ex try: cmd_log(['git', 'add', '.'], cwd) cmd_log(['git', 'commit', '-a', '-m', commit_msg], cwd) except subprocess.CalledProcessError as ex: - log.exception(u'Unable to commit changes: %r', ex.output) + log.exception('Unable to commit changes: %r', ex.output) raise GitExportError(GitExportError.CANNOT_COMMIT) from ex try: cmd_log(['git', 'push', '-q', 'origin', branch], cwd) except subprocess.CalledProcessError as ex: - log.exception(u'Error running git push command: %r', ex.output) + log.exception('Error running git push command: %r', ex.output) raise GitExportError(GitExportError.CANNOT_PUSH) from ex diff --git a/cms/djangoapps/contentstore/migrations/0001_initial.py b/cms/djangoapps/contentstore/migrations/0001_initial.py index edae499bb9..af5aa387f1 100644 --- a/cms/djangoapps/contentstore/migrations/0001_initial.py +++ b/cms/djangoapps/contentstore/migrations/0001_initial.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - - import django.db.models.deletion from django.conf import settings from django.db import migrations, models @@ -32,7 +29,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')), ('enabled', models.BooleanField(default=False, verbose_name='Enabled')), - ('profile_whitelist', models.TextField(help_text=u'A comma-separated list of names of profiles to include in video encoding downloads.', blank=True)), + ('profile_whitelist', models.TextField(help_text='A comma-separated list of names of profiles to include in video encoding downloads.', blank=True)), ('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, editable=False, to=settings.AUTH_USER_MODEL, null=True, verbose_name='Changed by')), ], options={ diff --git a/cms/djangoapps/contentstore/migrations/0002_add_assets_page_flag.py b/cms/djangoapps/contentstore/migrations/0002_add_assets_page_flag.py index eec99e24d2..678349489d 100644 --- a/cms/djangoapps/contentstore/migrations/0002_add_assets_page_flag.py +++ b/cms/djangoapps/contentstore/migrations/0002_add_assets_page_flag.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - - import django.db.models.deletion from django.conf import settings from django.db import migrations, models diff --git a/cms/djangoapps/contentstore/migrations/0003_remove_assets_page_flag.py b/cms/djangoapps/contentstore/migrations/0003_remove_assets_page_flag.py index 08f8f73d3c..ffe5d183df 100644 --- a/cms/djangoapps/contentstore/migrations/0003_remove_assets_page_flag.py +++ b/cms/djangoapps/contentstore/migrations/0003_remove_assets_page_flag.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- - - from django.db import migrations, models diff --git a/cms/djangoapps/contentstore/migrations/0004_remove_push_notification_configmodel_table.py b/cms/djangoapps/contentstore/migrations/0004_remove_push_notification_configmodel_table.py index 28e17ab72e..bfec00f991 100644 --- a/cms/djangoapps/contentstore/migrations/0004_remove_push_notification_configmodel_table.py +++ b/cms/djangoapps/contentstore/migrations/0004_remove_push_notification_configmodel_table.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Generated by Django 1.11.22 on 2019-07-26 20:12 diff --git a/cms/djangoapps/contentstore/migrations/0005_add_enable_checklists_quality_waffle_flag.py b/cms/djangoapps/contentstore/migrations/0005_add_enable_checklists_quality_waffle_flag.py index 325a8ab70a..04dafec2d4 100644 --- a/cms/djangoapps/contentstore/migrations/0005_add_enable_checklists_quality_waffle_flag.py +++ b/cms/djangoapps/contentstore/migrations/0005_add_enable_checklists_quality_waffle_flag.py @@ -1,6 +1,3 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import, unicode_literals - from django.db import migrations from cms.djangoapps.contentstore.config.waffle import ENABLE_CHECKLISTS_QUALITY diff --git a/cms/djangoapps/contentstore/models.py b/cms/djangoapps/contentstore/models.py index c69643d27e..f6817ac59f 100644 --- a/cms/djangoapps/contentstore/models.py +++ b/cms/djangoapps/contentstore/models.py @@ -15,7 +15,7 @@ class VideoUploadConfig(ConfigurationModel): """ profile_whitelist = TextField( blank=True, - help_text=u"A comma-separated list of names of profiles to include in video encoding downloads." + help_text="A comma-separated list of names of profiles to include in video encoding downloads." ) @classmethod diff --git a/cms/djangoapps/contentstore/outlines.py b/cms/djangoapps/contentstore/outlines.py index 63fd53fa48..708afdf98c 100644 --- a/cms/djangoapps/contentstore/outlines.py +++ b/cms/djangoapps/contentstore/outlines.py @@ -14,7 +14,7 @@ from openedx.core.djangoapps.content.learning_sequences.data import ( CourseSectionData, CourseVisibility, ExamData, - VisibilityData, + VisibilityData ) from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.django import modulestore diff --git a/cms/djangoapps/contentstore/proctoring.py b/cms/djangoapps/contentstore/proctoring.py index fc9ea55c97..cf0704259b 100644 --- a/cms/djangoapps/contentstore/proctoring.py +++ b/cms/djangoapps/contentstore/proctoring.py @@ -5,7 +5,6 @@ Code related to the handling of Proctored Exams in Studio import logging -import six from django.conf import settings from edx_proctoring.api import ( create_exam, @@ -39,7 +38,7 @@ def register_special_exams(course_key): course = modulestore().get_course(course_key) if course is None: - raise ItemNotFoundError(u"Course {} does not exist", six.text_type(course_key)) # lint-amnesty, pylint: disable=raising-format-tuple + raise ItemNotFoundError("Course {} does not exist", str(course_key)) # lint-amnesty, pylint: disable=raising-format-tuple if not course.enable_proctored_exams and not course.enable_timed_exams: # likewise if course does not have these features turned on @@ -68,8 +67,8 @@ def register_special_exams(course_key): # add/update any exam entries in edx-proctoring for timed_exam in timed_exams: msg = ( - u'Found {location} as a timed-exam in course structure. Inspecting...'.format( - location=six.text_type(timed_exam.location) + 'Found {location} as a timed-exam in course structure. Inspecting...'.format( + location=str(timed_exam.location) ) ) log.info(msg) @@ -87,20 +86,20 @@ def register_special_exams(course_key): } try: - exam = get_exam_by_content_id(six.text_type(course_key), six.text_type(timed_exam.location)) + exam = get_exam_by_content_id(str(course_key), str(timed_exam.location)) # update case, make sure everything is synced exam_metadata['exam_id'] = exam['id'] exam_id = update_exam(**exam_metadata) - msg = u'Updated timed exam {exam_id}'.format(exam_id=exam['id']) + msg = 'Updated timed exam {exam_id}'.format(exam_id=exam['id']) log.info(msg) except ProctoredExamNotFoundException: - exam_metadata['course_id'] = six.text_type(course_key) - exam_metadata['content_id'] = six.text_type(timed_exam.location) + exam_metadata['course_id'] = str(course_key) + exam_metadata['content_id'] = str(timed_exam.location) exam_id = create_exam(**exam_metadata) - msg = u'Created new timed exam {exam_id}'.format(exam_id=exam_id) + msg = f'Created new timed exam {exam_id}' log.info(msg) exam_review_policy_metadata = { @@ -116,7 +115,7 @@ def register_special_exams(course_key): except ProctoredExamReviewPolicyNotFoundException: if timed_exam.exam_review_rules: # won't save an empty rule. create_exam_review_policy(**exam_review_policy_metadata) - msg = u'Created new exam review policy with exam_id {exam_id}'.format(exam_id=exam_id) + msg = f'Created new exam review policy with exam_id {exam_id}' log.info(msg) else: try: @@ -135,12 +134,12 @@ def register_special_exams(course_key): search = [ timed_exam for timed_exam in timed_exams if - six.text_type(timed_exam.location) == exam['content_id'] + str(timed_exam.location) == exam['content_id'] ] if not search: # This means it was turned off in Studio, we need to mark # the exam as inactive (we don't delete!) - msg = u'Disabling timed exam {exam_id}'.format(exam_id=exam['id']) + msg = 'Disabling timed exam {exam_id}'.format(exam_id=exam['id']) log.info(msg) update_exam( exam_id=exam['id'], diff --git a/cms/djangoapps/contentstore/rest_api/v1/tests/test_views.py b/cms/djangoapps/contentstore/rest_api/v1/tests/test_views.py index a17a4cf34b..c70327da1d 100644 --- a/cms/djangoapps/contentstore/rest_api/v1/tests/test_views.py +++ b/cms/djangoapps/contentstore/rest_api/v1/tests/test_views.py @@ -8,8 +8,8 @@ from opaque_keys.edx.keys import CourseKey from rest_framework import status from rest_framework.test import APITestCase -from lms.djangoapps.courseware.tests.factories import GlobalStaffFactory, InstructorFactory from common.djangoapps.student.tests.factories import UserFactory +from lms.djangoapps.courseware.tests.factories import GlobalStaffFactory, InstructorFactory from xmodule.modulestore.django import modulestore from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory @@ -77,7 +77,7 @@ class ProctoringExamSettingsTestMixin(): response = self.make_request(course_id=course_id) assert response.status_code == status.HTTP_404_NOT_FOUND assert response.data == { - 'detail': 'Course with course_id {} does not exist.'.format(course_id) + 'detail': f'Course with course_id {course_id} does not exist.' } diff --git a/cms/djangoapps/contentstore/rest_api/v1/urls.py b/cms/djangoapps/contentstore/rest_api/v1/urls.py index 75c13d78c3..83e03bcb73 100644 --- a/cms/djangoapps/contentstore/rest_api/v1/urls.py +++ b/cms/djangoapps/contentstore/rest_api/v1/urls.py @@ -10,7 +10,7 @@ app_name = 'v1' urlpatterns = [ re_path( - r'^proctored_exam_settings/{}$'.format(COURSE_ID_PATTERN), + fr'^proctored_exam_settings/{COURSE_ID_PATTERN}$', views.ProctoredExamSettingsView.as_view(), name="proctored_exam_settings" ), diff --git a/cms/djangoapps/contentstore/rest_api/v1/views.py b/cms/djangoapps/contentstore/rest_api/v1/views.py index 9ff8a19d1a..76ba4b359e 100644 --- a/cms/djangoapps/contentstore/rest_api/v1/views.py +++ b/cms/djangoapps/contentstore/rest_api/v1/views.py @@ -169,7 +169,7 @@ class ProctoredExamSettingsView(APIView): if not course_module: raise NotFound( - 'Course with course_id {} does not exist.'.format(course_id) + f'Course with course_id {course_id} does not exist.' ) return course_module diff --git a/cms/djangoapps/contentstore/signals/handlers.py b/cms/djangoapps/contentstore/signals/handlers.py index 69c1421df3..fb7ed333ad 100644 --- a/cms/djangoapps/contentstore/signals/handlers.py +++ b/cms/djangoapps/contentstore/signals/handlers.py @@ -5,22 +5,21 @@ import logging from datetime import datetime from functools import wraps -import six from django.core.cache import cache from django.dispatch import receiver from pytz import UTC from cms.djangoapps.contentstore.courseware_index import ( - CoursewareSearchIndexer, CourseAboutSearchIndexer, + CoursewareSearchIndexer, LibrarySearchIndexer ) from cms.djangoapps.contentstore.proctoring import register_special_exams +from common.djangoapps.track.event_transaction_utils import get_event_transaction_id, get_event_transaction_type +from common.djangoapps.util.module_utils import yield_dynamic_descriptor_descendants from lms.djangoapps.grades.api import task_compute_all_grades_for_course from openedx.core.djangoapps.credit.signals import on_course_publish from openedx.core.lib.gating import api as gating_api -from common.djangoapps.track.event_transaction_utils import get_event_transaction_id, get_event_transaction_type -from common.djangoapps.util.module_utils import yield_dynamic_descriptor_descendants from xmodule.modulestore.django import SignalHandler, modulestore from .signals import GRADING_POLICY_CHANGED @@ -36,7 +35,7 @@ def locked(expiry_seconds, key): # lint-amnesty, pylint: disable=missing-functi def wrapper(*args, **kwargs): cache_key = '{}-{}'.format(func.__name__, kwargs[key]) if cache.add(cache_key, "true", expiry_seconds): - log.info(u'Locking task in cache with key: %s for %s seconds', cache_key, expiry_seconds) + log.info('Locking task in cache with key: %s for %s seconds', cache_key, expiry_seconds) return func(*args, **kwargs) else: log.info('Task with key %s already exists in cache', cache_key) @@ -84,7 +83,7 @@ def listen_for_library_update(sender, library_key, **kwargs): # pylint: disable # import here, because signal is registered at startup, but items in tasks are not yet able to be loaded from cms.djangoapps.contentstore.tasks import update_library_index - update_library_index.delay(six.text_type(library_key), datetime.now(UTC).isoformat()) + update_library_index.delay(str(library_key), datetime.now(UTC).isoformat()) @receiver(SignalHandler.item_deleted) @@ -122,13 +121,13 @@ def handle_grading_policy_changed(sender, **kwargs): Receives signal and kicks off celery task to recalculate grades """ kwargs = { - 'course_key': six.text_type(kwargs.get('course_key')), - 'grading_policy_hash': six.text_type(kwargs.get('grading_policy_hash')), - 'event_transaction_id': six.text_type(get_event_transaction_id()), - 'event_transaction_type': six.text_type(get_event_transaction_type()), + 'course_key': str(kwargs.get('course_key')), + 'grading_policy_hash': str(kwargs.get('grading_policy_hash')), + 'event_transaction_id': str(get_event_transaction_id()), + 'event_transaction_type': str(get_event_transaction_type()), } result = task_compute_all_grades_for_course.apply_async(kwargs=kwargs, countdown=GRADING_POLICY_COUNTDOWN_SECONDS) - log.info(u"Grades: Created {task_name}[{task_id}] with arguments {kwargs}".format( + log.info("Grades: Created {task_name}[{task_id}] with arguments {kwargs}".format( task_name=task_compute_all_grades_for_course.name, task_id=result.task_id, kwargs=kwargs, diff --git a/cms/djangoapps/contentstore/storage.py b/cms/djangoapps/contentstore/storage.py index 0757676676..f3a04fbd21 100644 --- a/cms/djangoapps/contentstore/storage.py +++ b/cms/djangoapps/contentstore/storage.py @@ -16,7 +16,7 @@ class ImportExportS3Storage(S3BotoStorage): # pylint: disable=abstract-method def __init__(self): bucket = setting('COURSE_IMPORT_EXPORT_BUCKET', settings.AWS_STORAGE_BUCKET_NAME) - super(ImportExportS3Storage, self).__init__(bucket=bucket, custom_domain=None, querystring_auth=True) # lint-amnesty, pylint: disable=super-with-arguments + super().__init__(bucket=bucket, custom_domain=None, querystring_auth=True) # pylint: disable=invalid-name course_import_export_storage = get_storage_class(settings.COURSE_IMPORT_EXPORT_STORAGE)() diff --git a/cms/djangoapps/contentstore/tasks.py b/cms/djangoapps/contentstore/tasks.py index 2768a80525..1a2780fb93 100644 --- a/cms/djangoapps/contentstore/tasks.py +++ b/cms/djangoapps/contentstore/tasks.py @@ -29,7 +29,6 @@ from organizations.api import add_organization_course, ensure_organization from organizations.models import OrganizationCourse from path import Path as path from pytz import UTC -from six import iteritems, text_type from user_tasks.models import UserTaskArtifact, UserTaskStatus from user_tasks.tasks import UserTask @@ -42,9 +41,9 @@ from cms.djangoapps.contentstore.storage import course_import_export_storage from cms.djangoapps.contentstore.utils import initialize_permissions, reverse_usage_url, translation_language from cms.djangoapps.models.settings.course_metadata import CourseMetadata from common.djangoapps.course_action_state.models import CourseRerunState +from common.djangoapps.student.auth import has_course_author_access from openedx.core.djangoapps.embargo.models import CountryAccessRule, RestrictedCourse from openedx.core.lib.extract_tar import safetar_extractall -from common.djangoapps.student.auth import has_course_author_access from xmodule.contentstore.django import contentstore from xmodule.course_module import CourseFields from xmodule.exceptions import SerializationError @@ -53,6 +52,7 @@ from xmodule.modulestore.django import modulestore from xmodule.modulestore.exceptions import DuplicateCourseError, ItemNotFoundError from xmodule.modulestore.xml_exporter import export_course_to_xml, export_library_to_xml from xmodule.modulestore.xml_importer import import_course_from_xml, import_library_from_xml + from .outlines import update_outline_from_modulestore User = get_user_model() @@ -76,7 +76,7 @@ def clone_instance(instance, field_values): """ instance.pk = None - for field, value in iteritems(field_values): + for field, value in field_values.items(): setattr(instance, field, value) instance.save() @@ -136,14 +136,14 @@ def rerun_course(source_course_key_string, destination_course_key_string, user_i except DuplicateCourseError: # do NOT delete the original course, only update the status CourseRerunState.objects.failed(course_key=destination_course_key) - LOGGER.exception(u'Course Rerun Error') + LOGGER.exception('Course Rerun Error') return "duplicate course" # catch all exceptions so we can update the state and properly cleanup the course. except Exception as exc: # pylint: disable=broad-except # update state: Failed CourseRerunState.objects.failed(course_key=destination_course_key) - LOGGER.exception(u'Course Rerun Error') + LOGGER.exception('Course Rerun Error') try: # cleanup any remnants of the course @@ -152,12 +152,12 @@ def rerun_course(source_course_key_string, destination_course_key_string, user_i # it's possible there was an error even before the course module was created pass - return u"exception: " + text_type(exc) + return "exception: " + str(exc) def deserialize_fields(json_fields): fields = json.loads(json_fields) - for field_name, value in iteritems(fields): + for field_name, value in fields.items(): fields[field_name] = getattr(CourseFields, field_name).from_json(value) return fields @@ -183,7 +183,7 @@ def update_search_index(course_id, triggered_time_isoformat): # expensive (sometimes hours-long for really complex courses). if isinstance(course_key, CCXLocator): LOGGER.warning( - u'Search indexing skipped for CCX Course %s (this is currently too slow to run in production)', + 'Search indexing skipped for CCX Course %s (this is currently too slow to run in production)', course_id ) return @@ -193,13 +193,13 @@ def update_search_index(course_id, triggered_time_isoformat): except SearchIndexingError as exc: error_list = exc.error_list LOGGER.error( - u"Search indexing error for complete course %s - %s - %s", + "Search indexing error for complete course %s - %s - %s", course_id, - text_type(exc), + str(exc), error_list, ) else: - LOGGER.debug(u'Search indexing successful for complete course %s', course_id) + LOGGER.debug('Search indexing successful for complete course %s', course_id) @shared_task @@ -211,9 +211,9 @@ def update_library_index(library_id, triggered_time_isoformat): LibrarySearchIndexer.index(modulestore(), library_key, triggered_at=(_parse_time(triggered_time_isoformat))) except SearchIndexingError as exc: - LOGGER.error(u'Search indexing error for library %s - %s', library_id, text_type(exc)) + LOGGER.error('Search indexing error for library %s - %s', library_id, str(exc)) else: - LOGGER.debug(u'Search indexing successful for library %s', library_id) + LOGGER.debug('Search indexing successful for library %s', library_id) class CourseExportTask(UserTask): # pylint: disable=abstract-method @@ -244,8 +244,8 @@ class CourseExportTask(UserTask): # pylint: disable=abstract-method Returns: text_type: The generated name """ - key = arguments_dict[u'course_key_string'] - return u'Export of {}'.format(key) + key = arguments_dict['course_key_string'] + return f'Export of {key}' @shared_task(base=CourseExportTask, bind=True) @@ -262,11 +262,11 @@ def export_olx(self, user_id, course_key_string, language): user = User.objects.get(pk=user_id) except User.DoesNotExist: with translation_language(language): - self.status.fail(_(u'Unknown User ID: {0}').format(user_id)) + self.status.fail(_('Unknown User ID: {0}').format(user_id)) return if not has_course_author_access(user, courselike_key): with translation_language(language): - self.status.fail(_(u'Permission denied')) + self.status.fail(_('Permission denied')) return if isinstance(courselike_key, LibraryLocator): @@ -275,16 +275,16 @@ def export_olx(self, user_id, course_key_string, language): courselike_module = modulestore().get_course(courselike_key) try: - self.status.set_state(u'Exporting') + self.status.set_state('Exporting') tarball = create_export_tarball(courselike_module, courselike_key, {}, self.status) - artifact = UserTaskArtifact(status=self.status, name=u'Output') + artifact = UserTaskArtifact(status=self.status, name='Output') artifact.file.save(name=os.path.basename(tarball.name), content=File(tarball)) artifact.save() # catch all exceptions so we can record useful error messages except Exception as exception: # pylint: disable=broad-except - LOGGER.exception(u'Error exporting course %s', courselike_key, exc_info=True) + LOGGER.exception('Error exporting course %s', courselike_key, exc_info=True) if self.status.state != UserTaskStatus.FAILED: - self.status.fail({'raw_error_msg': text_type(exception)}) + self.status.fail({'raw_error_msg': str(exception)}) return @@ -305,14 +305,14 @@ def create_export_tarball(course_module, course_key, context, status=None): export_course_to_xml(modulestore(), contentstore(), course_module.id, root_dir, name) if status: - status.set_state(u'Compressing') + status.set_state('Compressing') status.increment_completed_steps() - LOGGER.debug(u'tar file being generated at %s', export_file.name) + LOGGER.debug('tar file being generated at %s', export_file.name) with tarfile.open(name=export_file.name, mode='w:gz') as tar_file: tar_file.add(root_dir / name, arcname=name) except SerializationError as exc: - LOGGER.exception(u'There was an error exporting %s', course_key, exc_info=True) + LOGGER.exception('There was an error exporting %s', course_key, exc_info=True) parent = None try: failed_item = modulestore().get_item(exc.location) @@ -334,7 +334,7 @@ def create_export_tarball(course_module, course_key, context, status=None): 'edit_unit_url': context['edit_unit_url']})) raise except Exception as exc: - LOGGER.exception(u'There was an error exporting %s', course_key, exc_info=True) + LOGGER.exception('There was an error exporting %s', course_key, exc_info=True) context.update({ 'in_err': True, 'edit_unit_url': None, @@ -378,9 +378,9 @@ class CourseImportTask(UserTask): # pylint: disable=abstract-method Returns: text_type: The generated name """ - key = arguments_dict[u'course_key_string'] - filename = arguments_dict[u'archive_name'] - return u'Import of {} from {}'.format(key, filename) + key = arguments_dict['course_key_string'] + filename = arguments_dict['archive_name'] + return f'Import of {key} from {filename}' @shared_task(base=CourseImportTask, bind=True) @@ -396,11 +396,11 @@ def import_olx(self, user_id, course_key_string, archive_path, archive_name, lan user = User.objects.get(pk=user_id) except User.DoesNotExist: with translation_language(language): - self.status.fail(_(u'Unknown User ID: {0}').format(user_id)) + self.status.fail(_('Unknown User ID: {0}').format(user_id)) return if not has_course_author_access(user, courselike_key): with translation_language(language): - self.status.fail(_(u'Permission denied')) + self.status.fail(_('Permission denied')) return is_library = isinstance(courselike_key, LibraryLocator) @@ -420,24 +420,24 @@ def import_olx(self, user_id, course_key_string, archive_path, archive_name, lan subdir = base64.urlsafe_b64encode(repr(courselike_key).encode('utf-8')).decode('utf-8') course_dir = data_root / subdir try: - self.status.set_state(u'Unpacking') + self.status.set_state('Unpacking') - if not archive_name.endswith(u'.tar.gz'): + if not archive_name.endswith('.tar.gz'): with translation_language(language): - self.status.fail(_(u'We only support uploading a .tar.gz file.')) + self.status.fail(_('We only support uploading a .tar.gz file.')) return temp_filepath = course_dir / get_valid_filename(archive_name) if not course_dir.isdir(): os.mkdir(course_dir) - LOGGER.debug(u'importing course to {0}'.format(temp_filepath)) + LOGGER.debug(f'importing course to {temp_filepath}') # Copy the OLX archive from where it was uploaded to (S3, Swift, file system, etc.) if not course_import_export_storage.exists(archive_path): - LOGGER.info(u'Course import %s: Uploaded file %s not found', courselike_key, archive_path) + LOGGER.info('Course import %s: Uploaded file %s not found', courselike_key, archive_path) with translation_language(language): - self.status.fail(_(u'Tar file not found')) + self.status.fail(_('Tar file not found')) return with course_import_export_storage.open(archive_path, 'rb') as source: with open(temp_filepath, 'wb') as destination: @@ -448,7 +448,7 @@ def import_olx(self, user_id, course_key_string, archive_path, archive_name, lan return source.read(FILE_READ_CHUNK) for chunk in iter(read_chunk, b''): destination.write(chunk) - LOGGER.info(u'Course import %s: Download from storage complete', courselike_key) + LOGGER.info('Course import %s: Download from storage complete', courselike_key) # Delete from source location course_import_export_storage.delete(archive_path) @@ -456,40 +456,40 @@ def import_olx(self, user_id, course_key_string, archive_path, archive_name, lan # current course state before import. if is_course: if courselike_module.entrance_exam_enabled: - fake_request = RequestFactory().get(u'/') + fake_request = RequestFactory().get('/') fake_request.user = user from .views.entrance_exam import remove_entrance_exam_milestone_reference # TODO: Is this really ok? Seems dangerous for a live course remove_entrance_exam_milestone_reference(fake_request, courselike_key) LOGGER.info( - u'entrance exam milestone content reference for course %s has been removed', + 'entrance exam milestone content reference for course %s has been removed', courselike_module.id ) # Send errors to client with stage at which error occurred. except Exception as exception: # pylint: disable=broad-except if course_dir.isdir(): shutil.rmtree(course_dir) - LOGGER.info(u'Course import %s: Temp data cleared', courselike_key) + LOGGER.info('Course import %s: Temp data cleared', courselike_key) - LOGGER.exception(u'Error importing course %s', courselike_key, exc_info=True) - self.status.fail(text_type(exception)) + LOGGER.exception('Error importing course %s', courselike_key, exc_info=True) + self.status.fail(str(exception)) return # try-finally block for proper clean up after receiving file. try: tar_file = tarfile.open(temp_filepath) try: - safetar_extractall(tar_file, (course_dir + u'/')) + safetar_extractall(tar_file, (course_dir + '/')) except SuspiciousOperation as exc: - LOGGER.info(u'Course import %s: Unsafe tar file - %s', courselike_key, exc.args[0]) + LOGGER.info('Course import %s: Unsafe tar file - %s', courselike_key, exc.args[0]) with translation_language(language): - self.status.fail(_(u'Unsafe tar file. Aborting import.')) + self.status.fail(_('Unsafe tar file. Aborting import.')) return finally: tar_file.close() - LOGGER.info(u'Course import %s: Uploaded file extracted', courselike_key) - self.status.set_state(u'Verifying') + LOGGER.info('Course import %s: Uploaded file extracted', courselike_key) + self.status.set_state('Verifying') self.status.increment_completed_steps() # find the 'course.xml' file @@ -516,14 +516,14 @@ def import_olx(self, user_id, course_key_string, archive_path, archive_name, lan dirpath = get_dir_for_filename(course_dir, root_name) if not dirpath: with translation_language(language): - self.status.fail(_(u'Could not find the {0} file in the package.').format(root_name)) + self.status.fail(_('Could not find the {0} file in the package.').format(root_name)) return dirpath = os.path.relpath(dirpath, data_root) - LOGGER.debug(u'found %s at %s', root_name, dirpath) + LOGGER.debug('found %s at %s', root_name, dirpath) - LOGGER.info(u'Course import %s: Extracted file verified', courselike_key) - self.status.set_state(u'Updating') + LOGGER.info('Course import %s: Extracted file verified', courselike_key) + self.status.set_state('Updating') self.status.increment_completed_steps() courselike_items = import_func( @@ -535,32 +535,32 @@ def import_olx(self, user_id, course_key_string, archive_path, archive_name, lan ) new_location = courselike_items[0].location - LOGGER.debug(u'new course at %s', new_location) + LOGGER.debug('new course at %s', new_location) - LOGGER.info(u'Course import %s: Course import successful', courselike_key) + LOGGER.info('Course import %s: Course import successful', courselike_key) except Exception as exception: # pylint: disable=broad-except - LOGGER.exception(u'error importing course', exc_info=True) - self.status.fail(text_type(exception)) + LOGGER.exception('error importing course', exc_info=True) + self.status.fail(str(exception)) finally: if course_dir.isdir(): shutil.rmtree(course_dir) - LOGGER.info(u'Course import %s: Temp data cleared', courselike_key) + LOGGER.info('Course import %s: Temp data cleared', courselike_key) - if self.status.state == u'Updating' and is_course: + if self.status.state == 'Updating' and is_course: # Reload the course so we have the latest state course = modulestore().get_course(courselike_key) if course.entrance_exam_enabled: entrance_exam_chapter = modulestore().get_items( course.id, - qualifiers={u'category': u'chapter'}, - settings={u'is_entrance_exam': True} + qualifiers={'category': 'chapter'}, + settings={'is_entrance_exam': True} )[0] - metadata = {u'entrance_exam_id': text_type(entrance_exam_chapter.location)} + metadata = {'entrance_exam_id': str(entrance_exam_chapter.location)} CourseMetadata.update_from_dict(metadata, course, user) from .views.entrance_exam import add_entrance_exam_milestone add_entrance_exam_milestone(course.id, entrance_exam_chapter) - LOGGER.info(u'Course %s Entrance exam imported', course.id) + LOGGER.info('Course %s Entrance exam imported', course.id) @shared_task diff --git a/cms/djangoapps/contentstore/toggles.py b/cms/djangoapps/contentstore/toggles.py index fe18f19714..899ccabb52 100644 --- a/cms/djangoapps/contentstore/toggles.py +++ b/cms/djangoapps/contentstore/toggles.py @@ -1,7 +1,7 @@ """ CMS feature toggles. """ -from edx_toggles.toggles import SettingDictToggle, LegacyWaffleFlag, LegacyWaffleFlagNamespace +from edx_toggles.toggles import LegacyWaffleFlag, LegacyWaffleFlagNamespace, SettingDictToggle # .. toggle_name: FEATURES['ENABLE_EXPORT_GIT'] # .. toggle_implementation: SettingDictToggle @@ -20,7 +20,7 @@ EXPORT_GIT = SettingDictToggle( # Namespace for studio dashboard waffle flags. WAFFLE_NAMESPACE = 'contentstore' -WAFFLE_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name=WAFFLE_NAMESPACE, log_prefix=u'Contentstore: ') +WAFFLE_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name=WAFFLE_NAMESPACE, log_prefix='Contentstore: ') # Waffle flag to split library to new view. # .. toggle_name: split_library_on_studio_dashboard diff --git a/cms/djangoapps/contentstore/utils.py b/cms/djangoapps/contentstore/utils.py index 1091dda5cc..a12881f844 100644 --- a/cms/djangoapps/contentstore/utils.py +++ b/cms/djangoapps/contentstore/utils.py @@ -7,7 +7,6 @@ import logging from contextlib import contextmanager from datetime import datetime -import six from django.conf import settings from django.urls import reverse from django.utils import translation @@ -15,17 +14,16 @@ from django.utils.translation import ugettext as _ from opaque_keys.edx.keys import CourseKey, UsageKey from opaque_keys.edx.locator import LibraryLocator from pytz import UTC -from six import text_type +from common.djangoapps.student import auth +from common.djangoapps.student.models import CourseEnrollment +from common.djangoapps.student.roles import CourseInstructorRole, CourseStaffRole from openedx.core.djangoapps.django_comment_common.models import assign_default_role from openedx.core.djangoapps.django_comment_common.utils import seed_permissions_roles from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangoapps.site_configuration.models import SiteConfiguration from openedx.features.content_type_gating.models import ContentTypeGatingConfig from openedx.features.content_type_gating.partitions import CONTENT_TYPE_GATING_SCHEME -from common.djangoapps.student import auth -from common.djangoapps.student.models import CourseEnrollment -from common.djangoapps.student.roles import CourseInstructorRole, CourseStaffRole from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.django import modulestore from xmodule.modulestore.exceptions import ItemNotFoundError @@ -100,7 +98,7 @@ def _remove_instructors(course_key): try: remove_all_instructors(course_key) except Exception as err: # lint-amnesty, pylint: disable=broad-except - log.error(u"Error in deleting course groups for {0}: {1}".format(course_key, err)) + log.error(f"Error in deleting course groups for {course_key}: {err}") def get_lms_link_for_item(location, preview=False): @@ -132,10 +130,10 @@ def get_lms_link_for_item(location, preview=False): settings.FEATURES.get('PREVIEW_LMS_BASE') ) - return u"//{lms_base}/courses/{course_key}/jump_to/{location}".format( + return "//{lms_base}/courses/{course_key}/jump_to/{location}".format( lms_base=lms_base, - course_key=text_type(location.course_key), - location=text_type(location), + course_key=str(location.course_key), + location=str(location), ) @@ -151,9 +149,9 @@ def get_lms_link_for_certificate_web_view(course_key, mode): if lms_base is None: return None - return u"//{certificate_web_base}/certificates/course/{course_id}?preview={mode}".format( + return "//{certificate_web_base}/certificates/course/{course_id}?preview={mode}".format( certificate_web_base=lms_base, - course_id=six.text_type(course_key), + course_id=str(course_key), mode=mode ) @@ -292,7 +290,7 @@ def reverse_url(handler_name, key_name=None, key_value=None, kwargs=None): Creates the URL for the given handler. The optional key_name and key_value are passed in as kwargs to the handler. """ - kwargs_for_reverse = {key_name: six.text_type(key_value)} if key_name else None + kwargs_for_reverse = {key_name: str(key_value)} if key_name else None if kwargs: kwargs_for_reverse.update(kwargs) return reverse(handler_name, kwargs=kwargs_for_reverse) @@ -332,7 +330,7 @@ def get_split_group_display_name(xblock, course): """ for user_partition in get_user_partition_info(xblock, schemes=['random'], course=course): for group in user_partition['groups']: - if u'Group ID {group_id}'.format(group_id=group['id']) == xblock.display_name_with_default: + if 'Group ID {group_id}'.format(group_id=group['id']) == xblock.display_name_with_default: return group['name'] @@ -400,7 +398,7 @@ def get_user_partition_info(xblock, schemes=None, course=None): if course is None: log.warning( - u"Could not find course %s to retrieve user partition information", + "Could not find course %s to retrieve user partition information", xblock.location.course_key ) return [] @@ -432,7 +430,7 @@ def get_user_partition_info(xblock, schemes=None, course=None): }) # Next, add any groups set on the XBlock that have been deleted - all_groups = set(g.id for g in p.groups) + all_groups = {g.id for g in p.groups} missing_group_ids = selected_groups - all_groups for gid in missing_group_ids: groups.append({ @@ -445,7 +443,7 @@ def get_user_partition_info(xblock, schemes=None, course=None): # Put together the entire partition dictionary partitions.append({ "id": p.id, - "name": six.text_type(p.name), # Convert into a string in case ugettext_lazy was used + "name": str(p.name), # Convert into a string in case ugettext_lazy was used "scheme": p.scheme.name, "groups": groups, }) @@ -502,7 +500,7 @@ def get_visibility_partition_info(xblock, course=None): else: # Translators: This is building up a list of groups. It is marked for translation because of the # comma, which is used as a separator between each group. - selected_groups_label = _(u'{previous_groups}, {current_group}').format( + selected_groups_label = _('{previous_groups}, {current_group}').format( previous_groups=selected_groups_label, current_group=group['name'] ) @@ -527,7 +525,7 @@ def get_xblock_aside_instance(usage_key): if aside.scope_ids.block_type == usage_key.aside_type: return aside except ItemNotFoundError: - log.warning(u'Unable to load item %s', usage_key.usage_key) + log.warning('Unable to load item %s', usage_key.usage_key) def is_self_paced(course): @@ -567,7 +565,7 @@ def get_sibling_urls(subsection): # section.get_parent SHOULD return the course, but for some reason, it might not sections = section.get_parent().get_children() except AttributeError: - log.error(u"URL Retrieval Error # 1: subsection {subsection} included in section {section}".format( + log.error("URL Retrieval Error # 1: subsection {subsection} included in section {section}".format( section=section.location, subsection=subsection.location )) @@ -583,7 +581,7 @@ def get_sibling_urls(subsection): try: sections = section.get_parent().get_children() except AttributeError: - log.error(u"URL Retrieval Error # 2: subsection {subsection} included in section {section}".format( + log.error("URL Retrieval Error # 2: subsection {subsection} included in section {section}".format( section=section.location, subsection=subsection.location )) diff --git a/cms/djangoapps/contentstore/video_utils.py b/cms/djangoapps/contentstore/video_utils.py index 887b9d2ac3..a0b382ebd1 100644 --- a/cms/djangoapps/contentstore/video_utils.py +++ b/cms/djangoapps/contentstore/video_utils.py @@ -1,19 +1,17 @@ -#-*- coding: utf-8 -*- """ Utils related to the videos. """ import logging +from urllib.parse import urljoin import requests -import six from django.conf import settings from django.core.files.images import get_image_dimensions from django.core.files.uploadedfile import SimpleUploadedFile from django.utils.translation import ugettext as _ from edxval.api import get_course_video_image_url, update_video_image -from six.moves.urllib.parse import urljoin # Youtube thumbnail sizes. # https://img.youtube.com/vi/{youtube_id}/{thumbnail_quality}.jpg @@ -43,15 +41,15 @@ def validate_video_image(image_file, skip_aspect_ratio=False): if not all(hasattr(image_file, attr) for attr in ['name', 'content_type', 'size']): error = _('The image must have name, content type, and size information.') elif image_file.content_type not in list(settings.VIDEO_IMAGE_SUPPORTED_FILE_FORMATS.values()): - error = _(u'This image file type is not supported. Supported file types are {supported_file_formats}.').format( + error = _('This image file type is not supported. Supported file types are {supported_file_formats}.').format( supported_file_formats=list(settings.VIDEO_IMAGE_SUPPORTED_FILE_FORMATS.keys()) ) elif image_file.size > settings.VIDEO_IMAGE_SETTINGS['VIDEO_IMAGE_MAX_BYTES']: - error = _(u'This image file must be smaller than {image_max_size}.').format( + error = _('This image file must be smaller than {image_max_size}.').format( image_max_size=settings.VIDEO_IMAGE_MAX_FILE_SIZE_MB ) elif image_file.size < settings.VIDEO_IMAGE_SETTINGS['VIDEO_IMAGE_MIN_BYTES']: - error = _(u'This image file must be larger than {image_min_size}.').format( + error = _('This image file must be larger than {image_min_size}.').format( image_min_size=settings.VIDEO_IMAGE_MIN_FILE_SIZE_KB ) else: @@ -63,15 +61,15 @@ def validate_video_image(image_file, skip_aspect_ratio=False): return _('There is a problem with this image file. Try to upload a different file.') image_file_aspect_ratio = abs(image_file_width / float(image_file_height) - settings.VIDEO_IMAGE_ASPECT_RATIO) if image_file_width < settings.VIDEO_IMAGE_MIN_WIDTH or image_file_height < settings.VIDEO_IMAGE_MIN_HEIGHT: - error = _(u'Recommended image resolution is {image_file_max_width}x{image_file_max_height}. ' - u'The minimum resolution is {image_file_min_width}x{image_file_min_height}.').format( + error = _('Recommended image resolution is {image_file_max_width}x{image_file_max_height}. ' + 'The minimum resolution is {image_file_min_width}x{image_file_min_height}.').format( image_file_max_width=settings.VIDEO_IMAGE_MAX_WIDTH, image_file_max_height=settings.VIDEO_IMAGE_MAX_HEIGHT, image_file_min_width=settings.VIDEO_IMAGE_MIN_WIDTH, image_file_min_height=settings.VIDEO_IMAGE_MIN_HEIGHT ) elif not skip_aspect_ratio and image_file_aspect_ratio > settings.VIDEO_IMAGE_ASPECT_RATIO_ERROR_MARGIN: - error = _(u'This image file must have an aspect ratio of {video_image_aspect_ratio_text}.').format( + error = _('This image file must have an aspect ratio of {video_image_aspect_ratio_text}.').format( video_image_aspect_ratio_text=settings.VIDEO_IMAGE_ASPECT_RATIO_TEXT ) else: @@ -108,7 +106,7 @@ def validate_and_update_video_image(course_key_string, edx_video_id, image_file, error = validate_video_image(image_file, skip_aspect_ratio=True) if error: LOGGER.info( - u'VIDEOS: Scraping youtube video thumbnail failed for edx_video_id [%s] in course [%s] with error: %s', + 'VIDEOS: Scraping youtube video thumbnail failed for edx_video_id [%s] in course [%s] with error: %s', edx_video_id, course_key_string, error @@ -117,7 +115,7 @@ def validate_and_update_video_image(course_key_string, edx_video_id, image_file, update_video_image(edx_video_id, course_key_string, image_file, image_filename) LOGGER.info( - u'VIDEOS: Scraping youtube video thumbnail for edx_video_id [%s] in course [%s]', edx_video_id, course_key_string # lint-amnesty, pylint: disable=line-too-long + 'VIDEOS: Scraping youtube video thumbnail for edx_video_id [%s] in course [%s]', edx_video_id, course_key_string # lint-amnesty, pylint: disable=line-too-long ) @@ -128,7 +126,7 @@ def scrape_youtube_thumbnail(course_id, edx_video_id, youtube_id): # Scrape when course video image does not exist for edx_video_id. if not get_course_video_image_url(course_id, edx_video_id): thumbnail_content, thumbnail_content_type = download_youtube_video_thumbnail(youtube_id) - supported_content_types = {v: k for k, v in six.iteritems(settings.VIDEO_IMAGE_SUPPORTED_FILE_FORMATS)} + supported_content_types = {v: k for k, v in settings.VIDEO_IMAGE_SUPPORTED_FILE_FORMATS.items()} image_filename = '{youtube_id}{image_extention}'.format( youtube_id=youtube_id, image_extention=supported_content_types.get(