BOM-2369 (D): pyupgrade on contentstore/views (#26767)

* pyupgrade on contentstore/views

* Apply suggestions from code review

Co-authored-by: Usama Sadiq <usama.sadiq@arbisoft.com>

Co-authored-by: Usama Sadiq <usama.sadiq@arbisoft.com>
This commit is contained in:
M. Zulqarnain
2021-03-05 14:55:14 +05:00
committed by GitHub
parent efe4041ef7
commit 41e5403f4e
44 changed files with 926 additions and 983 deletions

View File

@@ -7,7 +7,6 @@ import math
import re
from functools import partial
import six
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
@@ -17,13 +16,12 @@ from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.decorators.http import require_http_methods, require_POST
from opaque_keys.edx.keys import AssetKey, CourseKey
from pymongo import ASCENDING, DESCENDING
from six import text_type
from common.djangoapps.edxmako.shortcuts import render_to_response
from openedx.core.djangoapps.contentserver.caching import del_cached_content
from common.djangoapps.student.auth import has_course_author_access
from common.djangoapps.util.date_utils import get_default_time_display
from common.djangoapps.util.json_request import JsonResponse
from openedx.core.djangoapps.contentserver.caching import del_cached_content
from xmodule.contentstore.content import StaticContent
from xmodule.contentstore.django import contentstore
from xmodule.exceptions import NotFoundError
@@ -206,9 +204,9 @@ def _get_error_if_invalid_parameters(requested_filter):
if invalid_filters:
error_message = {
'error_code': 'invalid_asset_type_filter',
'developer_message': u'The asset_type parameter to the request is invalid. '
u'The {} filters are not described in the settings.FILES_AND_UPLOAD_TYPE_FILTERS '
u'dictionary.'.format(invalid_filters)
'developer_message': 'The asset_type parameter to the request is invalid. '
'The {} filters are not described in the settings.FILES_AND_UPLOAD_TYPE_FILTERS '
'dictionary.'.format(invalid_filters)
}
return JsonResponse({'error': error_message}, status=400)
@@ -414,7 +412,7 @@ def _upload_asset(request, course_key):
try:
content = update_course_run_asset(course_key, upload_file)
except AssetSizeTooLargeException as exception:
return JsonResponse({'error': text_type(exception)}, status=413)
return JsonResponse({'error': str(exception)}, status=413)
# readback the saved content - we need the database timestamp
readback = contentstore().find(content.location)
@@ -436,7 +434,7 @@ def _get_error_if_course_does_not_exist(course_key): # lint-amnesty, pylint: di
try:
modulestore().get_course(course_key)
except ItemNotFoundError:
logging.error(u'Could not find course: %s', course_key)
logging.error('Could not find course: %s', course_key)
return HttpResponseBadRequest()
@@ -472,8 +470,8 @@ def _get_file_too_large_error_message(filename):
"""returns formatted error message for large files"""
return _(
u'File {filename} exceeds maximum size of '
u'{maximum_size_in_megabytes} MB.'
'File {filename} exceeds maximum size of '
'{maximum_size_in_megabytes} MB.'
).format(
filename=filename,
maximum_size_in_megabytes=settings.MAX_ASSET_UPLOAD_FILE_SIZE_IN_MB,
@@ -585,7 +583,7 @@ def _delete_thumbnail(thumbnail_location, course_key, asset_key): # lint-amnest
contentstore().delete(thumbnail_content.get_id())
del_cached_content(thumbnail_location)
except Exception: # pylint: disable=broad-except
logging.warning(u'Could not delete thumbnail: %s', thumbnail_location)
logging.warning('Could not delete thumbnail: %s', thumbnail_location)
def _get_asset_json(display_name, content_type, date, location, thumbnail_location, locked):
@@ -604,5 +602,5 @@ def _get_asset_json(display_name, content_type, date, location, thumbnail_locati
'thumbnail': StaticContent.serialize_asset_key_with_slash(thumbnail_location) if thumbnail_location else None,
'locked': locked,
# needed for Backbone delete/update.
'id': six.text_type(location)
'id': str(location)
}

View File

@@ -26,7 +26,6 @@ course.certificates: {
import json
import logging
import six
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
@@ -37,7 +36,6 @@ from django.views.decorators.http import require_http_methods
from eventtracking import tracker
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import AssetKey, CourseKey
from six import text_type
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.edxmako.shortcuts import render_to_response
@@ -87,7 +85,7 @@ def _delete_asset(course_key, asset_key_string):
except InvalidKeyError:
# Unable to parse the asset key, log and return
LOGGER.info(
u"In course %r, unable to parse asset key %r, not attempting to delete signatory.",
"In course %r, unable to parse asset key %r, not attempting to delete signatory.",
course_key,
asset_key_string,
)
@@ -95,7 +93,7 @@ def _delete_asset(course_key, asset_key_string):
else:
# Unable to parse the asset key, log and return
LOGGER.info(
u"In course %r, unable to parse asset key %r, not attempting to delete signatory.",
"In course %r, unable to parse asset key %r, not attempting to delete signatory.",
course_key,
asset_key_string,
)
@@ -123,7 +121,7 @@ class CertificateValidationError(CertificateException):
pass # lint-amnesty, pylint: disable=unnecessary-pass
class CertificateManager(object):
class CertificateManager:
"""
The CertificateManager is responsible for storage, retrieval, and manipulation of Certificates
Certificates are not stored in the Django ORM, they are a field/setting on the course descriptor
@@ -153,7 +151,7 @@ class CertificateManager(object):
# Ensure the schema version meets our expectations
if certificate_data.get("version") != CERTIFICATE_SCHEMA_VERSION:
raise TypeError(
u"Unsupported certificate schema version: {0}. Expected version: {1}.".format(
"Unsupported certificate schema version: {}. Expected version: {}.".format(
certificate_data.get("version"),
CERTIFICATE_SCHEMA_VERSION
)
@@ -238,13 +236,13 @@ class CertificateManager(object):
Deserialize from a JSON representation into a Certificate object.
'value' should be either a Certificate instance, or a valid JSON string
"""
if not six.PY2 and isinstance(value, bytes):
if isinstance(value, bytes):
value = value.decode('utf-8')
# Ensure the schema fieldset meets our expectations
for key in ("name", "description", "version"):
if key not in value:
raise CertificateValidationError(_(u"Certificate dict {0} missing value key '{1}'").format(value, key))
raise CertificateValidationError(_("Certificate dict {0} missing value key '{1}'").format(value, key))
# Load up the Certificate data
certificate_data = CertificateManager.parse(value)
@@ -314,7 +312,7 @@ class CertificateManager(object):
tracker.emit(event_name, event_data)
class Certificate(object):
class Certificate:
"""
The logical representation of an individual course certificate
"""
@@ -349,7 +347,7 @@ def certificate_activation_handler(request, course_key_string):
try:
course = _get_course_and_check_access(course_key, request.user)
except PermissionDenied:
msg = _(u'PermissionDenied: Failed in authenticating {user}').format(user=request.user)
msg = _('PermissionDenied: Failed in authenticating {user}').format(user=request.user)
return JsonResponse({"error": msg}, status=403)
data = json.loads(request.body.decode('utf8'))
@@ -364,7 +362,7 @@ def certificate_activation_handler(request, course_key_string):
store.update_item(course, request.user.id)
cert_event_type = 'activated' if is_active else 'deactivated'
CertificateManager.track_event(cert_event_type, {
'course_id': six.text_type(course.id),
'course_id': str(course.id),
})
return HttpResponse(status=200)
@@ -387,7 +385,7 @@ def certificates_list_handler(request, course_key_string):
try:
course = _get_course_and_check_access(course_key, request.user)
except PermissionDenied:
msg = _(u'PermissionDenied: Failed in authenticating {user}').format(user=request.user)
msg = _('PermissionDenied: Failed in authenticating {user}').format(user=request.user)
return JsonResponse({"error": msg}, status=403)
if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'):
@@ -442,7 +440,7 @@ def certificates_list_handler(request, course_key_string):
try:
new_certificate = CertificateManager.deserialize_certificate(course, request.body)
except CertificateValidationError as err:
return JsonResponse({"error": text_type(err)}, status=400)
return JsonResponse({"error": str(err)}, status=400)
if course.certificates.get('certificates') is None:
course.certificates['certificates'] = []
course.certificates['certificates'].append(new_certificate.certificate_data)
@@ -454,7 +452,7 @@ def certificates_list_handler(request, course_key_string):
)
store.update_item(course, request.user.id)
CertificateManager.track_event('created', {
'course_id': six.text_type(course.id),
'course_id': str(course.id),
'configuration_id': new_certificate.id
})
course = _get_course_and_check_access(course_key, request.user)
@@ -499,7 +497,7 @@ def certificates_detail_handler(request, course_key_string, certificate_id):
try:
new_certificate = CertificateManager.deserialize_certificate(course, request.body)
except CertificateValidationError as err:
return JsonResponse({"error": text_type(err)}, status=400)
return JsonResponse({"error": str(err)}, status=400)
serialized_certificate = CertificateManager.serialize_certificate(new_certificate)
cert_event_type = 'created'
@@ -511,7 +509,7 @@ def certificates_detail_handler(request, course_key_string, certificate_id):
store.update_item(course, request.user.id)
CertificateManager.track_event(cert_event_type, {
'course_id': six.text_type(course.id),
'course_id': str(course.id),
'configuration_id': serialized_certificate["id"]
})
return JsonResponse(serialized_certificate, status=201)
@@ -533,7 +531,7 @@ def certificates_detail_handler(request, course_key_string, certificate_id):
certificate_id=certificate_id
)
CertificateManager.track_event('deleted', {
'course_id': six.text_type(course.id),
'course_id': str(course.id),
'configuration_id': certificate_id
})
return JsonResponse(status=204)

View File

@@ -4,8 +4,8 @@ Studio component views
import logging
from urllib.parse import quote_plus
import six
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
@@ -14,7 +14,6 @@ from django.utils.translation import ugettext as _
from django.views.decorators.http import require_GET
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import UsageKey
from six.moves.urllib.parse import quote_plus
from xblock.core import XBlock
from xblock.django.request import django_to_webob_request, webob_to_django_response
from xblock.exceptions import NoSuchHandlerError
@@ -22,10 +21,10 @@ from xblock.plugin import PluginMissingError
from xblock.runtime import Mixologist
from common.djangoapps.edxmako.shortcuts import render_to_response
from openedx.core.lib.xblock_utils import get_aside_from_xblock, is_xblock_aside
from common.djangoapps.student.auth import has_course_author_access
from common.djangoapps.xblock_django.api import authorable_xblocks, disabled_xblocks
from common.djangoapps.xblock_django.models import XBlockStudioConfigurationFlag
from openedx.core.lib.xblock_utils import get_aside_from_xblock, is_xblock_aside
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
@@ -43,7 +42,7 @@ log = logging.getLogger(__name__)
# NOTE: This list is disjoint from ADVANCED_COMPONENT_TYPES
COMPONENT_TYPES = ['discussion', 'html', 'openassessment', 'problem', 'video']
ADVANCED_COMPONENT_TYPES = sorted(set(name for name, class_ in XBlock.load_classes()) - set(COMPONENT_TYPES))
ADVANCED_COMPONENT_TYPES = sorted({name for name, class_ in XBlock.load_classes()} - set(COMPONENT_TYPES))
ADVANCED_PROBLEM_TYPES = settings.ADVANCED_PROBLEM_TYPES
@@ -155,10 +154,10 @@ def container_handler(request, usage_key_string):
assert unit is not None, "Could not determine unit page"
subsection = get_parent_xblock(unit)
assert subsection is not None, "Could not determine parent subsection from unit " + six.text_type(
assert subsection is not None, "Could not determine parent subsection from unit " + str(
unit.location)
section = get_parent_xblock(subsection)
assert section is not None, "Could not determine ancestor section from unit " + six.text_type(unit.location)
assert section is not None, "Could not determine ancestor section from unit " + str(unit.location)
# for the sequence navigator
prev_url, next_url = get_sibling_urls(subsection)
@@ -270,7 +269,7 @@ def get_component_templates(courselike, library=False): # lint-amnesty, pylint:
return {
"show_legend": XBlockStudioConfigurationFlag.is_enabled(),
"allow_unsupported_xblocks": allow_unsupported,
"documentation_label": _(u"{platform_name} Support Levels:").format(platform_name=settings.PLATFORM_NAME)
"documentation_label": _("{platform_name} Support Levels:").format(platform_name=settings.PLATFORM_NAME)
}
component_display_names = {
@@ -367,7 +366,7 @@ def get_component_templates(courselike, library=False): # lint-amnesty, pylint:
try:
component_display_name = xblock_type_display_name(component)
except PluginMissingError:
log.warning(u'Unable to load xblock type %s to read display_name', component, exc_info=True)
log.warning('Unable to load xblock type %s to read display_name', component, exc_info=True)
else:
templates_for_category.append(
create_template_dict(
@@ -425,12 +424,12 @@ def get_component_templates(courselike, library=False): # lint-amnesty, pylint:
# prevents any authors from trying to instantiate the
# non-existent component type by not showing it in the menu
log.warning(
u"Advanced component %s does not exist. It will not be added to the Studio new component menu.",
"Advanced component %s does not exist. It will not be added to the Studio new component menu.",
category
)
else:
log.error(
u"Improper format for course advanced keys! %s",
"Improper format for course advanced keys! %s",
course_advanced_keys
)
if advanced_component_templates['templates']:
@@ -503,7 +502,7 @@ def component_handler(request, usage_key_string, handler, suffix=''):
handler_descriptor.xmodule_runtime = StudioEditModuleRuntime(request.user)
resp = handler_descriptor.handle(handler, req, suffix)
except NoSuchHandlerError:
log.info(u"XBlock %s attempted to access missing handler %r", handler_descriptor, handler, exc_info=True)
log.info("XBlock %s attempted to access missing handler %r", handler_descriptor, handler, exc_info=True)
raise Http404 # lint-amnesty, pylint: disable=raise-missing-from
# unintentional update to handle any side effects of handle call

View File

@@ -1,6 +1,7 @@
"""
Views related to operations on course objects
"""
# pylint: disable=filter-builtin-not-iterating
import copy
@@ -12,7 +13,6 @@ import string
from collections import defaultdict
import django.utils
import six
from ccx_keys.locator import CCXLocator
from django.conf import settings
from django.contrib.auth.decorators import login_required
@@ -23,6 +23,7 @@ from django.urls import reverse
from django.utils.translation import ugettext as _
from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.decorators.http import require_GET, require_http_methods
from edx_django_utils.monitoring import function_trace
from edx_toggles.toggles import LegacyWaffleSwitchNamespace
from milestones import api as milestones_api
from opaque_keys import InvalidKeyError
@@ -30,9 +31,6 @@ from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locator import BlockUsageLocator
from organizations.api import add_organization_course, ensure_organization
from organizations.exceptions import InvalidOrganizationException
from six import text_type
from six.moves import filter
from edx_django_utils.monitoring import function_trace
from cms.djangoapps.course_creators.views import add_user_with_status_unrequested, get_course_creator_status
from cms.djangoapps.models.settings.course_grading import CourseGradingModel
@@ -42,6 +40,27 @@ from common.djangoapps.course_action_state.managers import CourseActionStateItem
from common.djangoapps.course_action_state.models import CourseRerunState, CourseRerunUIStateManager
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.edxmako.shortcuts import render_to_response
from common.djangoapps.student import auth
from common.djangoapps.student.auth import has_course_author_access, has_studio_read_access, has_studio_write_access
from common.djangoapps.student.roles import (
CourseCreatorRole,
CourseInstructorRole,
CourseStaffRole,
GlobalStaff,
UserBasedRole
)
from common.djangoapps.util.course import get_link_for_about_page
from common.djangoapps.util.date_utils import get_default_time_display
from common.djangoapps.util.json_request import JsonResponse, JsonResponseBadRequest, expect_json
from common.djangoapps.util.milestones_helpers import (
is_prerequisite_courses_enabled,
is_valid_course_key,
remove_prerequisite_course,
set_prerequisite_courses
)
from common.djangoapps.util.string_utils import _has_non_ascii_characters
from common.djangoapps.xblock_django.api import deprecated_xblocks
from openedx.core import toggles as core_toggles
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.credit.api import get_credit_requirements, is_credit_course
from openedx.core.djangoapps.credit.tasks import update_credit_course_requirements
@@ -54,23 +73,6 @@ from openedx.features.content_type_gating.models import ContentTypeGatingConfig
from openedx.features.content_type_gating.partitions import CONTENT_TYPE_GATING_SCHEME
from openedx.features.course_experience.waffle import ENABLE_COURSE_ABOUT_SIDEBAR_HTML
from openedx.features.course_experience.waffle import waffle as course_experience_waffle
from common.djangoapps.student import auth
from common.djangoapps.student.auth import has_course_author_access, has_studio_read_access, has_studio_write_access
from common.djangoapps.student.roles import (
CourseCreatorRole, CourseInstructorRole, CourseStaffRole, GlobalStaff, UserBasedRole
)
from common.djangoapps.util.course import get_link_for_about_page
from common.djangoapps.util.date_utils import get_default_time_display
from common.djangoapps.util.json_request import JsonResponse, JsonResponseBadRequest, expect_json
from common.djangoapps.util.milestones_helpers import (
is_prerequisite_courses_enabled,
is_valid_course_key,
remove_prerequisite_course,
set_prerequisite_courses
)
from openedx.core import toggles as core_toggles
from common.djangoapps.util.string_utils import _has_non_ascii_characters
from common.djangoapps.xblock_django.api import deprecated_xblocks
from xmodule.contentstore.content import StaticContent
from xmodule.course_module import DEFAULT_START_DATE, CourseFields
from xmodule.error_module import ErrorBlock
@@ -112,7 +114,6 @@ from .library import (
should_redirect_to_library_authoring_mfe
)
log = logging.getLogger(__name__)
@@ -403,7 +404,7 @@ def _accessible_courses_summary_iter(request, org=None):
courses_summary = [] if org == '' else CourseOverview.get_all_courses(orgs=[org])
else:
courses_summary = modulestore().get_course_summaries()
courses_summary = six.moves.filter(course_filter, courses_summary)
courses_summary = filter(course_filter, courses_summary)
in_process_course_actions = get_in_process_course_actions(request)
return courses_summary, in_process_course_actions
@@ -430,7 +431,7 @@ def _accessible_courses_iter(request):
return has_studio_read_access(request.user, course.id)
courses = six.moves.filter(course_filter, modulestore().get_courses())
courses = filter(course_filter, modulestore().get_courses())
in_process_course_actions = get_in_process_course_actions(request)
return courses, in_process_course_actions
@@ -458,7 +459,7 @@ def _accessible_courses_iter_for_tests(request):
return has_studio_read_access(request.user, course.id)
courses = six.moves.filter(course_filter, modulestore().get_course_summaries())
courses = filter(course_filter, modulestore().get_course_summaries())
in_process_course_actions = get_in_process_course_actions(request)
return courses, in_process_course_actions
@@ -516,7 +517,7 @@ def course_listing(request):
"""
optimization_enabled = GlobalStaff().has_user(request.user) and \
LegacyWaffleSwitchNamespace(name=WAFFLE_NAMESPACE).is_enabled(u'enable_global_staff_optimization')
LegacyWaffleSwitchNamespace(name=WAFFLE_NAMESPACE).is_enabled('enable_global_staff_optimization')
org = request.GET.get('org', '') if optimization_enabled else None
courses_iter, in_process_course_actions = get_courses_accessible_to_user(request, org)
@@ -530,27 +531,27 @@ def course_listing(request):
Return a dict of the data which the view requires for each unsucceeded course
"""
return {
u'display_name': uca.display_name,
u'course_key': six.text_type(uca.course_key),
u'org': uca.course_key.org,
u'number': uca.course_key.course,
u'run': uca.course_key.run,
u'is_failed': True if uca.state == CourseRerunUIStateManager.State.FAILED else False, # lint-amnesty, pylint: disable=simplifiable-if-expression
u'is_in_progress': True if uca.state == CourseRerunUIStateManager.State.IN_PROGRESS else False, # lint-amnesty, pylint: disable=simplifiable-if-expression
u'dismiss_link': reverse_course_url(
u'course_notifications_handler',
'display_name': uca.display_name,
'course_key': str(uca.course_key),
'org': uca.course_key.org,
'number': uca.course_key.course,
'run': uca.course_key.run,
'is_failed': uca.state == CourseRerunUIStateManager.State.FAILED,
'is_in_progress': uca.state == CourseRerunUIStateManager.State.IN_PROGRESS,
'dismiss_link': reverse_course_url(
'course_notifications_handler',
uca.course_key,
kwargs={
u'action_state_id': uca.id,
'action_state_id': uca.id,
},
) if uca.state == CourseRerunUIStateManager.State.FAILED else u''
) if uca.state == CourseRerunUIStateManager.State.FAILED else ''
}
split_archived = settings.FEATURES.get(u'ENABLE_SEPARATE_ARCHIVED_COURSES', False)
split_archived = settings.FEATURES.get('ENABLE_SEPARATE_ARCHIVED_COURSES', False)
active_courses, archived_courses = _process_courses_list(courses_iter, in_process_course_actions, split_archived)
in_process_course_actions = [format_in_process_course_view(uca) for uca in in_process_course_actions]
return render_to_response(u'index.html', {
return render_to_response('index.html', {
'courses': active_courses,
'split_studio_home': split_library_view_on_dashboard(),
'archived_courses': archived_courses,
@@ -564,8 +565,8 @@ def course_listing(request):
'request_course_creator_url': reverse('request_course_creator'),
'course_creator_status': _get_course_creator_status(user),
'rerun_creator_status': GlobalStaff().has_user(user),
'allow_unicode_course_id': settings.FEATURES.get(u'ALLOW_UNICODE_COURSE_ID', False),
'allow_course_reruns': settings.FEATURES.get(u'ALLOW_COURSE_RERUNS', True),
'allow_unicode_course_id': settings.FEATURES.get('ALLOW_UNICODE_COURSE_ID', False),
'allow_course_reruns': settings.FEATURES.get('ALLOW_COURSE_RERUNS', True),
'optimization_enabled': optimization_enabled,
'active_tab': 'courses'
})
@@ -589,7 +590,7 @@ def library_listing(request):
'course_creator_status': _get_course_creator_status(request.user),
'allow_unicode_course_id': settings.FEATURES.get('ALLOW_UNICODE_COURSE_ID', False),
'archived_courses': True,
'allow_course_reruns': settings.FEATURES.get(u'ALLOW_COURSE_RERUNS', True),
'allow_course_reruns': settings.FEATURES.get('ALLOW_COURSE_RERUNS', True),
'rerun_creator_status': GlobalStaff().has_user(request.user),
'split_studio_home': split_library_view_on_dashboard(),
'active_tab': 'libraries'
@@ -604,8 +605,8 @@ def _format_library_for_view(library, request):
return {
'display_name': library.display_name,
'library_key': six.text_type(library.location.library_key),
'url': reverse_library_url(u'library_handler', six.text_type(library.location.library_key)),
'library_key': str(library.location.library_key),
'url': reverse_library_url('library_handler', str(library.location.library_key)),
'org': library.display_org_with_default,
'number': library.display_number_with_default,
'can_edit': has_studio_write_access(request.user, library.location.library_key),
@@ -673,7 +674,7 @@ def course_index(request, course_key):
reindex_link = None
if settings.FEATURES.get('ENABLE_COURSEWARE_INDEX', False):
if GlobalStaff().has_user(request.user):
reindex_link = "/course/{course_id}/search_reindex".format(course_id=six.text_type(course_key))
reindex_link = "/course/{course_id}/search_reindex".format(course_id=str(course_key))
sections = course_module.get_children()
course_structure = _course_outline_json(request, course_module)
locator_to_show = request.GET.get('show', None)
@@ -774,7 +775,7 @@ def _process_courses_list(courses_iter, in_process_course_actions, split_archive
"""
return {
'display_name': course.display_name,
'course_key': six.text_type(course.location.course_key),
'course_key': str(course.location.course_key),
'url': reverse_course_url('course_handler', course.id),
'lms_link': get_lms_link_for_item(course.location),
'rerun_link': _get_rerun_link_for_item(course.id),
@@ -875,7 +876,7 @@ def _create_or_rerun_course(request):
# existing xml courses this cannot be changed in CourseBlock.
# # TODO get rid of defining wiki slug in this org/course/run specific way and reconcile
# w/ xmodule.course_module.CourseBlock.__init__
wiki_slug = u"{0}.{1}.{2}".format(org, course, run)
wiki_slug = f"{org}.{course}.{run}"
definition_data = {'wiki_slug': wiki_slug}
fields.update(definition_data)
@@ -885,17 +886,17 @@ def _create_or_rerun_course(request):
destination_course_key = rerun_course(request.user, source_course_key, org, course, run, fields)
return JsonResponse({
'url': reverse_url('course_handler'),
'destination_course_key': six.text_type(destination_course_key)
'destination_course_key': str(destination_course_key)
})
else:
try:
new_course = create_new_course(request.user, org, course, run, fields)
return JsonResponse({
'url': reverse_course_url('course_handler', new_course.id),
'course_key': six.text_type(new_course.id),
'course_key': str(new_course.id),
})
except ValidationError as ex:
return JsonResponse({'error': text_type(ex)}, status=400)
return JsonResponse({'error': str(ex)}, status=400)
except DuplicateCourseError:
return JsonResponse({
'ErrMsg': _(
@@ -912,7 +913,7 @@ def _create_or_rerun_course(request):
})
except InvalidKeyError as error:
return JsonResponse({
"ErrMsg": _(u"Unable to create course '{name}'.\n\n{err}").format(name=display_name, err=text_type(error))}
"ErrMsg": _("Unable to create course '{name}'.\n\n{err}").format(name=display_name, err=str(error))}
)
@@ -997,7 +998,7 @@ def rerun_course(user, source_course_key, org, number, run, fields, background=T
fields['video_upload_pipeline'] = {}
json_fields = json.dumps(fields, cls=EdxJSONEncoder)
args = [six.text_type(source_course_key), six.text_type(destination_course_key), user.id, json_fields]
args = [str(source_course_key), str(destination_course_key), user.id, json_fields]
if background:
rerun_course_task.delay(*args)
@@ -1306,7 +1307,7 @@ def grading_handler(request, course_key_string, grader_index=None):
# update credit course requirements if 'minimum_grade_credit'
# field value is changed
if 'minimum_grade_credit' in request.json:
update_credit_course_requirements.delay(six.text_type(course_key))
update_credit_course_requirements.delay(str(course_key))
# None implies update the whole model (cutoffs, graceperiod, and graders) not a specific grader
if grader_index is None:
@@ -1419,7 +1420,7 @@ def advanced_settings_handler(request, course_key_string):
# update the course tabs if required by any setting changes
_refresh_course_tabs(request, course_module)
except InvalidTabsException as err:
log.exception(text_type(err))
log.exception(str(err))
response_message = [
{
'message': _('An error occurred while trying to save your tabs'),
@@ -1438,7 +1439,7 @@ def advanced_settings_handler(request, course_key_string):
# Handle all errors that validation doesn't catch
except (TypeError, ValueError, InvalidTabsException) as err:
return HttpResponseBadRequest(
django.utils.html.escape(text_type(err)),
django.utils.html.escape(str(err)),
content_type="text/plain"
)
@@ -1476,7 +1477,7 @@ def validate_textbook_json(textbook):
"""
if isinstance(textbook, (bytes, bytearray)): # data appears as bytes
textbook = textbook.decode('utf-8')
if isinstance(textbook, six.string_types):
if isinstance(textbook, str):
try:
textbook = json.loads(textbook)
except ValueError:
@@ -1485,7 +1486,7 @@ def validate_textbook_json(textbook):
raise TextbookValidationError("must be JSON object")
if not textbook.get("tab_title"):
raise TextbookValidationError("must have tab_title")
tid = six.text_type(textbook.get("id", ""))
tid = str(textbook.get("id", ""))
if tid and not tid[0].isdigit():
raise TextbookValidationError("textbook ID must start with a digit")
return textbook
@@ -1544,9 +1545,9 @@ def textbooks_list_handler(request, course_key_string):
try:
textbooks = validate_textbooks_json(request.body)
except TextbookValidationError as err:
return JsonResponse({"error": text_type(err)}, status=400)
return JsonResponse({"error": str(err)}, status=400)
tids = set(t["id"] for t in textbooks if "id" in t)
tids = {t["id"] for t in textbooks if "id" in t}
for textbook in textbooks:
if "id" not in textbook:
tid = assign_textbook_id(textbook, tids)
@@ -1563,9 +1564,9 @@ def textbooks_list_handler(request, course_key_string):
try:
textbook = validate_textbook_json(request.body)
except TextbookValidationError as err:
return JsonResponse({"error": text_type(err)}, status=400)
return JsonResponse({"error": str(err)}, status=400)
if not textbook.get("id"):
tids = set(t["id"] for t in course.pdf_textbooks if "id" in t)
tids = {t["id"] for t in course.pdf_textbooks if "id" in t}
textbook["id"] = assign_textbook_id(textbook, tids)
existing = course.pdf_textbooks
existing.append(textbook)
@@ -1602,7 +1603,7 @@ def textbooks_detail_handler(request, course_key_string, textbook_id):
with store.bulk_operations(course_key):
course_module = get_course_and_check_access(course_key, request.user)
matching_id = [tb for tb in course_module.pdf_textbooks
if six.text_type(tb.get("id")) == six.text_type(textbook_id)]
if str(tb.get("id")) == str(textbook_id)]
if matching_id:
textbook = matching_id[0]
else:
@@ -1616,7 +1617,7 @@ def textbooks_detail_handler(request, course_key_string, textbook_id):
try:
new_textbook = validate_textbook_json(request.body)
except TextbookValidationError as err:
return JsonResponse({"error": text_type(err)}, status=400)
return JsonResponse({"error": str(err)}, status=400)
new_textbook["id"] = textbook_id
if textbook:
i = course_module.pdf_textbooks.index(textbook)
@@ -1761,7 +1762,7 @@ def group_configurations_list_handler(request, course_key_string):
try:
new_configuration = GroupConfiguration(request.body, course).get_user_partition()
except GroupConfigurationsValidationError as err:
return JsonResponse({"error": text_type(err)}, status=400)
return JsonResponse({"error": str(err)}, status=400)
course.user_partitions.append(new_configuration)
response = JsonResponse(new_configuration.to_json(), status=201)
@@ -1793,7 +1794,7 @@ def group_configurations_detail_handler(request, course_key_string, group_config
with store.bulk_operations(course_key):
course = get_course_and_check_access(course_key, request.user)
matching_id = [p for p in course.user_partitions
if six.text_type(p.id) == six.text_type(group_configuration_id)]
if str(p.id) == str(group_configuration_id)]
if matching_id:
configuration = matching_id[0]
else:
@@ -1803,7 +1804,7 @@ def group_configurations_detail_handler(request, course_key_string, group_config
try:
new_configuration = GroupConfiguration(request.body, course, group_configuration_id).get_user_partition() # lint-amnesty, pylint: disable=line-too-long
except GroupConfigurationsValidationError as err:
return JsonResponse({"error": text_type(err)}, status=400)
return JsonResponse({"error": str(err)}, status=400)
if configuration:
index = course.user_partitions.index(configuration)

View File

@@ -7,7 +7,6 @@ Intended to be utilized as an AJAX callback handler, versus a proper view/screen
import logging
from functools import wraps
import six
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, HttpResponseBadRequest
@@ -17,10 +16,10 @@ from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey, UsageKey
from cms.djangoapps.models.settings.course_metadata import CourseMetadata
from openedx.core.djangolib.js_utils import dump_js_escaped_json
from common.djangoapps.student.auth import has_course_author_access
from common.djangoapps.util import milestones_helpers
from openedx.core import toggles as core_toggles
from openedx.core.djangolib.js_utils import dump_js_escaped_json
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
@@ -132,7 +131,7 @@ def _create_entrance_exam(request, course_key, entrance_exam_minimum_score_pct=N
return HttpResponse(status=400)
# Create the entrance exam item (currently it's just a chapter)
parent_locator = six.text_type(course.location)
parent_locator = str(course.location)
created_block = create_xblock(
parent_locator=parent_locator,
user=request.user,
@@ -147,13 +146,13 @@ def _create_entrance_exam(request, course_key, entrance_exam_minimum_score_pct=N
metadata = {
'entrance_exam_enabled': True,
'entrance_exam_minimum_score_pct': entrance_exam_minimum_score_pct,
'entrance_exam_id': six.text_type(created_block.location),
'entrance_exam_id': str(created_block.location),
}
CourseMetadata.update_from_dict(metadata, course, request.user)
# Create the entrance exam section item.
create_xblock(
parent_locator=six.text_type(created_block.location),
parent_locator=str(created_block.location),
user=request.user,
category='sequential',
display_name=_('Entrance Exam - Subsection')
@@ -179,7 +178,7 @@ def _get_entrance_exam(request, course_key):
try:
exam_descriptor = modulestore().get_item(exam_key)
return HttpResponse( # lint-amnesty, pylint: disable=http-response-with-content-type-json
dump_js_escaped_json({'locator': six.text_type(exam_descriptor.location)}),
dump_js_escaped_json({'locator': str(exam_descriptor.location)}),
status=200, content_type='application/json')
except ItemNotFoundError:
return HttpResponse(status=404)
@@ -248,7 +247,7 @@ def add_entrance_exam_milestone(course_id, x_block): # lint-amnesty, pylint: di
if len(milestones): # lint-amnesty, pylint: disable=len-as-condition
milestone = milestones[0]
else:
description = u'Autogenerated during {} entrance exam creation.'.format(six.text_type(course_id))
description = 'Autogenerated during {} entrance exam creation.'.format(str(course_id))
milestone = milestones_helpers.add_milestone({
'name': _('Completed Course Entrance Exam'),
'namespace': milestone_namespace,
@@ -256,13 +255,13 @@ def add_entrance_exam_milestone(course_id, x_block): # lint-amnesty, pylint: di
})
relationship_types = milestones_helpers.get_milestone_relationship_types()
milestones_helpers.add_course_milestone(
six.text_type(course_id),
str(course_id),
relationship_types['REQUIRES'],
milestone
)
milestones_helpers.add_course_content_milestone(
six.text_type(course_id),
six.text_type(x_block.location),
str(course_id),
str(x_block.location),
relationship_types['FULFILLS'],
milestone
)
@@ -279,4 +278,4 @@ def remove_entrance_exam_milestone_reference(request, course_key):
for course_child in course_children:
if course_child.is_entrance_exam:
delete_item(request, course_child.scope_ids.usage_id)
milestones_helpers.remove_content_references(six.text_type(course_child.scope_ids.usage_id))
milestones_helpers.remove_content_references(str(course_child.scope_ids.usage_id))

View File

@@ -4,8 +4,8 @@ import functools
from django.http import HttpResponse, HttpResponseNotFound, HttpResponseServerError
from common.djangoapps.edxmako.shortcuts import render_to_response, render_to_string
from openedx.core.djangolib.js_utils import dump_js_escaped_json
from common.djangoapps.util.views import fix_crum_request
from openedx.core.djangolib.js_utils import dump_js_escaped_json
__all__ = ['not_found', 'server_error', 'render_404', 'render_500']

View File

@@ -6,7 +6,6 @@ the giturl attribute is set.
import logging
import six
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.utils.translation import ugettext as _
@@ -34,7 +33,7 @@ def export_git(request, course_key_string):
course_module = modulestore().get_course(course_key)
failed = False
log.debug(u'export_git course_module=%s', course_module)
log.debug('export_git course_module=%s', course_module)
msg = ""
if 'action' in request.GET and course_module.giturl:
@@ -48,7 +47,7 @@ def export_git(request, course_key_string):
msg = _('Course successfully exported to git repository')
except git_export_utils.GitExportError as ex:
failed = True
msg = six.text_type(ex)
msg = str(ex)
return render_to_response('export_git.html', {
'context_course': course_module,

View File

@@ -2,9 +2,9 @@
Helper methods for Studio views.
"""
import urllib
from uuid import uuid4
import six
from django.conf import settings
from django.http import HttpResponse
from django.utils.translation import ugettext as _
@@ -12,8 +12,8 @@ from opaque_keys.edx.keys import UsageKey
from xblock.core import XBlock
from cms.djangoapps.models.settings.course_grading import CourseGradingModel
from openedx.core.toggles import ENTRANCE_EXAMS
from common.djangoapps.edxmako.shortcuts import render_to_string
from openedx.core.toggles import ENTRANCE_EXAMS
from xmodule.modulestore.django import modulestore
from xmodule.tabs import StaticTab
@@ -107,9 +107,9 @@ def xblock_studio_url(xblock, parent_xblock=None):
if category == 'course':
return reverse_course_url('course_handler', xblock.location.course_key)
elif category in ('chapter', 'sequential'):
return u'{url}?show={usage_key}'.format(
return '{url}?show={usage_key}'.format(
url=reverse_course_url('course_handler', xblock.location.course_key),
usage_key=six.moves.urllib.parse.quote(six.text_type(xblock.location))
usage_key=urllib.parse.quote(str(xblock.location))
)
elif category == 'library':
library_key = xblock.location.course_key
@@ -220,7 +220,7 @@ def create_xblock(parent_locator, user, category, display_name, boilerplate=None
# TODO need to fix components that are sending definition_data as strings, instead of as dicts
# For now, migrate them into dicts here.
if isinstance(data, six.string_types):
if isinstance(data, str):
data = {'data': data}
created_block = store.create_child(

View File

@@ -25,7 +25,6 @@ from django.views.decorators.http import require_GET, require_http_methods
from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locator import LibraryLocator
from path import Path as path
from six import text_type
from storages.backends.s3boto import S3BotoStorage
from storages.backends.s3boto3 import S3Boto3Storage
from user_tasks.conf import settings as user_tasks_settings
@@ -123,7 +122,7 @@ def _write_chunk(request, courselike_key):
course_dir = data_root / subdir
filename = request.FILES['course-data'].name
courselike_string = text_type(courselike_key) + filename
courselike_string = str(courselike_key) + filename
# Do everything in a try-except block to make sure everything is properly cleaned up.
try:
# Use sessions to keep info about import progress
@@ -143,7 +142,7 @@ def _write_chunk(request, courselike_key):
if not course_dir.isdir():
os.mkdir(course_dir)
logging.debug(u'importing course to {0}'.format(temp_filepath))
logging.debug(f'importing course to {temp_filepath}')
# Get upload chunks byte ranges
try:
@@ -165,7 +164,7 @@ def _write_chunk(request, courselike_key):
if size < int(content_range['start']):
_save_request_status(request, courselike_string, -1)
log.warning(
u"Reported range %s does not match size downloaded so far %s",
"Reported range %s does not match size downloaded so far %s",
content_range['start'],
size
)
@@ -200,19 +199,19 @@ def _write_chunk(request, courselike_key):
}]
})
log.info(u"Course import %s: Upload complete", courselike_key)
log.info("Course import %s: Upload complete", courselike_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)
import_olx.delay(
request.user.id, text_type(courselike_key), storage_path, filename, request.LANGUAGE_CODE)
request.user.id, str(courselike_key), storage_path, filename, request.LANGUAGE_CODE)
# Send errors to client with stage at which error occurred.
except Exception as exception: # pylint: disable=broad-except
_save_request_status(request, courselike_string, -1)
if course_dir.isdir():
shutil.rmtree(course_dir)
log.info(u"Course import %s: Temp data cleared", courselike_key)
log.info("Course import %s: Temp data cleared", courselike_key)
log.exception(
"error importing course"
@@ -250,12 +249,12 @@ def import_status_handler(request, course_key_string, filename=None):
raise PermissionDenied()
# The task status record is authoritative once it's been created
args = {u'course_key_string': course_key_string, u'archive_name': filename}
args = {'course_key_string': course_key_string, 'archive_name': filename}
name = CourseImportTask.generate_name(args)
task_status = UserTaskStatus.objects.filter(name=name)
for status_filter in STATUS_FILTERS:
task_status = status_filter().filter_queryset(request, task_status, import_status_handler)
task_status = task_status.order_by(u'-created').first()
task_status = task_status.order_by('-created').first()
if task_status is None:
# The task hasn't been initialized yet; did we store info in the session already?
try:
@@ -279,7 +278,7 @@ def send_tarball(tarball, size):
"""
wrapper = FileWrapper(tarball, settings.COURSE_EXPORT_DOWNLOAD_CHUNK_SIZE)
response = StreamingHttpResponse(wrapper, content_type='application/x-tgz')
response['Content-Disposition'] = u'attachment; filename=%s' % os.path.basename(tarball.name)
response['Content-Disposition'] = 'attachment; filename=%s' % os.path.basename(tarball.name)
response['Content-Length'] = size
return response
@@ -377,7 +376,7 @@ def export_status_handler(request, course_key_string):
output_url = reverse_course_url('export_output_handler', course_key)
elif isinstance(artifact.file.storage, S3BotoStorage):
filename = os.path.basename(artifact.file.name)
disposition = u'attachment; filename="{}"'.format(filename)
disposition = f'attachment; filename="{filename}"'
output_url = artifact.file.storage.url(artifact.file.name, response_headers={
'response-content-disposition': disposition,
'response-content-encoding': 'application/octet-stream',
@@ -385,7 +384,7 @@ def export_status_handler(request, course_key_string):
})
elif isinstance(artifact.file.storage, S3Boto3Storage):
filename = os.path.basename(artifact.file.name)
disposition = u'attachment; filename="{}"'.format(filename)
disposition = f'attachment; filename="{filename}"'
output_url = artifact.file.storage.url(artifact.file.name, parameters={
'ResponseContentDisposition': disposition,
'ResponseContentEncoding': 'application/octet-stream',
@@ -450,9 +449,9 @@ def _latest_task_status(request, course_key_string, view_func=None):
Get the most recent export status update for the specified course/library
key.
"""
args = {u'course_key_string': course_key_string}
args = {'course_key_string': course_key_string}
name = CourseExportTask.generate_name(args)
task_status = UserTaskStatus.objects.filter(name=name)
for status_filter in STATUS_FILTERS:
task_status = status_filter().filter_queryset(request, task_status, view_func)
return task_status.order_by(u'-created').first()
return task_status.order_by('-created').first()

View File

@@ -24,7 +24,6 @@ from help_tokens.core import HelpUrlExpert
from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locator import LibraryUsageLocator
from pytz import UTC
from six import binary_type, text_type
from web_fragments.fragment import Fragment
from xblock.core import XBlock
from xblock.fields import Scope
@@ -34,15 +33,15 @@ from cms.djangoapps.models.settings.course_grading import CourseGradingModel
from cms.djangoapps.xblock_config.models import CourseEditLTIFieldsEnabledFlag
from cms.lib.xblock.authoring_mixin import VISIBILITY_VIEW
from common.djangoapps.edxmako.shortcuts import render_to_string
from openedx.core.lib.gating import api as gating_api
from openedx.core.lib.xblock_utils import hash_resource, request_token, wrap_xblock, wrap_xblock_aside
from openedx.core.djangoapps.bookmarks import api as bookmarks_api
from common.djangoapps.static_replace import replace_static_urls
from common.djangoapps.student.auth import has_studio_read_access, has_studio_write_access
from openedx.core.toggles import ENTRANCE_EXAMS
from common.djangoapps.util.date_utils import get_default_time_display
from common.djangoapps.util.json_request import JsonResponse, expect_json
from common.djangoapps.xblock_django.user_service import DjangoXBlockUserService
from openedx.core.djangoapps.bookmarks import api as bookmarks_api
from openedx.core.lib.gating import api as gating_api
from openedx.core.lib.xblock_utils import hash_resource, request_token, wrap_xblock, wrap_xblock_aside
from openedx.core.toggles import ENTRANCE_EXAMS
from xmodule.course_module import DEFAULT_START_DATE
from xmodule.library_tools import LibraryToolsService
from xmodule.modulestore import EdxJSONEncoder, ModuleStoreEnum
@@ -96,7 +95,7 @@ def _filter_entrance_exam_grader(graders):
the grader type for a given section of a course
"""
if ENTRANCE_EXAMS.is_enabled():
graders = [grader for grader in graders if grader.get('type') != u'Entrance Exam']
graders = [grader for grader in graders if grader.get('type') != 'Entrance Exam']
return graders
@@ -230,7 +229,7 @@ def xblock_handler(request, usage_key_string):
_is_library_component_limit_reached(parent_usage_key)):
return JsonResponse(
{
'error': _(u'Libraries cannot have more than {limit} components').format(
'error': _('Libraries cannot have more than {limit} components').format(
limit=settings.MAX_BLOCKS_PER_CONTENT_LIBRARY
)
},
@@ -244,8 +243,8 @@ def xblock_handler(request, usage_key_string):
request.json.get('display_name'),
)
return JsonResponse({
'locator': text_type(dest_usage_key),
'courseKey': text_type(dest_usage_key.course_key)
'locator': str(dest_usage_key),
'courseKey': str(dest_usage_key.course_key)
})
else:
return _create_item(request)
@@ -269,7 +268,7 @@ def xblock_handler(request, usage_key_string):
)
class StudioPermissionsService(object):
class StudioPermissionsService:
"""
Service that can provide information about a user's permissions.
@@ -289,7 +288,7 @@ class StudioPermissionsService(object):
return has_studio_write_access(self._user, course_key)
class StudioEditModuleRuntime(object):
class StudioEditModuleRuntime:
"""
An extremely minimal ModuleSystem shim used for XBlock edits and studio_view.
(i.e. whenever we're not using PreviewModuleSystem.) This is required to make information
@@ -321,7 +320,7 @@ class StudioEditModuleRuntime(object):
return None
@require_http_methods(("GET"))
@require_http_methods("GET")
@login_required
@expect_json
def xblock_view_handler(request, usage_key_string, view_name):
@@ -349,14 +348,14 @@ def xblock_view_handler(request, usage_key_string, view_name):
xblock.runtime.wrappers.append(partial(
wrap_xblock,
'StudioRuntime',
usage_id_serializer=text_type,
usage_id_serializer=str,
request_token=request_token(request),
))
xblock.runtime.wrappers_asides.append(partial(
wrap_xblock_aside,
'StudioRuntime',
usage_id_serializer=text_type,
usage_id_serializer=str,
request_token=request_token(request),
extra_classes=['wrapper-comp-plugins']
))
@@ -371,7 +370,7 @@ def xblock_view_handler(request, usage_key_string, view_name):
# dungeon and surface as uneditable, unsaveable, and undeletable
# component-goblins.
except Exception as exc: # pylint: disable=broad-except
log.debug(u"Unable to render %s for %r", view_name, xblock, exc_info=True)
log.debug("Unable to render %s for %r", view_name, xblock, exc_info=True)
fragment = Fragment(render_to_string('html_error.html', {'message': str(exc)}))
elif view_name in PREVIEW_VIEWS + container_views:
@@ -395,8 +394,8 @@ def xblock_view_handler(request, usage_key_string, view_name):
}
except ValueError:
return HttpResponse(
content=u"Couldn't parse paging parameters: enable_paging: "
u"{0}, page_number: {1}, page_size: {2}".format(
content="Couldn't parse paging parameters: enable_paging: "
"{}, page_number: {}, page_size: {}".format(
request.GET.get('enable_paging', 'false'),
request.GET.get('page_number', 0),
request.GET.get('page_size', 0)
@@ -440,7 +439,7 @@ def xblock_view_handler(request, usage_key_string, view_name):
hashed_resources[hash_resource(resource)] = resource._asdict()
fragment_content = fragment.content
if isinstance(fragment_content, binary_type):
if isinstance(fragment_content, bytes):
fragment_content = fragment.content.decode('utf-8')
return JsonResponse({
@@ -452,7 +451,7 @@ def xblock_view_handler(request, usage_key_string, view_name):
return HttpResponse(status=406)
@require_http_methods(("GET"))
@require_http_methods("GET")
@login_required
@expect_json
def xblock_outline_handler(request, usage_key_string):
@@ -480,7 +479,7 @@ def xblock_outline_handler(request, usage_key_string):
return Http404
@require_http_methods(("GET"))
@require_http_methods("GET")
@login_required
@expect_json
def xblock_container_handler(request, usage_key_string):
@@ -540,7 +539,7 @@ def _save_xblock(user, xblock, data=None, children_strings=None, metadata=None,
store.revert_to_published(xblock.location, user.id)
# Returning the same sort of result that we do for other save operations. In the future,
# we may want to return the full XBlockInfo.
return JsonResponse({'id': text_type(xblock.location)})
return JsonResponse({'id': str(xblock.location)})
old_metadata = own_metadata(xblock)
old_content = xblock.get_explicitly_set_fields_by_scope(Scope.content)
@@ -619,8 +618,8 @@ def _save_xblock(user, xblock, data=None, children_strings=None, metadata=None,
value = field.from_json(value)
except ValueError as verr:
reason = _("Invalid data")
if text_type(verr):
reason = _(u"Invalid data ({details})").format(details=text_type(verr))
if str(verr):
reason = _("Invalid data ({details})").format(details=str(verr))
return JsonResponse({"error": reason}, 400)
field.write_to(xblock, value)
@@ -647,7 +646,7 @@ def _save_xblock(user, xblock, data=None, children_strings=None, metadata=None,
store.update_item(course, user.id)
result = {
'id': text_type(xblock.location),
'id': str(xblock.location),
'data': data,
'metadata': own_metadata(xblock)
}
@@ -712,13 +711,13 @@ def _create_item(request):
# Only these categories are supported at this time.
if category not in ['html', 'problem', 'video']:
return HttpResponseBadRequest(
u"Category '%s' not supported for Libraries" % category, content_type='text/plain'
"Category '%s' not supported for Libraries" % category, content_type='text/plain'
)
if _is_library_component_limit_reached(usage_key):
return JsonResponse(
{
'error': _(u'Libraries cannot have more than {limit} components').format(
'error': _('Libraries cannot have more than {limit} components').format(
limit=settings.MAX_BLOCKS_PER_CONTENT_LIBRARY
)
},
@@ -734,7 +733,7 @@ def _create_item(request):
)
return JsonResponse(
{'locator': text_type(created_block.location), 'courseKey': text_type(created_block.location.course_key)}
{'locator': str(created_block.location), 'courseKey': str(created_block.location.course_key)}
)
@@ -766,7 +765,7 @@ def is_source_item_in_target_parents(source_item, target_parent):
"""
target_ancestors = _create_xblock_ancestor_info(target_parent, is_concise=True)['ancestors']
for target_ancestor in target_ancestors:
if text_type(source_item.location) == target_ancestor['id']:
if str(source_item.location) == target_ancestor['id']:
return True
return False
@@ -786,7 +785,7 @@ def _move_item(source_usage_key, target_parent_usage_key, user, target_index=Non
"""
# Get the list of all parentable component type XBlocks.
parent_component_types = list(
set(name for name, class_ in XBlock.load_classes() if getattr(class_, 'has_children', False)) -
{name for name, class_ in XBlock.load_classes() if getattr(class_, 'has_children', False)} -
set(DIRECT_ONLY_CATEGORIES)
)
@@ -810,7 +809,7 @@ def _move_item(source_usage_key, target_parent_usage_key, user, target_index=Non
if (valid_move_type.get(target_parent_type, '') != source_type and
target_parent_type not in parent_component_types):
error = _(u'You can not move {source_type} into {target_parent_type}.').format(
error = _('You can not move {source_type} into {target_parent_type}.').format(
source_type=source_type,
target_parent_type=target_parent_type,
)
@@ -823,20 +822,20 @@ def _move_item(source_usage_key, target_parent_usage_key, user, target_index=Non
elif target_parent_type == 'split_test':
error = _('You can not move an item directly into content experiment.')
elif source_index is None:
error = _(u'{source_usage_key} not found in {parent_usage_key}.').format(
source_usage_key=text_type(source_usage_key),
parent_usage_key=text_type(source_parent.location)
error = _('{source_usage_key} not found in {parent_usage_key}.').format(
source_usage_key=str(source_usage_key),
parent_usage_key=str(source_parent.location)
)
else:
try:
target_index = int(target_index) if target_index is not None else None
if target_index is not None and len(target_parent.children) < target_index:
error = _(u'You can not move {source_usage_key} at an invalid index ({target_index}).').format(
source_usage_key=text_type(source_usage_key),
error = _('You can not move {source_usage_key} at an invalid index ({target_index}).').format(
source_usage_key=str(source_usage_key),
target_index=target_index
)
except ValueError:
error = _(u'You must provide target_index ({target_index}) as an integer.').format(
error = _('You must provide target_index ({target_index}) as an integer.').format(
target_index=target_index
)
if error:
@@ -854,16 +853,16 @@ def _move_item(source_usage_key, target_parent_usage_key, user, target_index=Non
)
log.info(
u'MOVE: %s moved from %s to %s at %d index',
text_type(source_usage_key),
text_type(source_parent.location),
text_type(target_parent_usage_key),
'MOVE: %s moved from %s to %s at %d index',
str(source_usage_key),
str(source_parent.location),
str(target_parent_usage_key),
insert_at
)
context = {
'move_source_locator': text_type(source_usage_key),
'parent_locator': text_type(target_parent_usage_key),
'move_source_locator': str(source_usage_key),
'parent_locator': str(target_parent_usage_key),
'source_index': target_index if target_index is not None else source_index
}
return JsonResponse(context)
@@ -895,9 +894,9 @@ def _duplicate_item(parent_usage_key, duplicate_source_usage_key, user, display_
duplicate_metadata['display_name'] = display_name
else:
if source_item.display_name is None:
duplicate_metadata['display_name'] = _(u"Duplicate of {0}").format(source_item.category)
duplicate_metadata['display_name'] = _("Duplicate of {0}").format(source_item.category)
else:
duplicate_metadata['display_name'] = _(u"Duplicate of '{0}'").format(source_item.display_name)
duplicate_metadata['display_name'] = _("Duplicate of '{0}'").format(source_item.display_name)
asides_to_create = []
for aside in source_item.runtime.get_asides(source_item):
@@ -1000,7 +999,7 @@ def orphan_handler(request, course_key_string):
course_usage_key = CourseKey.from_string(course_key_string)
if request.method == 'GET':
if has_studio_read_access(request.user, course_usage_key):
return JsonResponse([text_type(item) for item in modulestore().get_orphans(course_usage_key)])
return JsonResponse([str(item) for item in modulestore().get_orphans(course_usage_key)])
else:
raise PermissionDenied()
if request.method == 'DELETE':
@@ -1028,7 +1027,7 @@ def _delete_orphans(course_usage_key, user_id, commit=False):
if branch == ModuleStoreEnum.BranchName.published:
revision = ModuleStoreEnum.RevisionOption.published_only
store.delete_item(itemloc, user_id, revision=revision)
return [text_type(item) for item in items]
return [str(item) for item in items]
def _get_xblock(usage_key, user):
@@ -1052,7 +1051,7 @@ def _get_xblock(usage_key, user):
raise
except InvalidLocationError:
log.error("Can't find item by location.")
return JsonResponse({"error": "Can't find item by location: " + text_type(usage_key)}, 404)
return JsonResponse({"error": "Can't find item by location: " + str(usage_key)}, 404)
def _get_module_info(xblock, rewrite_static_links=True, include_ancestor_info=False, include_publishing_info=False):
@@ -1104,7 +1103,7 @@ def _get_gating_info(course, xblock):
course.gating_prerequisites = gating_api.get_prerequisites(course.id)
info["is_prereq"] = gating_api.is_prerequisite(course.id, xblock.location)
info["prereqs"] = [
p for p in course.gating_prerequisites if text_type(xblock.location) not in p['namespace']
p for p in course.gating_prerequisites if str(xblock.location) not in p['namespace']
]
prereq, prereq_min_score, prereq_min_completion = gating_api.get_required_content(
course.id,
@@ -1199,14 +1198,14 @@ def create_xblock_info(xblock, data=None, metadata=None, include_ancestor_info=F
# Translators: The {pct_sign} here represents the percent sign, i.e., '%'
# in many languages. This is used to avoid Transifex's misinterpreting of
# '% o'. The percent sign is also translatable as a standalone string.
explanatory_message = _(u'Students must score {score}{pct_sign} or higher to access course materials.').format(
explanatory_message = _('Students must score {score}{pct_sign} or higher to access course materials.').format(
score=int(parent_xblock.entrance_exam_minimum_score_pct * 100),
# Translators: This is the percent sign. It will be used to represent
# a percent value out of 100, e.g. "58%" means "58/100".
pct_sign=_('%'))
xblock_info = {
'id': text_type(xblock.location),
'id': str(xblock.location),
'display_name': xblock.display_name_with_default,
'category': xblock.category,
'has_children': xblock.has_children
@@ -1399,7 +1398,7 @@ def add_container_page_publishing_info(xblock, xblock_info):
xblock_info["staff_lock_from"] = None
class VisibilityState(object):
class VisibilityState:
"""
Represents the possible visibility states for an xblock:
@@ -1575,6 +1574,6 @@ def _xblock_type_and_display_name(xblock):
"""
Returns a string representation of the xblock's type and display name
"""
return _(u'{section_or_subsection} "{display_name}"').format(
return _('{section_or_subsection} "{display_name}"').format(
section_or_subsection=xblock_type_display_name(xblock),
display_name=xblock.display_name_with_default)

View File

@@ -19,7 +19,6 @@ from opaque_keys.edx.keys import CourseKey
from opaque_keys.edx.locator import LibraryLocator, LibraryUsageLocator
from organizations.api import ensure_organization
from organizations.exceptions import InvalidOrganizationException
from six import text_type
from cms.djangoapps.course_creators.views import get_course_creator_status
from common.djangoapps.edxmako.shortcuts import render_to_response
@@ -123,14 +122,14 @@ def _display_library(library_key_string, request):
raise Http404 # This is not a library
if not has_studio_read_access(request.user, library_key):
log.exception(
u"User %s tried to access library %s without permission",
request.user.username, text_type(library_key)
"User %s tried to access library %s without permission",
request.user.username, str(library_key)
)
raise PermissionDenied()
library = modulestore().get_library(library_key)
if library is None:
log.exception(u"Library %s not found", text_type(library_key))
log.exception("Library %s not found", str(library_key))
raise Http404
response_format = 'html'
@@ -161,7 +160,7 @@ def _list_libraries(request):
lib_info = [
{
"display_name": lib.display_name,
"library_key": text_type(lib.location.library_key),
"library_key": str(lib.location.library_key),
}
for lib in libraries
if (
@@ -202,12 +201,12 @@ def _create_library(request):
except KeyError as error:
log.exception("Unable to create library - missing required JSON key.")
return JsonResponseBadRequest({
"ErrMsg": _(u"Unable to create library - missing required field '{field}'").format(field=text_type(error))
"ErrMsg": _("Unable to create library - missing required field '{field}'").format(field=str(error))
})
except InvalidKeyError as error:
log.exception("Unable to create library - invalid key.")
return JsonResponseBadRequest({
"ErrMsg": _(u"Unable to create library '{name}'.\n\n{err}").format(name=display_name, err=text_type(error))
"ErrMsg": _("Unable to create library '{name}'.\n\n{err}").format(name=display_name, err=str(error))
})
except DuplicateCourseError:
log.exception("Unable to create library - one already exists with the same key.")
@@ -226,7 +225,7 @@ def _create_library(request):
).format(organization_key=org)
})
lib_key_str = text_type(new_lib.location.library_key)
lib_key_str = str(new_lib.location.library_key)
return JsonResponse({
'url': reverse_library_url('library_handler', lib_key_str),
'library_key': lib_key_str,
@@ -252,10 +251,10 @@ def library_blocks_view(library, user, response_format):
prev_version = library.runtime.course_entry.structure['previous_version']
return JsonResponse({
"display_name": library.display_name,
"library_id": text_type(library.location.library_key),
"version": text_type(library.runtime.course_entry.course_key.version_guid),
"previous_version": text_type(prev_version) if prev_version else None,
"blocks": [text_type(x) for x in children],
"library_id": str(library.location.library_key),
"version": str(library.runtime.course_entry.course_key.version_guid),
"previous_version": str(prev_version) if prev_version else None,
"blocks": [str(x) for x in children],
})
can_edit = has_studio_write_access(user, library.location.library_key)
@@ -305,7 +304,7 @@ def manage_library_users(request, library_key_string):
'context_library': library,
'users': formatted_users,
'allow_actions': bool(user_perms & STUDIO_EDIT_ROLES),
'library_key': text_type(library_key),
'library_key': str(library_key),
'lib_users_url': reverse_library_url('manage_library_users', library_key_string),
'show_children_previews': library.show_children_previews
})

View File

@@ -3,7 +3,6 @@
import logging
from functools import partial
import six
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.http import Http404, HttpResponseBadRequest
@@ -16,10 +15,11 @@ from xblock.django.request import django_to_webob_request, webob_to_django_respo
from xblock.exceptions import NoSuchHandlerError
from xblock.runtime import KvsFieldData
from common.djangoapps import static_replace
from cms.djangoapps.xblock_config.models import StudioConfig
from cms.lib.xblock.field_data import CmsFieldData
from common.djangoapps import static_replace
from common.djangoapps.edxmako.shortcuts import render_to_string
from common.djangoapps.xblock_django.user_service import DjangoXBlockUserService
from lms.djangoapps.lms_xblock.field_data import LmsFieldData
from openedx.core.lib.license import wrap_with_license
from openedx.core.lib.xblock_utils import (
@@ -30,7 +30,6 @@ from openedx.core.lib.xblock_utils import (
wrap_xblock_aside,
xblock_local_resource_url
)
from common.djangoapps.xblock_django.user_service import DjangoXBlockUserService
from xmodule.contentstore.django import contentstore
from xmodule.error_module import ErrorBlock
from xmodule.exceptions import NotFoundError, ProcessingError
@@ -73,7 +72,7 @@ def preview_handler(request, usage_key_string, handler, suffix=''):
resp = instance.handle(handler, req, suffix)
except NoSuchHandlerError:
log.exception(u"XBlock %s attempted to access missing handler %r", instance, handler)
log.exception("XBlock %s attempted to access missing handler %r", instance, handler)
raise Http404 # lint-amnesty, pylint: disable=raise-missing-from
except NotFoundError:
@@ -102,7 +101,7 @@ class PreviewModuleSystem(ModuleSystem): # pylint: disable=abstract-method
def handler_url(self, block, handler_name, suffix='', query='', thirdparty=False):
return reverse('preview_handler', kwargs={
'usage_key_string': six.text_type(block.scope_ids.usage_id),
'usage_key_string': str(block.scope_ids.usage_id),
'handler': handler_name,
'suffix': suffix,
}) + '?' + query
@@ -121,7 +120,7 @@ class PreviewModuleSystem(ModuleSystem): # pylint: disable=abstract-method
# (see https://openedx.atlassian.net/browse/TE-811)
return [
aside_type
for aside_type in super(PreviewModuleSystem, self).applicable_aside_types(block) # lint-amnesty, pylint: disable=super-with-arguments
for aside_type in super().applicable_aside_types(block)
if aside_type != 'acid_aside'
]
@@ -138,7 +137,7 @@ class PreviewModuleSystem(ModuleSystem): # pylint: disable=abstract-method
for aside, aside_fn in aside_frag_fns:
aside_frag = aside_fn(block, context)
if aside_frag.content != u'':
if aside_frag.content != '':
aside_frag_wrapped = self.wrap_aside(block, aside, view_name, aside_frag, context)
aside.save()
result.add_fragment_resources(aside_frag_wrapped)
@@ -167,7 +166,7 @@ def _preview_module_system(request, descriptor, field_data):
wrap_xblock,
'PreviewRuntime',
display_name_only=display_name_only,
usage_id_serializer=six.text_type,
usage_id_serializer=str,
request_token=request_token(request)
),
@@ -181,7 +180,7 @@ def _preview_module_system(request, descriptor, field_data):
partial(
wrap_xblock_aside,
'PreviewRuntime',
usage_id_serializer=six.text_type,
usage_id_serializer=str,
request_token=request_token(request)
)
]
@@ -285,7 +284,7 @@ def _studio_wrap_xblock(xblock, view, frag, context, display_name_only=False):
is_reorderable = _is_xblock_reorderable(xblock, context)
selected_groups_label = get_visibility_partition_info(xblock)['selected_groups_label']
if selected_groups_label:
selected_groups_label = _(u'Access restricted to: {list_of_groups}').format(list_of_groups=selected_groups_label) # lint-amnesty, pylint: disable=line-too-long
selected_groups_label = _('Access restricted to: {list_of_groups}').format(list_of_groups=selected_groups_label) # lint-amnesty, pylint: disable=line-too-long
course = modulestore().get_course(xblock.location.course_key)
template_context = {
'xblock_context': context,
@@ -326,6 +325,6 @@ def get_preview_fragment(request, descriptor, context):
try:
fragment = module.render(preview_view, context)
except Exception as exc: # pylint: disable=broad-except
log.warning(u"Unable to render %s for %r", preview_view, module, exc_info=True)
log.warning("Unable to render %s for %r", preview_view, module, exc_info=True)
fragment = Fragment(render_to_string('html_error.html', {'message': str(exc)}))
return fragment

View File

@@ -59,7 +59,7 @@ def howitworks(request):
return render_to_response('howitworks.html', {})
@waffle_switch('{}.{}'.format(waffle.WAFFLE_NAMESPACE, waffle.ENABLE_ACCESSIBILITY_POLICY_PAGE))
@waffle_switch(f'{waffle.WAFFLE_NAMESPACE}.{waffle.ENABLE_ACCESSIBILITY_POLICY_PAGE}')
def accessibility(request):
"""
Display the accessibility accommodation form.

View File

@@ -1,9 +1,6 @@
"""
Views related to course tabs
"""
import six
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.http import HttpResponseNotFound
@@ -100,7 +97,7 @@ def reorder_tabs_handler(course_item, request):
tab = get_tab_by_tab_id_locator(old_tab_list, tab_id_locator)
if tab is None:
return JsonResponse(
{"error": u"Tab with id_locator '{0}' does not exist.".format(tab_id_locator)}, status=400
{"error": f"Tab with id_locator '{tab_id_locator}' does not exist."}, status=400
)
new_tab_list.append(tab)
@@ -114,7 +111,7 @@ def reorder_tabs_handler(course_item, request):
CourseTabList.validate_tabs(new_tab_list)
except InvalidTabsException as exception:
return JsonResponse(
{"error": u"New list of tabs is not valid: {0}.".format(str(exception))}, status=400
{"error": "New list of tabs is not valid: {}.".format(str(exception))}, status=400
)
# persist the new order of the tabs
@@ -136,7 +133,7 @@ def edit_tab_handler(course_item, request):
tab = get_tab_by_tab_id_locator(course_item.tabs, tab_id_locator)
if tab is None:
return JsonResponse(
{"error": u"Tab with id_locator '{0}' does not exist.".format(tab_id_locator)}, status=400
{"error": f"Tab with id_locator '{tab_id_locator}' does not exist."}, status=400
)
if 'is_hidden' in request.json:
@@ -144,7 +141,7 @@ def edit_tab_handler(course_item, request):
tab.is_hidden = request.json['is_hidden']
modulestore().update_item(course_item, request.user.id)
else:
raise NotImplementedError(u'Unsupported request to edit tab: {0}'.format(request.json))
raise NotImplementedError(f'Unsupported request to edit tab: {request.json}')
return JsonResponse()
@@ -200,7 +197,7 @@ def primitive_delete(course, num):
def primitive_insert(course, num, tab_type, name):
"Inserts a new tab at the given number (0 based)."
validate_args(num, tab_type)
new_tab = CourseTab.from_json({u'type': six.text_type(tab_type), u'name': six.text_type(name)})
new_tab = CourseTab.from_json({'type': str(tab_type), 'name': str(name)})
tabs = course.tabs
tabs.insert(num, new_tab)
modulestore().update_item(course, ModuleStoreEnum.UserID.primitive_command)

View File

@@ -20,7 +20,7 @@ class RolesTest(TestCase):
"""
def setUp(self):
""" Test case setup """
super(RolesTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.global_admin = AdminFactory()
self.instructor = User.objects.create_user('testinstructor', 'testinstructor+courses@edx.org', 'foo')

View File

@@ -6,13 +6,12 @@ Unit tests for the asset upload endpoint.
import json
from datetime import datetime
from io import BytesIO
from unittest import mock
from unittest.mock import patch
import mock
import six
from ddt import data, ddt
from django.conf import settings
from django.test.utils import override_settings
from mock import patch
from opaque_keys.edx.keys import AssetKey
from opaque_keys.edx.locator import CourseLocator
from PIL import Image
@@ -43,7 +42,7 @@ class AssetsTestCase(CourseTestCase):
Parent class for all asset tests.
"""
def setUp(self):
super(AssetsTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.url = reverse_course_url('assets_handler', self.course.id)
def upload_asset(self, name="asset-1", asset_type='text'):
@@ -61,14 +60,14 @@ class AssetsTestCase(CourseTestCase):
sample_asset = BytesIO()
sample_file_contents = b"This file is generated by python unit test"
if asset_type == 'text':
sample_asset.name = '{name}.txt'.format(name=name)
sample_asset.name = f'{name}.txt'
sample_asset.write(sample_file_contents)
elif asset_type == 'image':
image = Image.new("RGB", size=(50, 50), color=(256, 0, 0))
image.save(sample_asset, 'jpeg')
sample_asset.name = '{name}.jpg'.format(name=name)
sample_asset.name = f'{name}.jpg'
elif asset_type == 'opendoc':
sample_asset.name = '{name}.odt'.format(name=name)
sample_asset.name = f'{name}.odt'
sample_asset.write(sample_file_contents)
sample_asset.seek(0)
return sample_asset
@@ -131,12 +130,12 @@ class BasicAssetsTestCase(AssetsTestCase):
course = module_store.get_course(course_id)
filename = 'sample_static.html'
html_src_attribute = '"/static/{}"'.format(filename)
html_src_attribute = f'"/static/{filename}"'
asset_url = replace_static_urls(html_src_attribute, course_id=course.id)
url = asset_url.replace('"', '')
base_url = url.replace(filename, '')
self.assertIn("/{}".format(filename), url)
self.assertIn(f"/{filename}", url)
resp = self.client.get(url)
self.assertEqual(resp.status_code, 200)
@@ -146,7 +145,7 @@ class BasicAssetsTestCase(AssetsTestCase):
# browser append relative_path with base_url
absolute_path = base_url + relative_path
self.assertIn("/{}".format(relative_path), absolute_path)
self.assertIn(f"/{relative_path}", absolute_path)
resp = self.client.get(absolute_path)
self.assertEqual(resp.status_code, 200)
@@ -343,7 +342,7 @@ class UploadTestCase(AssetsTestCase):
Unit tests for uploading a file
"""
def setUp(self):
super(UploadTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.url = reverse_course_url('assets_handler', self.course.id)
def test_happy_path(self):
@@ -378,7 +377,7 @@ class DownloadTestCase(AssetsTestCase):
Unit tests for downloading a file.
"""
def setUp(self):
super(DownloadTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.url = reverse_course_url('assets_handler', self.course.id)
# First, upload something.
self.asset_name = 'download_test'
@@ -430,7 +429,7 @@ class AssetToJsonTestCase(AssetsTestCase):
)
self.assertEqual(output["portable_url"], "/static/my_file_name.jpg")
self.assertEqual(output["thumbnail"], "/asset-v1:org+class+run+type@thumbnail+block@my_file_name_thumb.jpg")
self.assertEqual(output["id"], six.text_type(location))
self.assertEqual(output["id"], str(location))
self.assertEqual(output['locked'], True)
output = assets._get_asset_json("name", content_type, upload_date, location, None, False)
@@ -458,7 +457,7 @@ class LockAssetTestCase(AssetsTestCase):
upload_date = datetime(2013, 6, 1, 10, 30, tzinfo=UTC)
asset_location = course.id.make_asset_key('asset', 'sample_static.html')
url = reverse_course_url(
'assets_handler', course.id, kwargs={'asset_key_string': six.text_type(asset_location)}
'assets_handler', course.id, kwargs={'asset_key_string': str(asset_location)}
)
resp = self.client.post(
@@ -502,7 +501,7 @@ class DeleteAssetTestCase(AssetsTestCase):
"""
def setUp(self):
""" Scaffolding """
super(DeleteAssetTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.url = reverse_course_url('assets_handler', self.course.id)
# First, upload something.
self.asset_name = 'delete_test'
@@ -518,7 +517,7 @@ class DeleteAssetTestCase(AssetsTestCase):
def test_delete_asset(self):
""" Tests the happy path :) """
test_url = reverse_course_url(
'assets_handler', self.course.id, kwargs={'asset_key_string': six.text_type(self.uploaded_url)})
'assets_handler', self.course.id, kwargs={'asset_key_string': str(self.uploaded_url)})
resp = self.client.delete(test_url, HTTP_ACCEPT="application/json")
self.assertEqual(resp.status_code, 204)
@@ -547,7 +546,7 @@ class DeleteAssetTestCase(AssetsTestCase):
mock_asset_key.return_value = thumbnail_location
test_url = reverse_course_url(
'assets_handler', self.course.id, kwargs={'asset_key_string': six.text_type(uploaded_image_url)})
'assets_handler', self.course.id, kwargs={'asset_key_string': str(uploaded_image_url)})
resp = self.client.delete(test_url, HTTP_ACCEPT="application/json")
self.assertEqual(resp.status_code, 204)
@@ -555,7 +554,7 @@ class DeleteAssetTestCase(AssetsTestCase):
""" Tests the sad path :( """
test_url = reverse_course_url(
'assets_handler',
self.course.id, kwargs={'asset_key_string': six.text_type("/c4x/edX/toy/asset/invalid.pdf")}
self.course.id, kwargs={'asset_key_string': "/c4x/edX/toy/asset/invalid.pdf"}
)
resp = self.client.delete(test_url, HTTP_ACCEPT="application/json")
self.assertEqual(resp.status_code, 404)
@@ -563,7 +562,7 @@ class DeleteAssetTestCase(AssetsTestCase):
def test_delete_asset_with_invalid_thumbnail(self):
""" Tests the sad path :( """
test_url = reverse_course_url(
'assets_handler', self.course.id, kwargs={'asset_key_string': six.text_type(self.uploaded_url)})
'assets_handler', self.course.id, kwargs={'asset_key_string': str(self.uploaded_url)})
self.content.thumbnail_location = StaticContent.get_location_from_path('/c4x/edX/toy/asset/invalid')
contentstore().save(self.content)
resp = self.client.delete(test_url, HTTP_ACCEPT="application/json")

View File

@@ -1,5 +1,3 @@
#-*- coding: utf-8 -*-
"""
Certificates Tests.
"""
@@ -7,14 +5,12 @@ Certificates Tests.
import itertools
import json
from unittest import mock
import ddt
import mock
import six
from django.conf import settings
from django.test.utils import override_settings
from opaque_keys.edx.keys import AssetKey
from six.moves import range
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.utils import get_lms_link_for_certificate_web_view, reverse_course_url
@@ -33,19 +29,19 @@ FEATURES_WITH_CERTS_ENABLED = settings.FEATURES.copy()
FEATURES_WITH_CERTS_ENABLED['CERTIFICATES_HTML_VIEW'] = True
CERTIFICATE_JSON = {
u'name': u'Test certificate',
u'description': u'Test description',
u'is_active': True,
u'version': CERTIFICATE_SCHEMA_VERSION,
'name': 'Test certificate',
'description': 'Test description',
'is_active': True,
'version': CERTIFICATE_SCHEMA_VERSION,
}
CERTIFICATE_JSON_WITH_SIGNATORIES = {
u'name': u'Test certificate',
u'description': u'Test description',
u'version': CERTIFICATE_SCHEMA_VERSION,
u'course_title': 'Course Title Override',
u'is_active': True,
u'signatories': [
'name': 'Test certificate',
'description': 'Test description',
'version': CERTIFICATE_SCHEMA_VERSION,
'course_title': 'Course Title Override',
'is_active': True,
'signatories': [
{
"name": "Bob Smith",
"title": "The DEAN.",
@@ -59,7 +55,7 @@ SIGNATORY_PATH = 'asset-v1:test+CSS101+SP2017+type@asset+block@Signature{}.png'
# pylint: disable=no-member
class HelperMethods(object):
class HelperMethods:
"""
Mixin that provides useful methods for certificate configuration tests.
"""
@@ -107,7 +103,7 @@ class HelperMethods(object):
# pylint: disable=no-member
class CertificatesBaseTestCase(object):
class CertificatesBaseTestCase:
"""
Mixin with base test cases for the certificates.
"""
@@ -128,8 +124,8 @@ class CertificatesBaseTestCase(object):
bad_jsons = [
# must have name of the certificate
{
u'description': 'Test description',
u'version': CERTIFICATE_SCHEMA_VERSION
'description': 'Test description',
'version': CERTIFICATE_SCHEMA_VERSION
},
# an empty json
@@ -155,8 +151,8 @@ class CertificatesBaseTestCase(object):
Test invalid json handling.
"""
# Invalid JSON.
invalid_json = u"{u'name': 'Test Name', u'description': 'Test description'," \
u" u'version': " + str(CERTIFICATE_SCHEMA_VERSION) + ", []}"
invalid_json = "{u'name': 'Test Name', u'description': 'Test description'," \
" u'version': " + str(CERTIFICATE_SCHEMA_VERSION) + ", []}"
response = self.client.post(
self._url(),
@@ -174,9 +170,9 @@ class CertificatesBaseTestCase(object):
def test_certificate_data_validation(self):
#Test certificate schema version
json_data_1 = {
u'version': 100,
u'name': u'Test certificate',
u'description': u'Test description'
'version': 100,
'name': 'Test certificate',
'description': 'Test description'
}
with self.assertRaises(Exception) as context:
@@ -189,8 +185,8 @@ class CertificatesBaseTestCase(object):
#Test certificate name is missing
json_data_2 = {
u'version': CERTIFICATE_SCHEMA_VERSION,
u'description': u'Test description'
'version': CERTIFICATE_SCHEMA_VERSION,
'description': 'Test description'
}
with self.assertRaises(Exception) as context:
@@ -212,7 +208,7 @@ class CertificatesListHandlerTestCase(
"""
Set up CertificatesListHandlerTestCase.
"""
super(CertificatesListHandlerTestCase, self).setUp('cms.djangoapps.contentstore.views.certificates.tracker') # lint-amnesty, pylint: disable=super-with-arguments
super().setUp('cms.djangoapps.contentstore.views.certificates.tracker')
self.reset_urls()
def _url(self):
@@ -226,11 +222,11 @@ class CertificatesListHandlerTestCase(
Test that you can create a certificate.
"""
expected = {
u'version': CERTIFICATE_SCHEMA_VERSION,
u'name': u'Test certificate',
u'description': u'Test description',
u'is_active': True,
u'signatories': []
'version': CERTIFICATE_SCHEMA_VERSION,
'name': 'Test certificate',
'description': 'Test description',
'is_active': True,
'signatories': []
}
response = self.client.ajax_post(
self._url(),
@@ -244,7 +240,7 @@ class CertificatesListHandlerTestCase(
self.assertEqual(content, expected)
self.assert_event_emitted(
'edx.certificate.configuration.created',
course_id=six.text_type(self.course.id),
course_id=str(self.course.id),
configuration_id=certificate_id,
)
@@ -272,7 +268,7 @@ class CertificatesListHandlerTestCase(
@override_settings(LMS_BASE="lms_base_url")
def test_lms_link_for_certificate_web_view(self):
test_url = "//lms_base_url/certificates/" \
"course/" + six.text_type(self.course.id) + '?preview=honor'
"course/" + str(self.course.id) + '?preview=honor'
link = get_lms_link_for_certificate_web_view(
course_key=self.course.id,
mode='honor'
@@ -403,11 +399,11 @@ class CertificatesListHandlerTestCase(
"""
self._add_course_certificates(count=2)
json_data = {
u'version': CERTIFICATE_SCHEMA_VERSION,
u'name': u'New test certificate',
u'description': u'New test description',
u'is_active': True,
u'signatories': []
'version': CERTIFICATE_SCHEMA_VERSION,
'name': 'New test certificate',
'description': 'New test description',
'is_active': True,
'signatories': []
}
response = self.client.post(
@@ -437,7 +433,7 @@ class CertificatesDetailHandlerTestCase(
"""
Set up CertificatesDetailHandlerTestCase.
"""
super(CertificatesDetailHandlerTestCase, self).setUp('cms.djangoapps.contentstore.views.certificates.tracker') # lint-amnesty, pylint: disable=super-with-arguments
super().setUp('cms.djangoapps.contentstore.views.certificates.tracker')
self.reset_urls()
def _url(self, cid=-1):
@@ -456,13 +452,13 @@ class CertificatesDetailHandlerTestCase(
PUT/POST new certificate.
"""
expected = {
u'id': 666,
u'version': CERTIFICATE_SCHEMA_VERSION,
u'name': u'Test certificate',
u'description': u'Test description',
u'is_active': True,
u'course_title': u'Course Title Override',
u'signatories': []
'id': 666,
'version': CERTIFICATE_SCHEMA_VERSION,
'name': 'Test certificate',
'description': 'Test description',
'is_active': True,
'course_title': 'Course Title Override',
'signatories': []
}
response = self.client.put(
@@ -476,7 +472,7 @@ class CertificatesDetailHandlerTestCase(
self.assertEqual(content, expected)
self.assert_event_emitted(
'edx.certificate.configuration.created',
course_id=six.text_type(self.course.id),
course_id=str(self.course.id),
configuration_id=666,
)
@@ -487,13 +483,13 @@ class CertificatesDetailHandlerTestCase(
self._add_course_certificates(count=2)
expected = {
u'id': 1,
u'version': CERTIFICATE_SCHEMA_VERSION,
u'name': u'New test certificate',
u'description': u'New test description',
u'is_active': True,
u'course_title': u'Course Title Override',
u'signatories': []
'id': 1,
'version': CERTIFICATE_SCHEMA_VERSION,
'name': 'New test certificate',
'description': 'New test description',
'is_active': True,
'course_title': 'Course Title Override',
'signatories': []
}
@@ -508,7 +504,7 @@ class CertificatesDetailHandlerTestCase(
self.assertEqual(content, expected)
self.assert_event_emitted(
'edx.certificate.configuration.modified',
course_id=six.text_type(self.course.id),
course_id=str(self.course.id),
configuration_id=1,
)
self.reload_course()
@@ -516,7 +512,7 @@ class CertificatesDetailHandlerTestCase(
# Verify that certificate is properly updated in the course.
course_certificates = self.course.certificates['certificates']
self.assertEqual(len(course_certificates), 2)
self.assertEqual(course_certificates[1].get('name'), u'New test certificate')
self.assertEqual(course_certificates[1].get('name'), 'New test certificate')
self.assertEqual(course_certificates[1].get('description'), 'New test description')
def test_can_edit_certificate_without_is_active(self):
@@ -537,13 +533,13 @@ class CertificatesDetailHandlerTestCase(
self.save_course()
expected = {
u'id': 1,
u'version': CERTIFICATE_SCHEMA_VERSION,
u'name': u'New test certificate',
u'description': u'New test description',
u'is_active': True,
u'course_title': u'Course Title Override',
u'signatories': []
'id': 1,
'version': CERTIFICATE_SCHEMA_VERSION,
'name': 'New test certificate',
'description': 'New test description',
'is_active': True,
'course_title': 'Course Title Override',
'signatories': []
}
@@ -573,7 +569,7 @@ class CertificatesDetailHandlerTestCase(
self.assertEqual(response.status_code, 204)
self.assert_event_emitted(
'edx.certificate.configuration.deleted',
course_id=six.text_type(self.course.id),
course_id=str(self.course.id),
configuration_id='1',
)
self.reload_course()
@@ -597,7 +593,7 @@ class CertificatesDetailHandlerTestCase(
self.assertEqual(response.status_code, 204)
self.assert_event_emitted(
'edx.certificate.configuration.deleted',
course_id=six.text_type(self.course.id),
course_id=str(self.course.id),
configuration_id='1',
)
self.reload_course()
@@ -622,7 +618,7 @@ class CertificatesDetailHandlerTestCase(
self.assertEqual(response.status_code, 204)
self.assert_event_emitted(
'edx.certificate.configuration.deleted',
course_id=six.text_type(self.course.id),
course_id=str(self.course.id),
configuration_id='1',
)
self.reload_course()
@@ -673,14 +669,14 @@ class CertificatesDetailHandlerTestCase(
"""
self._add_course_certificates(count=2, signatory_count=1, is_active=True, asset_path_format=signatory_path)
cert_data = {
u'id': 1,
u'version': CERTIFICATE_SCHEMA_VERSION,
u'name': u'New test certificate',
u'description': u'New test description',
u'course_title': u'Course Title Override',
u'org_logo_path': '',
u'is_active': False,
u'signatories': []
'id': 1,
'version': CERTIFICATE_SCHEMA_VERSION,
'name': 'New test certificate',
'description': 'New test description',
'course_title': 'Course Title Override',
'org_logo_path': '',
'is_active': False,
'signatories': []
}
user = UserFactory()
for role in [CourseInstructorRole, CourseStaffRole]:
@@ -790,7 +786,7 @@ class CertificatesDetailHandlerTestCase(
cert_event_type = 'activated' if is_active else 'deactivated'
self.assert_event_emitted(
'.'.join(['edx.certificate.configuration', cert_event_type]),
course_id=six.text_type(self.course.id),
course_id=str(self.course.id),
)
@ddt.data(*itertools.product([True, False], [C4X_SIGNATORY_PATH, SIGNATORY_PATH]))

View File

@@ -5,12 +5,11 @@ Unit tests for the container page.
import datetime
import re
from unittest.mock import Mock, patch
import six
from django.http import Http404
from django.test.client import RequestFactory
from django.utils import http
from mock import Mock, patch
from pytz import UTC
import cms.djangoapps.contentstore.views.component as views
@@ -31,7 +30,7 @@ class ContainerPageTestCase(StudioPageTestCase, LibraryTestCase):
reorderable_child_view = 'reorderable_container_child_preview'
def setUp(self):
super(ContainerPageTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.vertical = self._create_item(self.sequential.location, 'vertical', 'Unit')
self.html = self._create_item(self.vertical.location, "html", "HTML")
self.child_container = self._create_item(self.vertical.location, 'split_test', 'Split Test')
@@ -60,18 +59,18 @@ class ContainerPageTestCase(StudioPageTestCase, LibraryTestCase):
self._test_html_content(
self.child_container,
expected_section_tag=(
u'<section class="wrapper-xblock level-page is-hidden studio-xblock-wrapper" '
u'data-locator="{0}" data-course-key="{0.course_key}">'.format(self.child_container.location)
'<section class="wrapper-xblock level-page is-hidden studio-xblock-wrapper" '
'data-locator="{0}" data-course-key="{0.course_key}">'.format(self.child_container.location)
),
expected_breadcrumbs=(
u'<li class="nav-item">\\s*<a href="/course/{course}{section_parameters}">Week 1<\\/a>.*'
u'<a href="/course/{course}{subsection_parameters}">Lesson 1</a>'
'<li class="nav-item">\\s*<a href="/course/{course}{section_parameters}">Week 1<\\/a>.*'
'<a href="/course/{course}{subsection_parameters}">Lesson 1</a>'
).format(
course=re.escape(six.text_type(self.course.id)),
section_parameters=re.escape(u'?show={}'.format(http.urlquote(
course=re.escape(str(self.course.id)),
section_parameters=re.escape('?show={}'.format(http.urlquote(
str(self.chapter.location).encode()
))),
subsection_parameters=re.escape(u'?show={}'.format(http.urlquote(
subsection_parameters=re.escape('?show={}'.format(http.urlquote(
str(self.sequential.location).encode()
))),
),
@@ -89,16 +88,16 @@ class ContainerPageTestCase(StudioPageTestCase, LibraryTestCase):
self._test_html_content(
xblock,
expected_section_tag=(
u'<section class="wrapper-xblock level-page is-hidden studio-xblock-wrapper" '
u'data-locator="{0}" data-course-key="{0.course_key}">'.format(draft_container.location)
'<section class="wrapper-xblock level-page is-hidden studio-xblock-wrapper" '
'data-locator="{0}" data-course-key="{0.course_key}">'.format(draft_container.location)
),
expected_breadcrumbs=(
u'<a href="/course/{course}{subsection_parameters}">Lesson 1</a>.*'
u'<a href="/container/{unit_parameters}">Unit</a>.*'
'<a href="/course/{course}{subsection_parameters}">Lesson 1</a>.*'
'<a href="/container/{unit_parameters}">Unit</a>.*'
).format(
course=re.escape(six.text_type(self.course.id)),
course=re.escape(str(self.course.id)),
unit_parameters=re.escape(str(self.vertical.location)),
subsection_parameters=re.escape(u'?show={}'.format(http.urlquote(
subsection_parameters=re.escape('?show={}'.format(http.urlquote(
str(self.sequential.location).encode()
))),
),
@@ -228,6 +227,6 @@ class ContainerPageTestCase(StudioPageTestCase, LibraryTestCase):
# Check 200 response if 'usage_key_string' is correct
response = views.container_handler(
request=request,
usage_key_string=six.text_type(self.vertical.location)
usage_key_string=str(self.vertical.location)
)
self.assertEqual(response.status_code, 200)

View File

@@ -5,12 +5,11 @@ Unit tests for getting the list of courses and the course outline.
import datetime
import json
from unittest import mock
import ddt
import lxml
import mock
import pytz
import six
from django.conf import settings
from django.core.exceptions import PermissionDenied
from django.test.utils import override_settings
@@ -23,10 +22,10 @@ from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.utils import add_instructor, reverse_course_url, reverse_usage_url
from common.djangoapps.course_action_state.managers import CourseRerunUIStateManager
from common.djangoapps.course_action_state.models import CourseRerunState
from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES
from common.djangoapps.student.auth import has_course_author_access
from common.djangoapps.student.roles import CourseStaffRole, GlobalStaff, LibraryUserRole
from common.djangoapps.student.tests.factories import UserFactory
from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory, LibraryFactory, check_mongo_calls
@@ -44,7 +43,7 @@ class TestCourseIndex(CourseTestCase):
"""
Add a course with odd characters in the fields
"""
super(TestCourseIndex, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# had a problem where index showed course but has_access failed to retrieve it for non-staff
self.odd_course = CourseFactory.create(
org='test.org_1-2',
@@ -140,7 +139,7 @@ class TestCourseIndex(CourseTestCase):
# First spot check some values in the root response
self.assertEqual(json_response['category'], 'course')
self.assertEqual(json_response['id'], six.text_type(self.course.location))
self.assertEqual(json_response['id'], str(self.course.location))
self.assertEqual(json_response['display_name'], self.course.display_name)
self.assertTrue(json_response['published'])
self.assertIsNone(json_response['visibility_state'])
@@ -150,7 +149,7 @@ class TestCourseIndex(CourseTestCase):
self.assertGreater(len(children), 0)
first_child_response = children[0]
self.assertEqual(first_child_response['category'], 'chapter')
self.assertEqual(first_child_response['id'], six.text_type(chapter.location))
self.assertEqual(first_child_response['id'], str(chapter.location))
self.assertEqual(first_child_response['display_name'], 'Week 1')
self.assertTrue(json_response['published'])
self.assertEqual(first_child_response['visibility_state'], VisibilityState.unscheduled)
@@ -250,7 +249,7 @@ class TestCourseIndex(CourseTestCase):
"""
# Testing the response code by passing slash separated course id whose format is valid but no course
# having this id exists.
invalid_course_key = '{}_blah_blah_blah'.format(self.course.id)
invalid_course_key = f'{self.course.id}_blah_blah_blah'
course_updates_url = reverse_course_url('course_info_handler', invalid_course_key)
response = self.client.get(course_updates_url)
self.assertEqual(response.status_code, 404)
@@ -263,7 +262,7 @@ class TestCourseIndex(CourseTestCase):
self.assertEqual(response.status_code, 404)
# Testing the response by passing split course id whose format is invalid.
invalid_course_id = 'invalid.course.key/{}'.format(split_course_key)
invalid_course_id = f'invalid.course.key/{split_course_key}'
course_updates_url_split = reverse_course_url('course_info_handler', invalid_course_id)
response = self.client.get(course_updates_url_split)
self.assertEqual(response.status_code, 404)
@@ -274,7 +273,7 @@ class TestCourseIndex(CourseTestCase):
"""
# Testing the response code by passing slash separated course key, no course
# having this key exists.
invalid_course_key = '{}_some_invalid_run'.format(self.course.id)
invalid_course_key = f'{self.course.id}_some_invalid_run'
course_outline_url = reverse_course_url('course_handler', invalid_course_key)
response = self.client.get_html(course_outline_url)
self.assertEqual(response.status_code, 404)
@@ -329,7 +328,7 @@ class TestCourseIndexArchived(CourseTestCase):
"""
Add courses with the end date set to various values
"""
super(TestCourseIndexArchived, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# Base course has no end date (so is active)
self.course.end = None
@@ -426,7 +425,7 @@ class TestCourseOutline(CourseTestCase):
"""
Set up the for the course outline tests.
"""
super(TestCourseOutline, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.chapter = ItemFactory.create(
parent_location=self.course.location, category='chapter', display_name="Week 1"
@@ -456,7 +455,7 @@ class TestCourseOutline(CourseTestCase):
# First spot check some values in the root response
self.assertEqual(json_response['category'], 'course')
self.assertEqual(json_response['id'], six.text_type(self.course.location))
self.assertEqual(json_response['id'], str(self.course.location))
self.assertEqual(json_response['display_name'], self.course.display_name)
self.assertNotEqual(json_response.get('published', False), is_concise)
self.assertIsNone(json_response.get('visibility_state'))
@@ -466,7 +465,7 @@ class TestCourseOutline(CourseTestCase):
self.assertGreater(len(children), 0)
first_child_response = children[0]
self.assertEqual(first_child_response['category'], 'chapter')
self.assertEqual(first_child_response['id'], six.text_type(self.chapter.location))
self.assertEqual(first_child_response['id'], str(self.chapter.location))
self.assertEqual(first_child_response['display_name'], 'Week 1')
self.assertNotEqual(json_response.get('published', False), is_concise)
if not is_concise:
@@ -500,12 +499,12 @@ class TestCourseOutline(CourseTestCase):
self.assertIsNone(course_outline_initial_state('no-such-locator', course_structure))
# Verify that the correct initial state is returned for the test chapter
chapter_locator = six.text_type(self.chapter.location)
chapter_locator = str(self.chapter.location)
initial_state = course_outline_initial_state(chapter_locator, course_structure)
self.assertEqual(initial_state['locator_to_show'], chapter_locator)
expanded_locators = initial_state['expanded_locators']
self.assertIn(six.text_type(self.sequential.location), expanded_locators)
self.assertIn(six.text_type(self.vertical.location), expanded_locators)
self.assertIn(str(self.sequential.location), expanded_locators)
self.assertIn(str(self.vertical.location), expanded_locators)
def _create_test_data(self, course_module, create_blocks=False, publish=True, block_types=None):
"""
@@ -516,7 +515,7 @@ class TestCourseOutline(CourseTestCase):
ItemFactory.create(
parent_location=self.vertical.location,
category=block_type,
display_name=u'{} Problem'.format(block_type)
display_name=f'{block_type} Problem'
)
if not publish:
@@ -533,7 +532,7 @@ class TestCourseOutline(CourseTestCase):
expected_blocks.append(
[
reverse_usage_url('container_handler', self.vertical.location),
u'{} Problem'.format(block_type)
f'{block_type} Problem'
]
)
@@ -542,7 +541,7 @@ class TestCourseOutline(CourseTestCase):
[component for component in advanced_modules if component in deprecated_block_types]
)
six.assertCountEqual(self, info['blocks'], expected_blocks)
self.assertCountEqual(info['blocks'], expected_blocks)
self.assertEqual(
info['advance_settings_url'],
reverse_course_url('advanced_settings_handler', course_id)
@@ -604,7 +603,7 @@ class TestCourseReIndex(CourseTestCase):
Set up the for the course outline tests.
"""
super(TestCourseReIndex, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course.start = datetime.datetime(2014, 1, 1, tzinfo=pytz.utc)
modulestore().update_item(self.course, self.user.id)
@@ -694,7 +693,7 @@ class TestCourseReIndex(CourseTestCase):
user=self.user,
size=10,
from_=0,
course_id=six.text_type(self.course.id))
course_id=str(self.course.id))
self.assertEqual(response['total'], 1)
# Start manual reindex
@@ -706,7 +705,7 @@ class TestCourseReIndex(CourseTestCase):
user=self.user,
size=10,
from_=0,
course_id=six.text_type(self.course.id))
course_id=str(self.course.id))
self.assertEqual(response['total'], 1)
@mock.patch('xmodule.video_module.VideoBlock.index_dictionary')
@@ -720,7 +719,7 @@ class TestCourseReIndex(CourseTestCase):
user=self.user,
size=10,
from_=0,
course_id=six.text_type(self.course.id))
course_id=str(self.course.id))
self.assertEqual(response['total'], 1)
# set mocked exception response
@@ -742,7 +741,7 @@ class TestCourseReIndex(CourseTestCase):
user=self.user,
size=10,
from_=0,
course_id=six.text_type(self.course.id))
course_id=str(self.course.id))
self.assertEqual(response['total'], 1)
# set mocked exception response
@@ -764,7 +763,7 @@ class TestCourseReIndex(CourseTestCase):
user=self.user,
size=10,
from_=0,
course_id=six.text_type(self.course.id))
course_id=str(self.course.id))
self.assertEqual(response['total'], 1)
# set mocked exception response
@@ -804,7 +803,7 @@ class TestCourseReIndex(CourseTestCase):
user=self.user,
size=10,
from_=0,
course_id=six.text_type(self.course.id))
course_id=str(self.course.id))
self.assertEqual(response['total'], 1)
# Start manual reindex
@@ -816,7 +815,7 @@ class TestCourseReIndex(CourseTestCase):
user=self.user,
size=10,
from_=0,
course_id=six.text_type(self.course.id))
course_id=str(self.course.id))
self.assertEqual(response['total'], 1)
@mock.patch('xmodule.video_module.VideoBlock.index_dictionary')
@@ -830,7 +829,7 @@ class TestCourseReIndex(CourseTestCase):
user=self.user,
size=10,
from_=0,
course_id=six.text_type(self.course.id))
course_id=str(self.course.id))
self.assertEqual(response['total'], 1)
# set mocked exception response
@@ -852,7 +851,7 @@ class TestCourseReIndex(CourseTestCase):
user=self.user,
size=10,
from_=0,
course_id=six.text_type(self.course.id))
course_id=str(self.course.id))
self.assertEqual(response['total'], 1)
# set mocked exception response
@@ -874,7 +873,7 @@ class TestCourseReIndex(CourseTestCase):
user=self.user,
size=10,
from_=0,
course_id=six.text_type(self.course.id))
course_id=str(self.course.id))
self.assertEqual(response['total'], 1)
# set mocked exception response

View File

@@ -4,9 +4,9 @@ unit tests for course_info views and models.
import json
from unittest.mock import patch # lint-amnesty, pylint: disable=unused-import
from django.test.utils import override_settings # lint-amnesty, pylint: disable=unused-import
from mock import patch # lint-amnesty, pylint: disable=unused-import
from opaque_keys.edx.keys import UsageKey
from cms.djangoapps.contentstore.tests.test_course_settings import CourseTestCase
@@ -141,9 +141,9 @@ class CourseUpdateTest(CourseTestCase): # lint-amnesty, pylint: disable=missing
location.block_type,
block_id=location.block_id
)
update_date = u"January 23, 2014"
update_content = u"Hello world!"
update_data = u"<ol><li><h2>" + update_date + "</h2>" + update_content + "</li></ol>"
update_date = "January 23, 2014"
update_content = "Hello world!"
update_data = "<ol><li><h2>" + update_date + "</h2>" + update_content + "</li></ol>"
course_updates.data = update_data
modulestore().update_item(course_updates, self.user.id)
@@ -151,7 +151,7 @@ class CourseUpdateTest(CourseTestCase): # lint-amnesty, pylint: disable=missing
course_update_url = self.create_update_url()
resp = self.client.get_json(course_update_url)
payload = json.loads(resp.content.decode('utf-8'))
self.assertEqual(payload, [{u'date': update_date, u'content': update_content, u'id': 1}])
self.assertEqual(payload, [{'date': update_date, 'content': update_content, 'id': 1}])
self.assertEqual(len(payload), 1)
# test getting single update item
@@ -159,7 +159,7 @@ class CourseUpdateTest(CourseTestCase): # lint-amnesty, pylint: disable=missing
first_update_url = self.create_update_url(provided_id=payload[0]['id'])
resp = self.client.get_json(first_update_url)
payload = json.loads(resp.content.decode('utf-8'))
self.assertEqual(payload, {u'date': u'January 23, 2014', u'content': u'Hello world!', u'id': 1})
self.assertEqual(payload, {'date': 'January 23, 2014', 'content': 'Hello world!', 'id': 1})
self.assertHTMLEqual(update_date, payload['date'])
self.assertHTMLEqual(update_content, payload['content'])
@@ -175,29 +175,29 @@ class CourseUpdateTest(CourseTestCase): # lint-amnesty, pylint: disable=missing
)
self.assertHTMLEqual(update_content, json.loads(resp.content.decode('utf-8'))['content'])
course_updates = modulestore().get_item(location)
self.assertEqual(course_updates.items, [{u'date': update_date, u'content': update_content, u'id': 1}])
self.assertEqual(course_updates.items, [{'date': update_date, 'content': update_content, 'id': 1}])
# course_updates 'data' field should not update automatically
self.assertEqual(course_updates.data, '')
# test delete course update item (soft delete)
course_updates = modulestore().get_item(location)
self.assertEqual(course_updates.items, [{u'date': update_date, u'content': update_content, u'id': 1}])
self.assertEqual(course_updates.items, [{'date': update_date, 'content': update_content, 'id': 1}])
# now try to delete first update item
resp = self.client.delete(course_update_url + '1')
self.assertEqual(json.loads(resp.content.decode('utf-8')), [])
# confirm that course update is soft deleted ('status' flag set to 'deleted') in db
course_updates = modulestore().get_item(location)
self.assertEqual(course_updates.items,
[{u'date': update_date, u'content': update_content, u'id': 1, u'status': 'deleted'}])
[{'date': update_date, 'content': update_content, 'id': 1, 'status': 'deleted'}])
# now try to get deleted update
resp = self.client.get_json(course_update_url + '1')
payload = json.loads(resp.content.decode('utf-8'))
self.assertEqual(payload.get('error'), u"Course update not found.")
self.assertEqual(payload.get('error'), "Course update not found.")
self.assertEqual(resp.status_code, 404)
# now check that course update don't munges html
update_content = u"""&lt;problem>
update_content = """&lt;problem>
&lt;p>&lt;/p>
&lt;multiplechoiceresponse>
<pre>&lt;problem>
@@ -248,7 +248,7 @@ class CourseUpdateTest(CourseTestCase): # lint-amnesty, pylint: disable=missing
# create a course via the view handler
self.client.ajax_post(course_update_url)
content = u"Sample update"
content = "Sample update"
payload = {'content': content, 'date': 'January 8, 2013'}
resp = self.client.ajax_post(course_update_url, payload)
@@ -266,13 +266,13 @@ class CourseUpdateTest(CourseTestCase): # lint-amnesty, pylint: disable=missing
updates_location = self.course.id.make_usage_key('course_info', 'updates')
self.assertTrue(isinstance(updates_location, UsageKey))
self.assertEqual(updates_location.block_id, u'updates')
self.assertEqual(updates_location.block_id, 'updates')
# check posting on handouts
handouts_location = self.course.id.make_usage_key('course_info', 'handouts')
course_handouts_url = reverse_usage_url('xblock_handler', handouts_location)
content = u"Sample handout"
content = "Sample handout"
payload = {'data': content}
resp = self.client.ajax_post(course_handouts_url, payload)
@@ -292,7 +292,7 @@ class CourseUpdateTest(CourseTestCase): # lint-amnesty, pylint: disable=missing
updates_location = self.course.id.make_usage_key('course_info', 'updates')
self.assertTrue(isinstance(updates_location, UsageKey))
self.assertEqual(updates_location.block_id, u'updates')
self.assertEqual(updates_location.block_id, 'updates')
course_updates = modulestore().get_item(updates_location)
course_update_items = list(reversed(get_course_update_items(course_updates)))
@@ -309,7 +309,7 @@ class CourseUpdateTest(CourseTestCase): # lint-amnesty, pylint: disable=missing
modulestore().update_item(course_updates, self.user.id)
update_content = 'Testing'
update_date = u"January 23, 2014"
update_date = "January 23, 2014"
course_update_url = self.create_update_url()
payload = {'content': update_content, 'date': update_date}
resp = self.client.ajax_post(
@@ -319,4 +319,4 @@ class CourseUpdateTest(CourseTestCase): # lint-amnesty, pylint: disable=missing
self.assertHTMLEqual(update_content, json.loads(resp.content.decode('utf-8'))['content'])
course_updates = modulestore().get_item(updates_location)
del course_updates.items[0]["status"]
self.assertEqual(course_updates.items, [{u'date': update_date, u'content': update_content, u'id': 2}])
self.assertEqual(course_updates.items, [{'date': update_date, 'content': update_content, 'id': 2}])

View File

@@ -3,8 +3,7 @@ Unit tests for credit eligibility UI in Studio.
"""
import mock
import six
from unittest import mock
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.utils import reverse_course_url
@@ -20,9 +19,9 @@ class CreditEligibilityTest(CourseTestCase):
eligibility requirements.
"""
def setUp(self):
super(CreditEligibilityTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course = CourseFactory.create(org='edX', number='dummy', display_name='Credit Course')
self.course_details_url = reverse_course_url('settings_handler', six.text_type(self.course.id))
self.course_details_url = reverse_course_url('settings_handler', str(self.course.id))
@mock.patch.dict("django.conf.settings.FEATURES", {'ENABLE_CREDIT_ELIGIBILITY': False})
def test_course_details_with_disabled_setting(self):
@@ -50,7 +49,7 @@ class CreditEligibilityTest(CourseTestCase):
# verify that credit eligibility requirements block shows if the
# course is set as credit course and it has eligibility requirements
credit_course = CreditCourse(course_key=six.text_type(self.course.id), enabled=True)
credit_course = CreditCourse(course_key=str(self.course.id), enabled=True)
credit_course.save()
self.assertEqual(len(get_credit_requirements(self.course.id)), 0)
# test that after publishing course, minimum grade requirement is added

View File

@@ -4,13 +4,12 @@ Test module for Entrance Exams AJAX callback handler workflows
import json
from unittest.mock import patch
import six
from django.conf import settings
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
from django.test.client import RequestFactory
from milestones.tests.utils import MilestonesTestCaseMixin
from mock import patch
from opaque_keys.edx.keys import UsageKey
from cms.djangoapps.contentstore.tests.utils import AjaxEnabledTestClient, CourseTestCase
@@ -40,18 +39,18 @@ class EntranceExamHandlerTests(CourseTestCase, MilestonesTestCaseMixin):
"""
Shared scaffolding for individual test runs
"""
super(EntranceExamHandlerTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course_key = self.course.id
self.usage_key = self.course.location
self.course_url = '/course/{}'.format(six.text_type(self.course.id))
self.exam_url = '/course/{}/entrance_exam/'.format(six.text_type(self.course.id))
self.course_url = '/course/{}'.format(str(self.course.id))
self.exam_url = '/course/{}/entrance_exam/'.format(str(self.course.id))
self.milestone_relationship_types = milestones_helpers.get_milestone_relationship_types()
def test_entrance_exam_milestone_addition(self):
"""
Unit Test: test addition of entrance exam milestone content
"""
parent_locator = six.text_type(self.course.location)
parent_locator = str(self.course.location)
created_block = create_xblock(
parent_locator=parent_locator,
user=self.user,
@@ -61,8 +60,8 @@ class EntranceExamHandlerTests(CourseTestCase, MilestonesTestCaseMixin):
)
add_entrance_exam_milestone(self.course.id, created_block)
content_milestones = milestones_helpers.get_course_content_milestones(
six.text_type(self.course.id),
six.text_type(created_block.location),
str(self.course.id),
str(created_block.location),
self.milestone_relationship_types['FULFILLS']
)
self.assertTrue(len(content_milestones))
@@ -72,7 +71,7 @@ class EntranceExamHandlerTests(CourseTestCase, MilestonesTestCaseMixin):
"""
Unit Test: test removal of entrance exam milestone content
"""
parent_locator = six.text_type(self.course.location)
parent_locator = str(self.course.location)
created_block = create_xblock(
parent_locator=parent_locator,
user=self.user,
@@ -82,8 +81,8 @@ class EntranceExamHandlerTests(CourseTestCase, MilestonesTestCaseMixin):
)
add_entrance_exam_milestone(self.course.id, created_block)
content_milestones = milestones_helpers.get_course_content_milestones(
six.text_type(self.course.id),
six.text_type(created_block.location),
str(self.course.id),
str(created_block.location),
self.milestone_relationship_types['FULFILLS']
)
self.assertEqual(len(content_milestones), 1)
@@ -92,8 +91,8 @@ class EntranceExamHandlerTests(CourseTestCase, MilestonesTestCaseMixin):
request.user = user
remove_entrance_exam_milestone_reference(request, self.course.id)
content_milestones = milestones_helpers.get_course_content_milestones(
six.text_type(self.course.id),
six.text_type(created_block.location),
str(self.course.id),
str(created_block.location),
self.milestone_relationship_types['FULFILLS']
)
self.assertEqual(len(content_milestones), 0)
@@ -113,9 +112,9 @@ class EntranceExamHandlerTests(CourseTestCase, MilestonesTestCaseMixin):
self.assertTrue(metadata['entrance_exam_enabled'])
self.assertIsNotNone(metadata['entrance_exam_minimum_score_pct'])
self.assertIsNotNone(metadata['entrance_exam_id']['value'])
self.assertTrue(len(milestones_helpers.get_course_milestones(six.text_type(self.course.id))))
self.assertTrue(len(milestones_helpers.get_course_milestones(str(self.course.id))))
content_milestones = milestones_helpers.get_course_content_milestones(
six.text_type(self.course.id),
str(self.course.id),
metadata['entrance_exam_id']['value'],
self.milestone_relationship_types['FULFILLS']
)
@@ -181,11 +180,11 @@ class EntranceExamHandlerTests(CourseTestCase, MilestonesTestCaseMixin):
)
user.set_password('test')
user.save()
milestones = milestones_helpers.get_course_milestones(six.text_type(self.course_key))
milestones = milestones_helpers.get_course_milestones(str(self.course_key))
self.assertEqual(len(milestones), 1)
milestone_key = '{}.{}'.format(milestones[0]['namespace'], milestones[0]['name'])
paths = milestones_helpers.get_course_milestones_fulfillment_paths(
six.text_type(self.course_key),
str(self.course_key),
milestones_helpers.serialize_user(user)
)

View File

@@ -1,5 +1,3 @@
#-*- coding: utf-8 -*-
"""
Exam Settings View Tests
"""
@@ -32,7 +30,7 @@ class TestExamSettingsView(CourseTestCase, UrlResetMixin):
"""
Set up the for the exam settings view tests.
"""
super(TestExamSettingsView, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.reset_urls()
@staticmethod

View File

@@ -4,10 +4,9 @@ Unit tests for the gating feature in Studio
import json
from unittest.mock import patch
import ddt
import six
from mock import patch
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.utils import reverse_usage_url
@@ -30,7 +29,7 @@ class TestSubsectionGating(CourseTestCase):
"""
Initial data setup
"""
super(TestSubsectionGating, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# Enable subsection gating for the test course
self.course.enable_subsection_gating = True
@@ -90,13 +89,13 @@ class TestSubsectionGating(CourseTestCase):
self.client.ajax_post(
self.seq2_url,
data={'prereqUsageKey': six.text_type(self.seq1.location), 'prereqMinScore': '100',
data={'prereqUsageKey': str(self.seq1.location), 'prereqMinScore': '100',
'prereqMinCompletion': '100'}
)
mock_set_required_content.assert_called_with(
self.course.id,
self.seq2.location,
six.text_type(self.seq1.location),
str(self.seq1.location),
'100',
'100'
)
@@ -133,17 +132,17 @@ class TestSubsectionGating(CourseTestCase):
mock_is_prereq, mock_get_required_content, mock_get_prereqs
):
mock_is_prereq.return_value = True
mock_get_required_content.return_value = six.text_type(self.seq1.location), min_score, min_completion
mock_get_required_content.return_value = str(self.seq1.location), min_score, min_completion
mock_get_prereqs.return_value = [
{'namespace': '{}{}'.format(six.text_type(self.seq1.location), GATING_NAMESPACE_QUALIFIER)},
{'namespace': '{}{}'.format(six.text_type(self.seq2.location), GATING_NAMESPACE_QUALIFIER)}
{'namespace': '{}{}'.format(str(self.seq1.location), GATING_NAMESPACE_QUALIFIER)},
{'namespace': '{}{}'.format(str(self.seq2.location), GATING_NAMESPACE_QUALIFIER)}
]
resp = json.loads(self.client.get_json(self.seq2_url).content.decode('utf-8'))
mock_is_prereq.assert_called_with(self.course.id, self.seq2.location)
mock_get_required_content.assert_called_with(self.course.id, self.seq2.location)
mock_get_prereqs.assert_called_with(self.course.id)
self.assertTrue(resp['is_prereq'])
self.assertEqual(resp['prereq'], six.text_type(self.seq1.location))
self.assertEqual(resp['prereq'], str(self.seq1.location))
self.assertEqual(resp['prereq_min_score'], min_score)
self.assertEqual(resp['prereq_min_completion'], min_completion)
self.assertEqual(resp['visibility_state'], VisibilityState.gated)

View File

@@ -1,5 +1,3 @@
#-*- coding: utf-8 -*-
"""
Group Configuration Tests.
"""
@@ -7,11 +5,9 @@ Group Configuration Tests.
import json
from operator import itemgetter
from unittest.mock import patch
import ddt
import six
from mock import patch
from six.moves import range
from cms.djangoapps.contentstore.course_group_config import (
CONTENT_GROUP_CONFIGURATION_NAME,
@@ -29,24 +25,24 @@ from xmodule.partitions.partitions import ENROLLMENT_TRACK_PARTITION_ID, Group,
from xmodule.validation import StudioValidation, StudioValidationMessage
GROUP_CONFIGURATION_JSON = {
u'name': u'Test name',
u'scheme': u'random',
u'description': u'Test description',
u'version': UserPartition.VERSION,
u'groups': [
'name': 'Test name',
'scheme': 'random',
'description': 'Test description',
'version': UserPartition.VERSION,
'groups': [
{
u'name': u'Group A',
u'version': 1,
'name': 'Group A',
'version': 1,
}, {
u'name': u'Group B',
u'version': 1,
'name': 'Group B',
'version': 1,
},
],
}
# pylint: disable=no-member
class HelperMethods(object):
class HelperMethods:
"""
Mixin that provides useful methods for Group Configuration tests.
"""
@@ -61,12 +57,12 @@ class HelperMethods(object):
sequential = ItemFactory.create(
category='sequential',
parent_location=self.course.location,
display_name=u'Test Subsection {}'.format(name_suffix)
display_name=f'Test Subsection {name_suffix}'
)
vertical = ItemFactory.create(
category='vertical',
parent_location=sequential.location,
display_name=u'Test Unit {}'.format(name_suffix)
display_name=f'Test Unit {name_suffix}'
)
c0_url = self.course.id.make_usage_key("vertical", "split_test_cond0")
c1_url = self.course.id.make_usage_key("vertical", "split_test_cond1")
@@ -75,7 +71,7 @@ class HelperMethods(object):
category='split_test',
parent_location=vertical.location,
user_partition_id=cid,
display_name=u"Test Content Experiment {}{}".format(name_suffix, special_characters),
display_name=f"Test Content Experiment {name_suffix}{special_characters}",
group_id_to_child={"0": c0_url, "1": c1_url, "2": c2_url}
)
ItemFactory.create(
@@ -102,7 +98,7 @@ class HelperMethods(object):
problem = ItemFactory.create(
category='problem',
parent_location=c1_vertical.location,
display_name=u"Test Problem"
display_name="Test Problem"
)
self.client.ajax_post(
reverse_usage_url("xblock_handler", problem.location),
@@ -130,20 +126,20 @@ class HelperMethods(object):
subsection = ItemFactory.create(
category='sequential',
parent_location=self.course.location,
display_name=u"Test Subsection {}".format(name_suffix)
display_name=f"Test Subsection {name_suffix}"
)
vertical_parent_location = subsection.location
vertical = ItemFactory.create(
category='vertical',
parent_location=vertical_parent_location,
display_name=u"Test Unit {}".format(name_suffix)
display_name=f"Test Unit {name_suffix}"
)
problem = ItemFactory.create(
category='problem',
parent_location=vertical.location,
display_name=u"Test Problem {}{}".format(name_suffix, special_characters)
display_name=f"Test Problem {name_suffix}{special_characters}"
)
group_access_content = {'group_access': {cid: [group_id]}}
@@ -175,7 +171,7 @@ class HelperMethods(object):
# pylint: disable=no-member
class GroupConfigurationsBaseTestCase(object):
class GroupConfigurationsBaseTestCase:
"""
Mixin with base test cases for the group configurations.
"""
@@ -199,17 +195,17 @@ class GroupConfigurationsBaseTestCase(object):
bad_jsons = [
# must have name of the configuration
{
u'description': 'Test description',
u'groups': [
{u'name': u'Group A'},
{u'name': u'Group B'},
'description': 'Test description',
'groups': [
{'name': 'Group A'},
{'name': 'Group B'},
],
},
# must have at least one group
{
u'name': u'Test name',
u'description': u'Test description',
u'groups': [],
'name': 'Test name',
'description': 'Test description',
'groups': [],
},
# an empty json
{},
@@ -233,7 +229,7 @@ class GroupConfigurationsBaseTestCase(object):
Test invalid json handling.
"""
# No property name.
invalid_json = u"{u'name': 'Test Name', []}"
invalid_json = "{'name': 'Test Name', []}"
response = self.client.post(
self._url(),
@@ -296,16 +292,16 @@ class GroupConfigurationsListHandlerTestCase(CourseTestCase, GroupConfigurations
Test that you can create a group configuration.
"""
expected = {
u'description': u'Test description',
u'name': u'Test name',
u'scheme': u'random',
u'version': UserPartition.VERSION,
u'groups': [
{u'name': u'Group A', u'version': 1},
{u'name': u'Group B', u'version': 1},
'description': 'Test description',
'name': 'Test name',
'scheme': 'random',
'version': UserPartition.VERSION,
'groups': [
{'name': 'Group A', 'version': 1},
{'name': 'Group B', 'version': 1},
],
u'parameters': {},
u'active': True
'parameters': {},
'active': True
}
response = self.client.ajax_post(
self._url(),
@@ -323,10 +319,10 @@ class GroupConfigurationsListHandlerTestCase(CourseTestCase, GroupConfigurations
# Verify that user_partitions in the course contains the new group configuration.
user_partititons = self.course.user_partitions
self.assertEqual(len(user_partititons), 1)
self.assertEqual(user_partititons[0].name, u'Test name')
self.assertEqual(user_partititons[0].name, 'Test name')
self.assertEqual(len(user_partititons[0].groups), 2)
self.assertEqual(user_partititons[0].groups[0].name, u'Group A')
self.assertEqual(user_partititons[0].groups[1].name, u'Group B')
self.assertEqual(user_partititons[0].groups[0].name, 'Group A')
self.assertEqual(user_partititons[0].groups[1].name, 'Group B')
self.assertEqual(user_partititons[0].parameters, {})
def test_lazily_creates_cohort_configuration(self):
@@ -346,7 +342,7 @@ class GroupConfigurationsListHandlerTestCase(CourseTestCase, GroupConfigurations
"""
group_config = dict(GROUP_CONFIGURATION_JSON)
group_config['scheme'] = scheme_id
group_config.setdefault('parameters', {})['course_id'] = six.text_type(self.course.id)
group_config.setdefault('parameters', {})['course_id'] = str(self.course.id)
response = self.client.ajax_post(
self._url(),
data=group_config
@@ -377,17 +373,17 @@ class GroupConfigurationsDetailHandlerTestCase(CourseTestCase, GroupConfiguratio
PUT new content group.
"""
expected = {
u'id': 666,
u'name': u'Test name',
u'scheme': u'cohort',
u'description': u'Test description',
u'version': UserPartition.VERSION,
u'groups': [
{u'id': 0, u'name': u'Group A', u'version': 1, u'usage': []},
{u'id': 1, u'name': u'Group B', u'version': 1, u'usage': []},
'id': 666,
'name': 'Test name',
'scheme': 'cohort',
'description': 'Test description',
'version': UserPartition.VERSION,
'groups': [
{'id': 0, 'name': 'Group A', 'version': 1, 'usage': []},
{'id': 1, 'name': 'Group B', 'version': 1, 'usage': []},
],
u'parameters': {},
u'active': True,
'parameters': {},
'active': True,
}
response = self.client.put(
self._url(cid=666),
@@ -403,10 +399,10 @@ class GroupConfigurationsDetailHandlerTestCase(CourseTestCase, GroupConfiguratio
# Verify that user_partitions in the course contains the new group configuration.
user_partitions = self.course.user_partitions
self.assertEqual(len(user_partitions), 1)
self.assertEqual(user_partitions[0].name, u'Test name')
self.assertEqual(user_partitions[0].name, 'Test name')
self.assertEqual(len(user_partitions[0].groups), 2)
self.assertEqual(user_partitions[0].groups[0].name, u'Group A')
self.assertEqual(user_partitions[0].groups[1].name, u'Group B')
self.assertEqual(user_partitions[0].groups[0].name, 'Group A')
self.assertEqual(user_partitions[0].groups[1].name, 'Group B')
self.assertEqual(user_partitions[0].parameters, {})
def test_can_edit_content_group(self):
@@ -417,17 +413,17 @@ class GroupConfigurationsDetailHandlerTestCase(CourseTestCase, GroupConfiguratio
self.save_course()
expected = {
u'id': self.ID,
u'name': u'New Test name',
u'scheme': u'cohort',
u'description': u'New Test description',
u'version': UserPartition.VERSION,
u'groups': [
{u'id': 0, u'name': u'New Group Name', u'version': 1, u'usage': []},
{u'id': 2, u'name': u'Group C', u'version': 1, u'usage': []},
'id': self.ID,
'name': 'New Test name',
'scheme': 'cohort',
'description': 'New Test description',
'version': UserPartition.VERSION,
'groups': [
{'id': 0, 'name': 'New Group Name', 'version': 1, 'usage': []},
{'id': 2, 'name': 'Group C', 'version': 1, 'usage': []},
],
u'parameters': {},
u'active': True,
'parameters': {},
'active': True,
}
response = self.client.put(
@@ -445,10 +441,10 @@ class GroupConfigurationsDetailHandlerTestCase(CourseTestCase, GroupConfiguratio
user_partititons = self.course.user_partitions
self.assertEqual(len(user_partititons), 1)
self.assertEqual(user_partititons[0].name, u'New Test name')
self.assertEqual(user_partititons[0].name, 'New Test name')
self.assertEqual(len(user_partititons[0].groups), 2)
self.assertEqual(user_partititons[0].groups[0].name, u'New Group Name')
self.assertEqual(user_partititons[0].groups[1].name, u'Group C')
self.assertEqual(user_partititons[0].groups[0].name, 'New Group Name')
self.assertEqual(user_partititons[0].groups[1].name, 'Group C')
self.assertEqual(user_partititons[0].parameters, {})
def test_can_delete_content_group(self):
@@ -521,18 +517,18 @@ class GroupConfigurationsDetailHandlerTestCase(CourseTestCase, GroupConfiguratio
PUT new group configuration when no configurations exist in the course.
"""
expected = {
u'id': 999,
u'name': u'Test name',
u'scheme': u'random',
u'description': u'Test description',
u'version': UserPartition.VERSION,
u'groups': [
{u'id': 0, u'name': u'Group A', u'version': 1},
{u'id': 1, u'name': u'Group B', u'version': 1},
'id': 999,
'name': 'Test name',
'scheme': 'random',
'description': 'Test description',
'version': UserPartition.VERSION,
'groups': [
{'id': 0, 'name': 'Group A', 'version': 1},
{'id': 1, 'name': 'Group B', 'version': 1},
],
u'usage': [],
u'parameters': {},
u'active': True,
'usage': [],
'parameters': {},
'active': True,
}
response = self.client.put(
@@ -548,10 +544,10 @@ class GroupConfigurationsDetailHandlerTestCase(CourseTestCase, GroupConfiguratio
# Verify that user_partitions in the course contains the new group configuration.
user_partitions = self.course.user_partitions
self.assertEqual(len(user_partitions), 1)
self.assertEqual(user_partitions[0].name, u'Test name')
self.assertEqual(user_partitions[0].name, 'Test name')
self.assertEqual(len(user_partitions[0].groups), 2)
self.assertEqual(user_partitions[0].groups[0].name, u'Group A')
self.assertEqual(user_partitions[0].groups[1].name, u'Group B')
self.assertEqual(user_partitions[0].groups[0].name, 'Group A')
self.assertEqual(user_partitions[0].groups[1].name, 'Group B')
self.assertEqual(user_partitions[0].parameters, {})
def test_can_edit_group_configuration(self):
@@ -562,18 +558,18 @@ class GroupConfigurationsDetailHandlerTestCase(CourseTestCase, GroupConfiguratio
self.save_course()
expected = {
u'id': self.ID,
u'name': u'New Test name',
u'scheme': u'random',
u'description': u'New Test description',
u'version': UserPartition.VERSION,
u'groups': [
{u'id': 0, u'name': u'New Group Name', u'version': 1},
{u'id': 2, u'name': u'Group C', u'version': 1},
'id': self.ID,
'name': 'New Test name',
'scheme': 'random',
'description': 'New Test description',
'version': UserPartition.VERSION,
'groups': [
{'id': 0, 'name': 'New Group Name', 'version': 1},
{'id': 2, 'name': 'Group C', 'version': 1},
],
u'usage': [],
u'parameters': {},
u'active': True,
'usage': [],
'parameters': {},
'active': True,
}
response = self.client.put(
@@ -591,10 +587,10 @@ class GroupConfigurationsDetailHandlerTestCase(CourseTestCase, GroupConfiguratio
user_partititons = self.course.user_partitions
self.assertEqual(len(user_partititons), 1)
self.assertEqual(user_partititons[0].name, u'New Test name')
self.assertEqual(user_partititons[0].name, 'New Test name')
self.assertEqual(len(user_partititons[0].groups), 2)
self.assertEqual(user_partititons[0].groups[0].name, u'New Group Name')
self.assertEqual(user_partititons[0].groups[1].name, u'Group C')
self.assertEqual(user_partititons[0].groups[0].name, 'New Group Name')
self.assertEqual(user_partititons[0].groups[1].name, 'Group C')
self.assertEqual(user_partititons[0].parameters, {})
def test_can_delete_group_configuration(self):
@@ -663,7 +659,7 @@ class GroupConfigurationsDetailHandlerTestCase(CourseTestCase, GroupConfiguratio
"""
group_config = dict(GROUP_CONFIGURATION_JSON)
group_config['scheme'] = scheme_id
group_config.setdefault('parameters', {})['course_id'] = six.text_type(self.course.id)
group_config.setdefault('parameters', {})['course_id'] = str(self.course.id)
response = self.client.ajax_post(
self._url(),
data=group_config
@@ -681,7 +677,7 @@ class GroupConfigurationsDetailHandlerTestCase(CourseTestCase, GroupConfiguratio
"""
group_config = dict(GROUP_CONFIGURATION_JSON)
group_config['scheme'] = scheme_id
group_config.setdefault('parameters', {})['course_id'] = six.text_type(self.course.id)
group_config.setdefault('parameters', {})['course_id'] = str(self.course.id)
response = self.client.put(
self._url(cid=partition_id),
data=json.dumps(group_config),
@@ -722,8 +718,8 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
{'id': 1, 'name': 'Group B', 'version': 1, 'usage': usage_for_group},
{'id': 2, 'name': 'Group C', 'version': 1, 'usage': []},
],
u'parameters': {},
u'active': True,
'parameters': {},
'active': True,
}
def test_content_group_not_used(self):
@@ -741,15 +737,15 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
"""
self._add_user_partitions(count=1, scheme_id='cohort')
vertical, __ = self._create_problem_with_content_group(
cid=0, group_id=1, name_suffix='0', special_characters=u"JOSÉ ANDRÉS"
cid=0, group_id=1, name_suffix='0', special_characters="JOSÉ ANDRÉS"
)
actual = self._get_user_partition('cohort')
expected = self._get_expected_content_group(
usage_for_group=[
{
'url': u"/container/{}".format(vertical.location),
'label': u"Test Unit 0 / Test Problem 0JOSÉ ANDRÉS"
'url': f"/container/{vertical.location}",
'label': "Test Unit 0 / Test Problem 0JOSÉ ANDRÉS"
}
]
)
@@ -767,7 +763,7 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
expected = self._get_expected_content_group(usage_for_group=[
{
'url': '/container/{}'.format(vertical.location),
'url': f'/container/{vertical.location}',
'label': 'Test Unit 0 / Test Problem 0'
}
])
@@ -792,7 +788,7 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
if module_store_type == ModuleStoreEnum.Type.mongo:
expected = self._get_expected_content_group(usage_for_group=[
{
'url': '/container/{}'.format(vertical.location),
'url': f'/container/{vertical.location}',
'label': 'Test Unit 0 / Test Problem 0'
}
])
@@ -818,11 +814,11 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
expected = self._get_expected_content_group(usage_for_group=[
{
'url': '/container/{}'.format(vertical1.location),
'url': f'/container/{vertical1.location}',
'label': 'Test Unit 1 / Test Problem 1'
},
{
'url': '/container/{}'.format(vertical.location),
'url': f'/container/{vertical.location}',
'label': 'Test Unit 0 / Test Problem 0'
}
])
@@ -884,13 +880,13 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
'groups': [
{'id': 3, 'name': 'Problem Group', 'version': 1, 'usage': [
{
'url': '/container/{}'.format(split_test.location),
'url': f'/container/{split_test.location}',
'label': 'Condition 1 vertical / Test Problem'
}
]},
],
u'parameters': {},
u'active': True,
'parameters': {},
'active': True,
}
actual = self._get_user_partition('cohort')
@@ -933,17 +929,17 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
'groups': [
{'id': 0, 'name': 'Group', 'version': 1, 'usage': [
{
'url': u"/container/{}".format(vertical.location),
'label': u"Test Subsection 0 / Test Unit 0"
'url': f"/container/{vertical.location}",
'label': "Test Subsection 0 / Test Unit 0"
},
{
'url': u"/container/{}".format(vertical.location),
'label': u"Test Unit 0 / Test Problem 0"
'url': f"/container/{vertical.location}",
'label': "Test Unit 0 / Test Problem 0"
}
]},
],
u'parameters': {},
u'active': True,
'parameters': {},
'active': True,
}
self.maxDiff = None
@@ -972,7 +968,7 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
{'id': 2, 'name': 'Group C', 'version': 1},
],
'usage': [{
'url': '/container/{}'.format(split_test.location),
'url': f'/container/{split_test.location}',
'label': 'Test Unit 0 / Test Content Experiment 0',
'validation': None,
}],
@@ -1002,7 +998,7 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
characters are being used in content experiment
"""
self._add_user_partitions(count=1)
__, split_test, __ = self._create_content_experiment(cid=0, name_suffix='0', special_characters=u"JOSÉ ANDRÉS")
__, split_test, __ = self._create_content_experiment(cid=0, name_suffix='0', special_characters="JOSÉ ANDRÉS")
actual = GroupConfiguration.get_split_test_partitions_with_usage(self.store, self.course, )
@@ -1019,7 +1015,7 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
],
'usage': [{
'url': reverse_usage_url("container_handler", split_test.location),
'label': u"Test Unit 0 / Test Content Experiment 0JOSÉ ANDRÉS",
'label': "Test Unit 0 / Test Content Experiment 0JOSÉ ANDRÉS",
'validation': None,
}],
'parameters': {},
@@ -1051,11 +1047,11 @@ class GroupConfigurationsUsageInfoTestCase(CourseTestCase, HelperMethods):
{'id': 2, 'name': 'Group C', 'version': 1},
],
'usage': [{
'url': '/container/{}'.format(split_test.location),
'url': f'/container/{split_test.location}',
'label': 'Test Unit 0 / Test Content Experiment 0',
'validation': None,
}, {
'url': '/container/{}'.format(split_test1.location),
'url': f'/container/{split_test1.location}',
'label': 'Test Unit 1 / Test Content Experiment 1',
'validation': None,
}],
@@ -1192,9 +1188,9 @@ class GroupConfigurationsValidationTestCase(CourseTestCase, HelperMethods):
"""
Tests if validation message is present (error case).
"""
mocked_message = StudioValidationMessage(StudioValidationMessage.ERROR, u"Validation message")
mocked_message = StudioValidationMessage(StudioValidationMessage.ERROR, "Validation message")
expected_result = StudioValidationMessage(
StudioValidationMessage.ERROR, u"This content experiment has issues that affect content visibility."
StudioValidationMessage.ERROR, "This content experiment has issues that affect content visibility."
)
self.verify_validation_add_usage_info(expected_result, mocked_message) # pylint: disable=no-value-for-parameter
@@ -1202,9 +1198,9 @@ class GroupConfigurationsValidationTestCase(CourseTestCase, HelperMethods):
"""
Tests if validation message is present (warning case).
"""
mocked_message = StudioValidationMessage(StudioValidationMessage.WARNING, u"Validation message")
mocked_message = StudioValidationMessage(StudioValidationMessage.WARNING, "Validation message")
expected_result = StudioValidationMessage(
StudioValidationMessage.WARNING, u"This content experiment has issues that affect content visibility."
StudioValidationMessage.WARNING, "This content experiment has issues that affect content visibility."
)
self.verify_validation_add_usage_info(expected_result, mocked_message) # pylint: disable=no-value-for-parameter
@@ -1233,9 +1229,9 @@ class GroupConfigurationsValidationTestCase(CourseTestCase, HelperMethods):
"""
Tests if validation message is present when updating usage info.
"""
mocked_message = StudioValidationMessage(StudioValidationMessage.WARNING, u"Validation message")
mocked_message = StudioValidationMessage(StudioValidationMessage.WARNING, "Validation message")
expected_result = StudioValidationMessage(
StudioValidationMessage.WARNING, u"This content experiment has issues that affect content visibility."
StudioValidationMessage.WARNING, "This content experiment has issues that affect content visibility."
)
# pylint: disable=no-value-for-parameter
self.verify_validation_update_usage_info(expected_result, mocked_message)

View File

@@ -1,5 +1,3 @@
#-*- coding: utf-8 -*-
"""
Course Header Menu Tests.
"""
@@ -31,7 +29,7 @@ class TestHeaderMenu(CourseTestCase, UrlResetMixin):
"""
Set up the for the course header menu tests.
"""
super(TestHeaderMenu, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.reset_urls()
def test_header_menu_without_web_certs_enabled(self):

View File

@@ -3,7 +3,6 @@ Unit tests for helpers.py.
"""
import six
from django.utils import http
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
@@ -20,7 +19,7 @@ class HelpersTestCase(CourseTestCase):
def test_xblock_studio_url(self):
# Verify course URL
course_url = u'/course/{}'.format(six.text_type(self.course.id))
course_url = '/course/{}'.format(str(self.course.id))
self.assertEqual(xblock_studio_url(self.course), course_url)
# Verify chapter URL
@@ -28,7 +27,7 @@ class HelpersTestCase(CourseTestCase):
display_name="Week 1")
self.assertEqual(
xblock_studio_url(chapter),
u'{}?show={}'.format(course_url, http.urlquote(str(chapter.location).encode()))
'{}?show={}'.format(course_url, http.urlquote(str(chapter.location).encode()))
)
# Verify sequential URL
@@ -36,18 +35,18 @@ class HelpersTestCase(CourseTestCase):
display_name="Lesson 1")
self.assertEqual(
xblock_studio_url(sequential),
u'{}?show={}'.format(course_url, http.urlquote(str(sequential.location).encode()))
'{}?show={}'.format(course_url, http.urlquote(str(sequential.location).encode()))
)
# Verify unit URL
vertical = ItemFactory.create(parent_location=sequential.location, category='vertical',
display_name='Unit')
self.assertEqual(xblock_studio_url(vertical), u'/container/{}'.format(vertical.location))
self.assertEqual(xblock_studio_url(vertical), f'/container/{vertical.location}')
# Verify child vertical URL
child_vertical = ItemFactory.create(parent_location=vertical.location, category='vertical',
display_name='Child Vertical')
self.assertEqual(xblock_studio_url(child_vertical), u'/container/{}'.format(child_vertical.location))
self.assertEqual(xblock_studio_url(child_vertical), f'/container/{child_vertical.location}')
# Verify video URL
video = ItemFactory.create(parent_location=child_vertical.location, category="video",
@@ -56,37 +55,37 @@ class HelpersTestCase(CourseTestCase):
# Verify library URL
library = LibraryFactory.create()
expected_url = u'/library/{}'.format(six.text_type(library.location.library_key))
expected_url = '/library/{}'.format(str(library.location.library_key))
self.assertEqual(xblock_studio_url(library), expected_url)
def test_xblock_type_display_name(self):
# Verify chapter type display name
chapter = ItemFactory.create(parent_location=self.course.location, category='chapter')
self.assertEqual(xblock_type_display_name(chapter), u'Section')
self.assertEqual(xblock_type_display_name('chapter'), u'Section')
self.assertEqual(xblock_type_display_name(chapter), 'Section')
self.assertEqual(xblock_type_display_name('chapter'), 'Section')
# Verify sequential type display name
sequential = ItemFactory.create(parent_location=chapter.location, category='sequential')
self.assertEqual(xblock_type_display_name(sequential), u'Subsection')
self.assertEqual(xblock_type_display_name('sequential'), u'Subsection')
self.assertEqual(xblock_type_display_name(sequential), 'Subsection')
self.assertEqual(xblock_type_display_name('sequential'), 'Subsection')
# Verify unit type display names
vertical = ItemFactory.create(parent_location=sequential.location, category='vertical')
self.assertEqual(xblock_type_display_name(vertical), u'Unit')
self.assertEqual(xblock_type_display_name('vertical'), u'Unit')
self.assertEqual(xblock_type_display_name(vertical), 'Unit')
self.assertEqual(xblock_type_display_name('vertical'), 'Unit')
# Verify child vertical type display name
child_vertical = ItemFactory.create(parent_location=vertical.location, category='vertical',
display_name='Child Vertical')
self.assertEqual(xblock_type_display_name(child_vertical), u'Vertical')
self.assertEqual(xblock_type_display_name(child_vertical), 'Vertical')
# Verify video type display names
video = ItemFactory.create(parent_location=vertical.location, category="video")
self.assertEqual(xblock_type_display_name(video), u'Video')
self.assertEqual(xblock_type_display_name('video'), u'Video')
self.assertEqual(xblock_type_display_name(video), 'Video')
self.assertEqual(xblock_type_display_name('video'), 'Video')
# Verify split test type display names
split_test = ItemFactory.create(parent_location=vertical.location, category="split_test")
self.assertEqual(xblock_type_display_name(split_test), u'Content Experiment')
self.assertEqual(xblock_type_display_name('split_test'), u'Content Experiment')
self.assertEqual(xblock_type_display_name(split_test), 'Content Experiment')
self.assertEqual(xblock_type_display_name('split_test'), 'Content Experiment')

View File

@@ -10,19 +10,18 @@ import re
import shutil
import tarfile
import tempfile
from io import BytesIO
from unittest.mock import Mock, patch
from uuid import uuid4
import ddt
import lxml
import six
from django.conf import settings
from django.core.files.storage import FileSystemStorage
from django.test.utils import override_settings
from milestones.tests.utils import MilestonesTestCaseMixin
from mock import Mock, patch
from opaque_keys.edx.locator import LibraryLocator
from path import Path as path
from six.moves import zip
from storages.backends.s3boto import S3BotoStorage
from storages.backends.s3boto3 import S3Boto3Storage
from user_tasks.models import UserTaskStatus
@@ -31,10 +30,10 @@ from cms.djangoapps.contentstore.tests.test_libraries import LibraryTestCase
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.utils import reverse_course_url
from cms.djangoapps.models.settings.course_metadata import CourseMetadata
from openedx.core.lib.extract_tar import safetar_extractall
from common.djangoapps.student import auth
from common.djangoapps.student.roles import CourseInstructorRole, CourseStaffRole
from common.djangoapps.util import milestones_helpers
from openedx.core.lib.extract_tar import safetar_extractall
from xmodule.contentstore.django import contentstore
from xmodule.modulestore import LIBRARY_ROOT, ModuleStoreEnum
from xmodule.modulestore.django import modulestore
@@ -57,7 +56,7 @@ class ImportEntranceExamTestCase(CourseTestCase, MilestonesTestCaseMixin):
"""
def setUp(self):
super(ImportEntranceExamTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.url = reverse_course_url('import_handler', self.course.id)
self.content_dir = path(tempfile.mkdtemp())
self.addCleanup(shutil.rmtree, self.content_dir)
@@ -107,7 +106,7 @@ class ImportEntranceExamTestCase(CourseTestCase, MilestonesTestCaseMixin):
"""
Check that pre existed entrance exam content should be overwrite with the imported course.
"""
exam_url = '/course/{}/entrance_exam/'.format(six.text_type(self.course.id))
exam_url = '/course/{}/entrance_exam/'.format(str(self.course.id))
resp = self.client.post(exam_url, {'entrance_exam_minimum_score_pct': 0.5}, http_accept='application/json')
self.assertEqual(resp.status_code, 201)
@@ -117,9 +116,9 @@ class ImportEntranceExamTestCase(CourseTestCase, MilestonesTestCaseMixin):
self.assertTrue(metadata['entrance_exam_enabled'])
self.assertIsNotNone(metadata['entrance_exam_minimum_score_pct'])
self.assertEqual(metadata['entrance_exam_minimum_score_pct']['value'], 0.5)
self.assertTrue(len(milestones_helpers.get_course_milestones(six.text_type(self.course.id))))
self.assertTrue(len(milestones_helpers.get_course_milestones(str(self.course.id))))
content_milestones = milestones_helpers.get_course_content_milestones(
six.text_type(self.course.id),
str(self.course.id),
metadata['entrance_exam_id']['value'],
milestones_helpers.get_milestone_relationship_types()['FULFILLS']
)
@@ -145,7 +144,7 @@ class ImportTestCase(CourseTestCase):
CREATE_USER = True
def setUp(self):
super(ImportTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.url = reverse_course_url('import_handler', self.course.id)
self.content_dir = path(tempfile.mkdtemp())
self.addCleanup(shutil.rmtree, self.content_dir)
@@ -538,7 +537,7 @@ class ExportTestCase(CourseTestCase):
"""
Sets up the test course.
"""
super(ExportTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.url = reverse_course_url('export_handler', self.course.id)
self.status_url = reverse_course_url('export_status_handler', self.course.id)
@@ -577,7 +576,7 @@ class ExportTestCase(CourseTestCase):
for item in resp.streaming_content:
resp_content += item
buff = six.BytesIO(resp_content)
buff = BytesIO(resp_content)
return tarfile.open(fileobj=buff)
def _verify_export_succeeded(self, resp):
@@ -854,7 +853,7 @@ class TestLibraryImportExport(CourseTestCase):
"""
def setUp(self):
super(TestLibraryImportExport, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.export_dir = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, self.export_dir, ignore_errors=True)
@@ -912,7 +911,7 @@ class TestCourseExportImport(LibraryTestCase):
"""
def setUp(self):
super(TestCourseExportImport, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.export_dir = tempfile.mkdtemp()
# Create a problem in library
@@ -1035,7 +1034,7 @@ class TestCourseExportImportProblem(CourseTestCase):
"""
def setUp(self):
super(TestCourseExportImportProblem, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.export_dir = tempfile.mkdtemp()
self.source_course = CourseFactory.create(default_store=ModuleStoreEnum.Type.split)
self.addCleanup(shutil.rmtree, self.export_dir, ignore_errors=True)

View File

@@ -4,24 +4,21 @@
import json
import re
from datetime import datetime, timedelta
from unittest.mock import Mock, PropertyMock, patch
import ddt
import six
from django.conf import settings
from django.http import Http404
from django.test import TestCase
from django.test.client import RequestFactory
from django.urls import reverse
from edx_proctoring.exceptions import ProctoredExamNotFoundException
from mock import Mock, PropertyMock, patch
from opaque_keys import InvalidKeyError
from opaque_keys.edx.asides import AsideUsageKeyV2
from opaque_keys.edx.keys import CourseKey, UsageKey
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
from pyquery import PyQuery
from pytz import UTC
from six import text_type
from six.moves import range
from web_fragments.fragment import Fragment
from webob import Response
from xblock.core import XBlockAside
@@ -34,12 +31,14 @@ from xblock.validation import ValidationMessage
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.utils import reverse_course_url, reverse_usage_url
from cms.djangoapps.contentstore.views import item as item_module
from lms.djangoapps.lms_xblock.mixin import NONSENSICAL_ACCESS_RESTRICTION
from common.djangoapps.student.tests.factories import UserFactory
from common.djangoapps.xblock_django.models import (
XBlockConfiguration, XBlockStudioConfiguration, XBlockStudioConfigurationFlag
XBlockConfiguration,
XBlockStudioConfiguration,
XBlockStudioConfigurationFlag
)
from common.djangoapps.xblock_django.user_service import DjangoXBlockUserService
from lms.djangoapps.lms_xblock.mixin import NONSENSICAL_ACCESS_RESTRICTION
from xmodule.capa_module import ProblemBlock
from xmodule.course_module import DEFAULT_START_DATE
from xmodule.modulestore import ModuleStoreEnum
@@ -72,7 +71,7 @@ class AsideTest(XBlockAside):
"""
Test xblock aside class
"""
FRAG_CONTENT = u"<p>Aside Foo rendered</p>"
FRAG_CONTENT = "<p>Aside Foo rendered</p>"
field11 = String(default="aside1_default_value1", scope=Scope.content)
field12 = String(default="aside1_default_value2", scope=Scope.settings)
@@ -87,7 +86,7 @@ class AsideTest(XBlockAside):
class ItemTest(CourseTestCase):
""" Base test class for create, save, and delete """
def setUp(self):
super(ItemTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.course_key = self.course.id
self.usage_key = self.course.location
@@ -115,9 +114,9 @@ class ItemTest(CourseTestCase):
def create_xblock(self, parent_usage_key=None, display_name=None, category=None, boilerplate=None): # lint-amnesty, pylint: disable=missing-function-docstring
data = {
'parent_locator': six.text_type(
'parent_locator': str(
self.usage_key
)if parent_usage_key is None else six.text_type(parent_usage_key),
)if parent_usage_key is None else str(parent_usage_key),
'category': category
}
if display_name is not None:
@@ -260,8 +259,8 @@ class GetItemTest(ItemTest):
html,
# The instance of the wrapper class will have an auto-generated ID. Allow any
# characters after wrapper.
u'"/container/{}" class="action-button">\\s*<span class="action-button-text">View</span>'.format(
re.escape(six.text_type(wrapper_usage_key))
'"/container/{}" class="action-button">\\s*<span class="action-button-text">View</span>'.format(
re.escape(str(wrapper_usage_key))
)
)
@@ -304,14 +303,14 @@ class GetItemTest(ItemTest):
# Rename groups in group configuration
GROUP_CONFIGURATION_JSON = {
u'id': 0,
u'name': u'first_partition',
u'scheme': u'random',
u'description': u'First Partition',
u'version': UserPartition.VERSION,
u'groups': [
{u'id': 0, u'name': u'New_NAME_A', u'version': 1},
{u'id': 1, u'name': u'New_NAME_B', u'version': 1},
'id': 0,
'name': 'first_partition',
'scheme': 'random',
'description': 'First Partition',
'version': UserPartition.VERSION,
'groups': [
{'id': 0, 'name': 'New_NAME_A', 'version': 1},
{'id': 1, 'name': 'New_NAME_B', 'version': 1},
],
}
@@ -458,13 +457,13 @@ class GetItemTest(ItemTest):
xblock (XBlock): An XBlock item.
xblock_info (dict): A dict containing xblock information.
"""
self.assertEqual(six.text_type(xblock.location), xblock_info['id'])
self.assertEqual(str(xblock.location), xblock_info['id'])
self.assertEqual(xblock.display_name, xblock_info['display_name'])
self.assertEqual(xblock.category, xblock_info['category'])
for usage_key in (problem_usage_key, vert_usage_key, seq_usage_key, chapter_usage_key):
xblock = self.get_item_from_modulestore(usage_key)
url = reverse_usage_url('xblock_handler', usage_key) + '?fields={field_type}'.format(field_type=field_type)
url = reverse_usage_url('xblock_handler', usage_key) + f'?fields={field_type}'
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
response = json.loads(response.content.decode('utf-8'))
@@ -567,7 +566,7 @@ class TestCreateItem(ItemTest):
self.assertEqual(new_tab.display_name, 'Empty')
class DuplicateHelper(object):
class DuplicateHelper:
"""
Helper mixin class for TestDuplicateItem and TestDuplicateItemWithAsides
"""
@@ -602,21 +601,21 @@ class DuplicateHelper(object):
self.assertEqual(duplicated_asides[0].field13, 'aside1_default_value3')
self.assertNotEqual(
six.text_type(original_item.location),
six.text_type(duplicated_item.location),
str(original_item.location),
str(duplicated_item.location),
"Location of duplicate should be different from original"
)
# Parent will only be equal for root of duplicated structure, in the case
# where an item is duplicated in-place.
if parent_usage_key and six.text_type(original_item.parent) == six.text_type(parent_usage_key):
if parent_usage_key and str(original_item.parent) == str(parent_usage_key):
self.assertEqual(
six.text_type(parent_usage_key), six.text_type(duplicated_item.parent),
str(parent_usage_key), str(duplicated_item.parent),
"Parent of duplicate should equal parent of source for root xblock when duplicated in-place"
)
else:
self.assertNotEqual(
six.text_type(original_item.parent), six.text_type(duplicated_item.parent),
str(original_item.parent), str(duplicated_item.parent),
"Parent duplicate should be different from source"
)
@@ -648,10 +647,10 @@ class DuplicateHelper(object):
return duplicated_item.display_name == original_item.category
return duplicated_item.display_name == original_item.display_name
if original_item.display_name is not None:
return duplicated_item.display_name == u"Duplicate of '{display_name}'".format(
return duplicated_item.display_name == "Duplicate of '{display_name}'".format(
display_name=original_item.display_name
)
return duplicated_item.display_name == u"Duplicate of {display_name}".format(
return duplicated_item.display_name == "Duplicate of {display_name}".format(
display_name=original_item.category
)
@@ -661,8 +660,8 @@ class DuplicateHelper(object):
"""
# pylint: disable=no-member
data = {
'parent_locator': six.text_type(parent_usage_key),
'duplicate_source_locator': six.text_type(source_usage_key)
'parent_locator': str(parent_usage_key),
'duplicate_source_locator': str(source_usage_key)
}
if display_name is not None:
data['display_name'] = display_name
@@ -678,7 +677,7 @@ class TestDuplicateItem(ItemTest, DuplicateHelper):
def setUp(self):
""" Creates the test course structure and a few components to 'duplicate'. """
super(TestDuplicateItem, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# Create a parent chapter (for testing children of children).
resp = self.create_xblock(parent_usage_key=self.usage_key, category='chapter')
self.chapter_usage_key = self.response_usage_key(resp)
@@ -787,7 +786,7 @@ class TestMoveItem(ItemTest):
"""
Creates the test course structure to build course outline tree.
"""
super(TestMoveItem, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.setup_course()
def setup_course(self, default_store=None):
@@ -873,8 +872,8 @@ class TestMoveItem(ItemTest):
resp (JsonResponse): Response after the move operation is complete.
"""
data = {
'move_source_locator': six.text_type(source_usage_key),
'parent_locator': six.text_type(target_usage_key)
'move_source_locator': str(source_usage_key),
'parent_locator': str(target_usage_key)
}
if target_index is not None:
data['target_index'] = target_index
@@ -901,8 +900,8 @@ class TestMoveItem(ItemTest):
response = self._move_component(source_usage_key, target_usage_key, target_index)
self.assertEqual(response.status_code, 200)
response = json.loads(response.content.decode('utf-8'))
self.assertEqual(response['move_source_locator'], six.text_type(source_usage_key))
self.assertEqual(response['parent_locator'], six.text_type(target_usage_key))
self.assertEqual(response['move_source_locator'], str(source_usage_key))
self.assertEqual(response['parent_locator'], str(target_usage_key))
self.assertEqual(response['source_index'], expected_index)
# Verify parent referance has been changed now.
@@ -989,7 +988,7 @@ class TestMoveItem(ItemTest):
self.assertEqual(response.status_code, 400)
response = json.loads(response.content.decode('utf-8'))
expected_error = u'You can not move {usage_key} at an invalid index ({target_index}).'.format(
expected_error = 'You can not move {usage_key} at an invalid index ({target_index}).'.format(
usage_key=self.html_usage_key,
target_index=parent_children_length + 10
)
@@ -1006,7 +1005,7 @@ class TestMoveItem(ItemTest):
self.assertEqual(response.status_code, 400)
response = json.loads(response.content.decode('utf-8'))
expected_error = u'You can not move {source_type} into {target_type}.'.format(
expected_error = 'You can not move {source_type} into {target_type}.'.format(
source_type=self.html_usage_key.block_type,
target_type=self.seq_usage_key.block_type
)
@@ -1151,7 +1150,7 @@ class TestMoveItem(ItemTest):
self.assertEqual(response.status_code, 400)
response = json.loads(response.content.decode('utf-8'))
error = u'You must provide target_index ({target_index}) as an integer.'.format(target_index=target_index)
error = f'You must provide target_index ({target_index}) as an integer.'
self.assertEqual(response['error'], error)
new_parent_loc = self.store.get_parent_location(self.html_usage_key)
self.assertEqual(new_parent_loc, parent_loc)
@@ -1160,7 +1159,7 @@ class TestMoveItem(ItemTest):
"""
Test move an item without specifying the target location.
"""
data = {'move_source_locator': six.text_type(self.html_usage_key)}
data = {'move_source_locator': str(self.html_usage_key)}
with self.assertRaises(InvalidKeyError):
self.client.patch(
reverse('xblock_handler'),
@@ -1248,10 +1247,10 @@ class TestMoveItem(ItemTest):
insert_at = 0
self.assert_move_item(self.html_usage_key, self.vert2_usage_key, insert_at)
mock_logger.info.assert_called_with(
u'MOVE: %s moved from %s to %s at %d index',
six.text_type(self.html_usage_key),
six.text_type(self.vert_usage_key),
six.text_type(self.vert2_usage_key),
'MOVE: %s moved from %s to %s at %d index',
str(self.html_usage_key),
str(self.vert_usage_key),
str(self.vert2_usage_key),
insert_at
)
@@ -1319,8 +1318,8 @@ class TestMoveItem(ItemTest):
self.setup_course(default_store=store_type)
data = {
'move_source_locator': six.text_type(self.usage_key.course_key.make_usage_key('html', 'html_test')),
'parent_locator': six.text_type(self.vert2_usage_key)
'move_source_locator': str(self.usage_key.course_key.make_usage_key('html', 'html_test')),
'parent_locator': str(self.vert2_usage_key)
}
with self.assertRaises(ItemNotFoundError):
self.client.patch(
@@ -1339,7 +1338,7 @@ class TestDuplicateItemWithAsides(ItemTest, DuplicateHelper):
def setUp(self):
""" Creates the test course structure and a few components to 'duplicate'. """
super(TestDuplicateItemWithAsides, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# Create a parent chapter
resp = self.create_xblock(parent_usage_key=self.usage_key, category='chapter')
self.chapter_usage_key = self.response_usage_key(resp)
@@ -1400,7 +1399,7 @@ class TestEditItemSetup(ItemTest):
def setUp(self):
""" Creates the test course structure and a couple problems to 'edit'. """
super(TestEditItemSetup, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# create a chapter
display_name = 'chapter created'
resp = self.create_xblock(display_name=display_name, category='chapter')
@@ -1503,7 +1502,7 @@ class TestEditItem(TestEditItemSetup):
user=self.user
)
# Both display and actual value should be None
self.assertEqual(xblock_info['due_date'], u'')
self.assertEqual(xblock_info['due_date'], '')
self.assertIsNone(xblock_info['due'])
def test_update_generic_fields(self):
@@ -1567,9 +1566,9 @@ class TestEditItem(TestEditItemSetup):
self.seq_update_url,
data={
'children': [
six.text_type(self.problem_usage_key),
six.text_type(unit2_usage_key),
six.text_type(unit1_usage_key)
str(self.problem_usage_key),
str(unit2_usage_key),
str(unit1_usage_key)
]
}
)
@@ -1594,7 +1593,7 @@ class TestEditItem(TestEditItemSetup):
# move unit 1 from sequential1 to sequential2
resp = self.client.ajax_post(
self.seq2_update_url,
data={'children': [six.text_type(unit_1_key), six.text_type(unit_2_key)]}
data={'children': [str(unit_1_key), str(unit_2_key)]}
)
self.assertEqual(resp.status_code, 200)
@@ -1617,7 +1616,7 @@ class TestEditItem(TestEditItemSetup):
# adding orphaned unit 1 should return an error
resp = self.client.ajax_post(
self.seq2_update_url,
data={'children': [six.text_type(unit_1_key)]}
data={'children': [str(unit_1_key)]}
)
self.assertContains(resp, "Invalid data, possibly caused by concurrent authors", status_code=400)
@@ -1641,7 +1640,7 @@ class TestEditItem(TestEditItemSetup):
# remove unit 2 should return an error
resp = self.client.ajax_post(
self.seq2_update_url,
data={'children': [six.text_type(unit_1_key)]}
data={'children': [str(unit_1_key)]}
)
self.assertContains(resp, "Invalid data, possibly caused by concurrent authors", status_code=400)
@@ -1808,7 +1807,7 @@ class TestEditItem(TestEditItemSetup):
self.client.ajax_post(
self.problem_update_url,
data={
'id': six.text_type(self.problem_usage_key),
'id': str(self.problem_usage_key),
'metadata': {},
'data': "<p>Problem content draft.</p>"
}
@@ -1863,7 +1862,7 @@ class TestEditItem(TestEditItemSetup):
resp = self.client.ajax_post(
unit_update_url,
data={
'id': six.text_type(unit_usage_key),
'id': str(unit_usage_key),
'metadata': {},
}
)
@@ -1883,7 +1882,7 @@ class TestEditItem(TestEditItemSetup):
response = self.client.ajax_post(
update_url,
data={
'id': six.text_type(video_usage_key),
'id': str(video_usage_key),
'metadata': {
'saved_video_position': "Not a valid relative time",
},
@@ -1913,7 +1912,7 @@ class TestEditItemSplitMongo(TestEditItemSetup):
resp = self.client.get(view_url, HTTP_ACCEPT='application/json')
self.assertEqual(resp.status_code, 200)
content = json.loads(resp.content.decode('utf-8'))
self.assertEqual(len(PyQuery(content['html'])('.xblock-{}'.format(STUDIO_VIEW))), 1)
self.assertEqual(len(PyQuery(content['html'])(f'.xblock-{STUDIO_VIEW}')), 1)
class TestEditSplitModule(ItemTest):
@@ -1922,11 +1921,11 @@ class TestEditSplitModule(ItemTest):
"""
def setUp(self):
super(TestEditSplitModule, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.user = UserFactory()
self.first_user_partition_group_1 = Group(six.text_type(MINIMUM_STATIC_PARTITION_ID + 1), 'alpha')
self.first_user_partition_group_2 = Group(six.text_type(MINIMUM_STATIC_PARTITION_ID + 2), 'beta')
self.first_user_partition_group_1 = Group(str(MINIMUM_STATIC_PARTITION_ID + 1), 'alpha')
self.first_user_partition_group_2 = Group(str(MINIMUM_STATIC_PARTITION_ID + 2), 'beta')
self.first_user_partition = UserPartition(
MINIMUM_STATIC_PARTITION_ID, 'first_partition', 'First Partition',
[self.first_user_partition_group_1, self.first_user_partition_group_2]
@@ -1934,9 +1933,9 @@ class TestEditSplitModule(ItemTest):
# There is a test point below (test_create_groups) that purposefully wants the group IDs
# of the 2 partitions to overlap (which is not something that normally happens).
self.second_user_partition_group_1 = Group(six.text_type(MINIMUM_STATIC_PARTITION_ID + 1), 'Group 1')
self.second_user_partition_group_2 = Group(six.text_type(MINIMUM_STATIC_PARTITION_ID + 2), 'Group 2')
self.second_user_partition_group_3 = Group(six.text_type(MINIMUM_STATIC_PARTITION_ID + 3), 'Group 3')
self.second_user_partition_group_1 = Group(str(MINIMUM_STATIC_PARTITION_ID + 1), 'Group 1')
self.second_user_partition_group_2 = Group(str(MINIMUM_STATIC_PARTITION_ID + 2), 'Group 2')
self.second_user_partition_group_3 = Group(str(MINIMUM_STATIC_PARTITION_ID + 3), 'Group 3')
self.second_user_partition = UserPartition(
MINIMUM_STATIC_PARTITION_ID + 10, 'second_partition', 'Second Partition',
[
@@ -2004,8 +2003,8 @@ class TestEditSplitModule(ItemTest):
vertical_1 = self.get_item_from_modulestore(split_test.children[1], verify_is_draft=True)
self.assertEqual("vertical", vertical_0.category)
self.assertEqual("vertical", vertical_1.category)
self.assertEqual("Group ID " + six.text_type(MINIMUM_STATIC_PARTITION_ID + 1), vertical_0.display_name)
self.assertEqual("Group ID " + six.text_type(MINIMUM_STATIC_PARTITION_ID + 2), vertical_1.display_name)
self.assertEqual("Group ID " + str(MINIMUM_STATIC_PARTITION_ID + 1), vertical_0.display_name)
self.assertEqual("Group ID " + str(MINIMUM_STATIC_PARTITION_ID + 2), vertical_1.display_name)
# Verify that the group_id_to_child mapping is correct.
self.assertEqual(2, len(split_test.group_id_to_child))
@@ -2145,7 +2144,7 @@ class TestComponentHandler(TestCase):
"""Tests for component handler api"""
def setUp(self):
super(TestComponentHandler, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.request_factory = RequestFactory()
@@ -2161,7 +2160,7 @@ class TestComponentHandler(TestCase):
self.usage_key = BlockUsageLocator(
CourseLocator('dummy_org', 'dummy_course', 'dummy_run'), 'dummy_category', 'dummy_name'
)
self.usage_key_string = text_type(self.usage_key)
self.usage_key_string = str(self.usage_key)
self.user = UserFactory()
@@ -2213,7 +2212,7 @@ class TestComponentHandler(TestCase):
def get_usage_key():
"""return usage key"""
return (
text_type(AsideUsageKeyV2(self.usage_key, "aside"))
str(AsideUsageKeyV2(self.usage_key, "aside"))
if is_xblock_aside
else self.usage_key_string
)
@@ -2244,7 +2243,7 @@ class TestComponentTemplates(CourseTestCase):
"""
def setUp(self):
super(TestComponentTemplates, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# Advanced Module support levels.
XBlockStudioConfiguration.objects.create(name='poll', enabled=True, support_level="fs")
XBlockStudioConfiguration.objects.create(name='survey', enabled=True, support_level="ps")
@@ -2319,9 +2318,9 @@ class TestComponentTemplates(CourseTestCase):
{
'boilerplate_name': None,
'category': 'drag-and-drop-v2',
'display_name': u'Drag and Drop',
'display_name': 'Drag and Drop',
'hinted': False,
'support_level': u'fs',
'support_level': 'fs',
'tab': 'advanced'
}
]
@@ -2331,7 +2330,7 @@ class TestComponentTemplates(CourseTestCase):
self.templates = get_component_templates(self.course)
self._verify_basic_component("video", "Video", "us")
problem_templates = self.get_templates_of_type('problem')
problem_no_boilerplate = self.get_template(problem_templates, u'Blank Advanced Problem')
problem_no_boilerplate = self.get_template(problem_templates, 'Blank Advanced Problem')
self.assertIsNotNone(problem_no_boilerplate)
self.assertEqual('us', problem_no_boilerplate['support_level'])
@@ -2350,7 +2349,7 @@ class TestComponentTemplates(CourseTestCase):
self.assertEqual(len(advanced_templates), 1)
world_cloud_template = advanced_templates[0]
self.assertEqual(world_cloud_template.get('category'), 'word_cloud')
self.assertEqual(world_cloud_template.get('display_name'), u'Word cloud')
self.assertEqual(world_cloud_template.get('display_name'), 'Word cloud')
self.assertIsNone(world_cloud_template.get('boilerplate_name', None))
# Verify that non-advanced components are not added twice
@@ -2373,7 +2372,7 @@ class TestComponentTemplates(CourseTestCase):
Test the handling of advanced problem templates.
"""
problem_templates = self.get_templates_of_type('problem')
circuit_template = self.get_template(problem_templates, u'Circuit Schematic Builder')
circuit_template = self.get_template(problem_templates, 'Circuit Schematic Builder')
self.assertIsNotNone(circuit_template)
self.assertEqual(circuit_template.get('category'), 'problem')
self.assertEqual(circuit_template.get('boilerplate_name'), 'circuitschematic.yaml')
@@ -2506,7 +2505,7 @@ class TestXBlockInfo(ItemTest):
Unit tests for XBlock's outline handling.
"""
def setUp(self):
super(TestXBlockInfo, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
user_id = self.user.id
self.chapter = ItemFactory.create(
parent_location=self.course.location, category='chapter', display_name="Week 1", user_id=user_id,
@@ -2702,7 +2701,7 @@ class TestXBlockInfo(ItemTest):
Validate that the xblock info is correct for the test course.
"""
self.assertEqual(xblock_info['category'], 'course')
self.assertEqual(xblock_info['id'], six.text_type(self.course.location))
self.assertEqual(xblock_info['id'], str(self.course.location))
self.assertEqual(xblock_info['display_name'], self.course.display_name)
self.assertTrue(xblock_info['published'])
self.assertFalse(xblock_info['highlights_enabled_for_messaging'])
@@ -2715,7 +2714,7 @@ class TestXBlockInfo(ItemTest):
Validate that the xblock info is correct for the test chapter.
"""
self.assertEqual(xblock_info['category'], 'chapter')
self.assertEqual(xblock_info['id'], six.text_type(self.chapter.location))
self.assertEqual(xblock_info['id'], str(self.chapter.location))
self.assertEqual(xblock_info['display_name'], 'Week 1')
self.assertTrue(xblock_info['published'])
self.assertIsNone(xblock_info.get('edited_by', None))
@@ -2735,7 +2734,7 @@ class TestXBlockInfo(ItemTest):
Validate that the xblock info is correct for the test sequential.
"""
self.assertEqual(xblock_info['category'], 'sequential')
self.assertEqual(xblock_info['id'], six.text_type(self.sequential.location))
self.assertEqual(xblock_info['id'], str(self.sequential.location))
self.assertEqual(xblock_info['display_name'], 'Lesson 1')
self.assertTrue(xblock_info['published'])
self.assertIsNone(xblock_info.get('edited_by', None))
@@ -2748,7 +2747,7 @@ class TestXBlockInfo(ItemTest):
Validate that the xblock info is correct for the test vertical.
"""
self.assertEqual(xblock_info['category'], 'vertical')
self.assertEqual(xblock_info['id'], six.text_type(self.vertical.location))
self.assertEqual(xblock_info['id'], str(self.vertical.location))
self.assertEqual(xblock_info['display_name'], 'Unit 1')
self.assertTrue(xblock_info['published'])
self.assertEqual(xblock_info['edited_by'], 'testuser')
@@ -2770,7 +2769,7 @@ class TestXBlockInfo(ItemTest):
Validate that the xblock info is correct for the test component.
"""
self.assertEqual(xblock_info['category'], 'video')
self.assertEqual(xblock_info['id'], six.text_type(self.video.location))
self.assertEqual(xblock_info['id'], str(self.video.location))
self.assertEqual(xblock_info['display_name'], 'My Video')
self.assertTrue(xblock_info['published'])
self.assertIsNone(xblock_info.get('edited_by', None))
@@ -2947,7 +2946,7 @@ class TestLibraryXBlockInfo(ModuleStoreTestCase):
"""
def setUp(self):
super(TestLibraryXBlockInfo, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
user_id = self.user.id
self.library = LibraryFactory.create()
self.top_level_html = ItemFactory.create(
@@ -2975,7 +2974,7 @@ class TestLibraryXBlockInfo(ModuleStoreTestCase):
ancestors = xblock_info['ancestor_info']['ancestors']
self.assertEqual(len(ancestors), 2)
self.assertEqual(ancestors[0]['category'], 'vertical')
self.assertEqual(ancestors[0]['id'], six.text_type(self.vertical.location))
self.assertEqual(ancestors[0]['id'], str(self.vertical.location))
self.assertEqual(ancestors[1]['category'], 'library')
def validate_component_xblock_info(self, xblock_info, original_block):
@@ -2983,7 +2982,7 @@ class TestLibraryXBlockInfo(ModuleStoreTestCase):
Validate that the xblock info is correct for the test component.
"""
self.assertEqual(xblock_info['category'], original_block.category)
self.assertEqual(xblock_info['id'], six.text_type(original_block.location))
self.assertEqual(xblock_info['id'], str(original_block.location))
self.assertEqual(xblock_info['display_name'], original_block.display_name)
self.assertIsNone(xblock_info.get('has_changes', None))
self.assertIsNone(xblock_info.get('published', None))

View File

@@ -5,17 +5,16 @@ More important high-level tests are in contentstore/tests/test_libraries.py
"""
from unittest import mock
from unittest.mock import patch
import ddt
import mock
from django.conf import settings
from django.test.utils import override_settings
from django.urls import reverse
from mock import patch
from opaque_keys.edx.locator import CourseKey, LibraryLocator
from organizations.api import get_organization_by_short_name
from organizations.exceptions import InvalidOrganizationException
from opaque_keys.edx.locator import CourseKey, LibraryLocator
from six import text_type
from six.moves import range
from cms.djangoapps.contentstore.tests.utils import AjaxEnabledTestClient, CourseTestCase, parse_json
from cms.djangoapps.contentstore.utils import reverse_course_url, reverse_library_url
@@ -32,7 +31,7 @@ LIBRARY_REST_URL = '/library/' # URL for GET/POST requests involving libraries
def make_url_for_lib(key):
""" Get the RESTful/studio URL for testing the given library """
if isinstance(key, LibraryLocator):
key = text_type(key)
key = str(key)
return LIBRARY_REST_URL + key
@@ -44,7 +43,7 @@ class UnitTestLibraries(CourseTestCase):
"""
def setUp(self):
super(UnitTestLibraries, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.client = AjaxEnabledTestClient()
self.client.login(username=self.user.username, password=self.user_password)
@@ -283,11 +282,11 @@ class UnitTestLibraries(CourseTestCase):
self.assertEqual(response.status_code, 200)
info = parse_json(response)
self.assertEqual(info['display_name'], lib.display_name)
self.assertEqual(info['library_id'], text_type(lib_key))
self.assertEqual(info['library_id'], str(lib_key))
self.assertEqual(info['previous_version'], None)
self.assertNotEqual(info['version'], None)
self.assertNotEqual(info['version'], '')
self.assertEqual(info['version'], text_type(version))
self.assertEqual(info['version'], str(version))
def test_get_lib_edit_html(self):
"""
@@ -368,7 +367,7 @@ class UnitTestLibraries(CourseTestCase):
"""
library = LibraryFactory.create()
extra_user, _ = self.create_non_staff_user()
manage_users_url = reverse_library_url('manage_library_users', text_type(library.location.library_key))
manage_users_url = reverse_library_url('manage_library_users', str(library.location.library_key))
response = self.client.get(manage_users_url)
self.assertEqual(response.status_code, 200)

View File

@@ -13,16 +13,16 @@ from common.djangoapps.student.tests.factories import UserFactory
class TestOrganizationListing(TestCase):
"""Verify Organization listing behavior."""
def setUp(self):
super(TestOrganizationListing, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.staff = UserFactory(is_staff=True)
self.client.login(username=self.staff.username, password='test')
self.org_names_listing_url = reverse('organizations')
self.org_short_names = ["alphaX", "betaX", "orgX"]
for index, short_name in enumerate(self.org_short_names):
add_organization(organization_data={
'name': u'Test Organization %s' % index,
'name': 'Test Organization %s' % index,
'short_name': short_name,
'description': u'Testing Organization %s Description' % index,
'description': 'Testing Organization %s Description' % index,
})
def test_organization_list(self):

View File

@@ -4,10 +4,9 @@ Tests for contentstore.views.preview.py
import re
from unittest import mock
import ddt
import mock
import six
from django.test.client import Client, RequestFactory
from xblock.core import XBlock, XBlockAside
@@ -61,11 +60,11 @@ class GetPreviewHtmlTestCase(ModuleStoreTestCase):
# Verify student view html is returned, and the usage ID is as expected.
html_pattern = re.escape(
six.text_type(course.id.make_usage_key('html', 'replaceme'))
str(course.id.make_usage_key('html', 'replaceme'))
).replace('replaceme', r'html_[0-9]*')
self.assertRegex(
html,
'data-usage-id="{}"'.format(html_pattern)
f'data-usage-id="{html_pattern}"'
)
self.assertRegex(html, '<html>foobar</html>')
self.assertRegex(html, r"data-block-type=[\"\']test_aside[\"\']")
@@ -186,7 +185,7 @@ class StudioXBlockServiceBindingTest(ModuleStoreTestCase):
"""
Set up the user and request that will be used.
"""
super(StudioXBlockServiceBindingTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.user = UserFactory()
self.course = CourseFactory.create()
self.request = mock.Mock()

View File

@@ -20,7 +20,7 @@ class TabsPageTests(CourseTestCase):
"""Common setup for tests"""
# call super class to setup course, etc.
super(TabsPageTests, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# Set the URL for tests
self.url = reverse_course_url('tabs_handler', self.course.id)
@@ -178,7 +178,7 @@ class TabsPageTests(CourseTestCase):
"""
Verify that the static tab renders itself with the correct HTML
"""
preview_url = '/xblock/{}/{}'.format(self.test_tab.location, STUDENT_VIEW)
preview_url = f'/xblock/{self.test_tab.location}/{STUDENT_VIEW}'
resp = self.client.get(preview_url, HTTP_ACCEPT='application/json')
self.assertEqual(resp.status_code, 200)
@@ -205,7 +205,7 @@ class PrimitiveTabEdit(ModuleStoreTestCase):
with self.assertRaises(IndexError):
tabs.primitive_delete(course, 7)
tabs.primitive_delete(course, 2)
self.assertNotIn({u'type': u'textbooks'}, course.tabs)
self.assertNotIn({'type': 'textbooks'}, course.tabs)
# Check that discussion has shifted up
self.assertEqual(course.tabs[2], {'type': 'discussion', 'name': 'Discussion'})

View File

@@ -14,7 +14,7 @@ class TextbookIndexTestCase(CourseTestCase):
"Test cases for the textbook index page"
def setUp(self):
"Set the URL for tests"
super(TextbookIndexTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.url = reverse_course_url('textbooks_list_handler', self.course.id)
def test_view_index(self):
@@ -113,7 +113,7 @@ class TextbookCreateTestCase(CourseTestCase):
def setUp(self):
"Set up a url and some textbook content for tests"
super(TextbookCreateTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.url = reverse_course_url('textbooks_list_handler', self.course.id)
self.textbook = {
@@ -173,7 +173,7 @@ class TextbookDetailTestCase(CourseTestCase):
def setUp(self):
"Set some useful content and URLs for tests"
super(TextbookDetailTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.textbook1 = {
"tab_title": "Economics",
"id": 1,
@@ -295,7 +295,7 @@ class TextbookValidationTestCase(TestCase):
def setUp(self):
"Set some useful content for tests"
super(TextbookValidationTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.tb1 = {
"tab_title": "Hi, mom!",

View File

@@ -2,19 +2,18 @@
import json
from io import BytesIO # lint-amnesty, pylint: disable=unused-import
from io import StringIO
from unittest.mock import ANY, Mock, patch
import ddt
import six
from django.test.testcases import TestCase
from django.urls import reverse
from edxval import api
from mock import ANY, Mock, patch
from cms.djangoapps.contentstore.tests.utils import CourseTestCase
from cms.djangoapps.contentstore.utils import reverse_course_url
from openedx.core.djangoapps.profile_images.tests.helpers import make_image_file
from common.djangoapps.student.roles import CourseStaffRole
from openedx.core.djangoapps.profile_images.tests.helpers import make_image_file
from ..transcript_settings import TranscriptionProviderErrorType, validate_transcript_credentials
@@ -235,31 +234,31 @@ class TranscriptDownloadTest(CourseTestCase):
)
# Expected response
expected_content = u'0\n00:00:00,010 --> 00:00:00,100\nHi, welcome to Edx.\n\n'
expected_content = '0\n00:00:00,010 --> 00:00:00,100\nHi, welcome to Edx.\n\n'
expected_headers = {
'Content-Disposition': 'attachment; filename="edx.srt"',
'Content-Language': u'en',
'Content-Language': 'en',
'Content-Type': 'application/x-subrip; charset=utf-8'
}
# Assert the actual response
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content.decode('utf-8'), expected_content)
for attribute, value in six.iteritems(expected_headers):
for attribute, value in expected_headers.items():
self.assertEqual(response.get(attribute), value)
@ddt.data(
(
{},
u'The following parameters are required: edx_video_id, language_code.'
'The following parameters are required: edx_video_id, language_code.'
),
(
{'edx_video_id': '123'},
u'The following parameters are required: language_code.'
'The following parameters are required: language_code.'
),
(
{'language_code': 'en'},
u'The following parameters are required: edx_video_id.'
'The following parameters are required: edx_video_id.'
),
)
@ddt.unpack
@@ -311,7 +310,7 @@ class TranscriptUploadTest(CourseTestCase):
"""
Tests that transcript upload handler works as expected.
"""
transcript_file_stream = six.StringIO('0\n00:00:00,010 --> 00:00:00,100\nПривіт, edX вітає вас.\n\n')
transcript_file_stream = StringIO('0\n00:00:00,010 --> 00:00:00,100\nПривіт, edX вітає вас.\n\n')
# Make request to transcript upload handler
response = self.client.post(
self.view_url,
@@ -329,7 +328,7 @@ class TranscriptUploadTest(CourseTestCase):
video_id='123',
language_code='en',
metadata={
'language_code': u'es',
'language_code': 'es',
'file_format': 'sjson',
'provider': 'Custom'
},
@@ -343,28 +342,28 @@ class TranscriptUploadTest(CourseTestCase):
'language_code': 'en',
'new_language_code': 'en',
},
u'A transcript file is required.'
'A transcript file is required.'
),
(
{
'language_code': u'en',
'file': u'0\n00:00:00,010 --> 00:00:00,100\nHi, welcome to Edx.\n\n'
'language_code': 'en',
'file': '0\n00:00:00,010 --> 00:00:00,100\nHi, welcome to Edx.\n\n'
},
u'The following parameters are required: edx_video_id, new_language_code.'
'The following parameters are required: edx_video_id, new_language_code.'
),
(
{
'language_code': u'en',
'new_language_code': u'en',
'file': u'0\n00:00:00,010 --> 00:00:00,100\nHi, welcome to Edx.\n\n'
'language_code': 'en',
'new_language_code': 'en',
'file': '0\n00:00:00,010 --> 00:00:00,100\nHi, welcome to Edx.\n\n'
},
u'The following parameters are required: edx_video_id.'
'The following parameters are required: edx_video_id.'
),
(
{
'file': u'0\n00:00:00,010 --> 00:00:00,100\nHi, welcome to Edx.\n\n'
'file': '0\n00:00:00,010 --> 00:00:00,100\nHi, welcome to Edx.\n\n'
},
u'The following parameters are required: edx_video_id, language_code, new_language_code.'
'The following parameters are required: edx_video_id, language_code, new_language_code.'
)
)
@ddt.unpack
@@ -400,7 +399,7 @@ class TranscriptUploadTest(CourseTestCase):
self.assertEqual(response.status_code, 400)
self.assertEqual(
json.loads(response.content.decode('utf-8'))['error'],
u'A transcript with the "es" language code already exists.'
'A transcript with the "es" language code already exists.'
)
@patch(
@@ -427,7 +426,7 @@ class TranscriptUploadTest(CourseTestCase):
self.assertEqual(response.status_code, 400)
self.assertEqual(
json.loads(response.content.decode('utf-8'))['error'],
u'There is a problem with this transcript file. Try to upload a different file.'
'There is a problem with this transcript file. Try to upload a different file.'
)
@patch(
@@ -438,7 +437,7 @@ class TranscriptUploadTest(CourseTestCase):
"""
Tests the transcript upload handler with an invalid transcript file.
"""
transcript_file_stream = six.StringIO('An invalid transcript SubRip file content')
transcript_file_stream = StringIO('An invalid transcript SubRip file content')
# Make request to transcript upload handler
response = self.client.post(
self.view_url,
@@ -454,7 +453,7 @@ class TranscriptUploadTest(CourseTestCase):
self.assertEqual(response.status_code, 400)
self.assertEqual(
json.loads(response.content.decode('utf-8'))['error'],
u'There is a problem with this transcript file. Try to upload a different file.'
'There is a problem with this transcript file. Try to upload a different file.'
)
@@ -535,7 +534,7 @@ class TranscriptDeleteTest(CourseTestCase):
self.assertEqual(self.user.is_staff, is_staff)
self.assertEqual(CourseStaffRole(self.course.id).has_user(self.user), is_course_staff)
video_id, language_code = u'1234', u'en'
video_id, language_code = '1234', 'en'
# Create a real transcript in VAL.
api.create_or_update_video_transcript(
video_id=video_id,

View File

@@ -6,15 +6,14 @@ import json
import tempfile
import textwrap
from codecs import BOM_UTF8
from unittest.mock import Mock, patch
from uuid import uuid4
import ddt
import six
from django.conf import settings
from django.test.utils import override_settings
from django.urls import reverse
from edxval.api import create_video
from mock import Mock, patch
from opaque_keys.edx.keys import UsageKey
from cms.djangoapps.contentstore.tests.utils import CourseTestCase, mock_requests_get
@@ -34,7 +33,7 @@ from xmodule.video_module.transcripts_utils import (
TEST_DATA_CONTENTSTORE = copy.deepcopy(settings.CONTENTSTORE)
TEST_DATA_CONTENTSTORE['DOC_STORE_CONFIG']['db'] = 'test_xcontent_%s' % uuid4().hex
SRT_TRANSCRIPT_CONTENT = u"""0
SRT_TRANSCRIPT_CONTENT = """0
00:00:10,500 --> 00:00:13,000
Elephant's Dream
@@ -58,7 +57,7 @@ class BaseTranscripts(CourseTestCase):
def clear_subs_content(self):
"""Remove, if transcripts content exists."""
for youtube_id in self.get_youtube_ids().values():
filename = 'subs_{0}.srt.sjson'.format(youtube_id)
filename = f'subs_{youtube_id}.srt.sjson'
content_location = StaticContent.compute_location(self.course.id, filename)
try:
content = contentstore().find(content_location)
@@ -72,7 +71,7 @@ class BaseTranscripts(CourseTestCase):
"""
filedata = json.dumps(subs, indent=2)
mime_type = 'application/json'
filename = 'subs_{0}.srt.sjson'.format(subs_id)
filename = f'subs_{subs_id}.srt.sjson'
content_location = StaticContent.compute_location(self.course.id, filename)
content = StaticContent(content_location, filename, mime_type, filedata)
@@ -82,11 +81,11 @@ class BaseTranscripts(CourseTestCase):
def setUp(self):
"""Create initial data."""
super(BaseTranscripts, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# Add video module
data = {
'parent_locator': six.text_type(self.course.location),
'parent_locator': str(self.course.location),
'category': 'video',
'type': 'video'
}
@@ -127,7 +126,7 @@ class BaseTranscripts(CourseTestCase):
Setup non video module for tests.
"""
data = {
'parent_locator': six.text_type(self.course.location),
'parent_locator': str(self.course.location),
'category': 'non_video',
'type': 'non_video'
}
@@ -156,10 +155,10 @@ class TestUploadTranscripts(BaseTranscripts):
Tests for '/transcripts/upload' endpoint.
"""
def setUp(self):
super(TestUploadTranscripts, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.contents = {
'good': SRT_TRANSCRIPT_CONTENT,
'bad': u'Some BAD data',
'bad': 'Some BAD data',
}
# Create temporary transcript files
self.good_srt_file = self.create_transcript_file(content=self.contents['good'], suffix='.srt')
@@ -169,12 +168,12 @@ class TestUploadTranscripts(BaseTranscripts):
# Setup a VEDA produced video and persist `edx_video_id` in VAL.
create_video({
'edx_video_id': u'123-456-789',
'edx_video_id': '123-456-789',
'status': 'upload',
'client_video_id': u'Test Video',
'client_video_id': 'Test Video',
'duration': 0,
'encoded_videos': [],
'courses': [six.text_type(self.course.id)]
'courses': [str(self.course.id)]
})
# Add clean up handler
@@ -226,9 +225,9 @@ class TestUploadTranscripts(BaseTranscripts):
return response
@ddt.data(
(u'123-456-789', False),
(u'', False),
(u'123-456-789', True)
('123-456-789', False),
('', False),
('123-456-789', True)
)
@ddt.unpack
def test_transcript_upload_success(self, edx_video_id, include_bom):
@@ -259,7 +258,7 @@ class TestUploadTranscripts(BaseTranscripts):
self.assertEqual(video.edx_video_id, expected_edx_video_id)
# Verify transcript content
actual_transcript = get_video_transcript_content(video.edx_video_id, language_code=u'en')
actual_transcript = get_video_transcript_content(video.edx_video_id, language_code='en')
actual_sjson_content = json.loads(actual_transcript['content'].decode('utf-8'))
expected_sjson_content = json.loads(Transcript.convert(
self.contents['good'],
@@ -276,7 +275,7 @@ class TestUploadTranscripts(BaseTranscripts):
self.assert_response(
response,
expected_status_code=400,
expected_message=u'Video locator is required.'
expected_message='Video locator is required.'
)
def test_transcript_upload_without_file(self):
@@ -287,7 +286,7 @@ class TestUploadTranscripts(BaseTranscripts):
self.assert_response(
response,
expected_status_code=400,
expected_message=u'A transcript file is required.'
expected_message='A transcript file is required.'
)
def test_transcript_upload_bad_format(self):
@@ -302,7 +301,7 @@ class TestUploadTranscripts(BaseTranscripts):
self.assert_response(
response,
expected_status_code=400,
expected_message=u'This transcript file type is not supported.'
expected_message='This transcript file type is not supported.'
)
def test_transcript_upload_bad_content(self):
@@ -318,7 +317,7 @@ class TestUploadTranscripts(BaseTranscripts):
self.assert_response(
response,
expected_status_code=400,
expected_message=u'There is a problem with this transcript file. Try to upload a different file.'
expected_message='There is a problem with this transcript file. Try to upload a different file.'
)
def test_transcript_upload_unknown_category(self):
@@ -332,7 +331,7 @@ class TestUploadTranscripts(BaseTranscripts):
self.assert_response(
response,
expected_status_code=400,
expected_message=u'Transcripts are supported only for "video" modules.'
expected_message='Transcripts are supported only for "video" modules.'
)
def test_transcript_upload_non_existent_item(self):
@@ -348,7 +347,7 @@ class TestUploadTranscripts(BaseTranscripts):
self.assert_response(
response,
expected_status_code=400,
expected_message=u'Cannot find item by locator.'
expected_message='Cannot find item by locator.'
)
def test_transcript_upload_without_edx_video_id(self):
@@ -359,7 +358,7 @@ class TestUploadTranscripts(BaseTranscripts):
self.assert_response(
response,
expected_status_code=400,
expected_message=u'Video ID is required.'
expected_message='Video ID is required.'
)
def test_transcript_upload_with_non_existant_edx_video_id(self):
@@ -379,7 +378,7 @@ class TestUploadTranscripts(BaseTranscripts):
self.assert_response(response, expected_status_code=400, expected_message='Invalid Video ID')
# Verify transcript does not exist for non-existant `edx_video_id`
self.assertIsNone(get_video_transcript_content(non_existant_edx_video_id, language_code=u'en'))
self.assertIsNone(get_video_transcript_content(non_existant_edx_video_id, language_code='en'))
@ddt.ddt
@@ -388,7 +387,7 @@ class TestChooseTranscripts(BaseTranscripts):
Tests for '/transcripts/choose' endpoint.
"""
def setUp(self):
super(TestChooseTranscripts, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# Create test transcript in contentstore
self.chosen_html5_id = 'test_html5_subs'
@@ -397,12 +396,12 @@ class TestChooseTranscripts(BaseTranscripts):
# Setup a VEDA produced video and persist `edx_video_id` in VAL.
create_video({
'edx_video_id': u'123-456-789',
'edx_video_id': '123-456-789',
'status': 'upload',
'client_video_id': u'Test Video',
'client_video_id': 'Test Video',
'duration': 0,
'encoded_videos': [],
'courses': [six.text_type(self.course.id)]
'courses': [str(self.course.id)]
})
def choose_transcript(self, locator, chosen_html5_id):
@@ -411,7 +410,7 @@ class TestChooseTranscripts(BaseTranscripts):
"""
payload = {}
if locator:
payload.update({'locator': six.text_type(locator)})
payload.update({'locator': str(locator)})
if chosen_html5_id:
payload.update({'html5_id': chosen_html5_id})
@@ -420,7 +419,7 @@ class TestChooseTranscripts(BaseTranscripts):
response = self.client.get(choose_transcript_url, {'data': json.dumps(payload)})
return response
@ddt.data(u'123-456-789', u'')
@ddt.data('123-456-789', '')
def test_choose_transcript_success(self, edx_video_id):
"""
Verify that choosing transcript file in video component basic tab works as
@@ -447,7 +446,7 @@ class TestChooseTranscripts(BaseTranscripts):
self.assertEqual(video.edx_video_id, expected_edx_video_id)
# Verify transcript content
actual_transcript = get_video_transcript_content(video.edx_video_id, language_code=u'en')
actual_transcript = get_video_transcript_content(video.edx_video_id, language_code='en')
actual_sjson_content = json.loads(actual_transcript['content'].decode('utf-8'))
expected_sjson_content = json.loads(self.sjson_subs)
self.assertDictEqual(actual_sjson_content, expected_sjson_content)
@@ -460,7 +459,7 @@ class TestChooseTranscripts(BaseTranscripts):
self.assert_response(
response,
expected_status_code=400,
expected_message=u'Incoming video data is empty.'
expected_message='Incoming video data is empty.'
)
def test_choose_transcript_fails_without_locator(self):
@@ -471,7 +470,7 @@ class TestChooseTranscripts(BaseTranscripts):
self.assert_response(
response,
expected_status_code=400,
expected_message=u'Cannot find item by locator.'
expected_message='Cannot find item by locator.'
)
def test_choose_transcript_with_no_html5_transcript(self):
@@ -483,7 +482,7 @@ class TestChooseTranscripts(BaseTranscripts):
self.assert_response(
response,
expected_status_code=400,
expected_message=u"No such transcript."
expected_message="No such transcript."
)
def test_choose_transcript_fails_on_unknown_category(self):
@@ -497,7 +496,7 @@ class TestChooseTranscripts(BaseTranscripts):
self.assert_response(
response,
expected_status_code=400,
expected_message=u'Transcripts are supported only for "video" modules.'
expected_message='Transcripts are supported only for "video" modules.'
)
@@ -507,7 +506,7 @@ class TestRenameTranscripts(BaseTranscripts):
Tests for '/transcripts/rename' endpoint.
"""
def setUp(self):
super(TestRenameTranscripts, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
# Create test transcript in contentstore and update item's sub.
self.item.sub = 'test_video_subs'
@@ -517,12 +516,12 @@ class TestRenameTranscripts(BaseTranscripts):
# Setup a VEDA produced video and persist `edx_video_id` in VAL.
create_video({
'edx_video_id': u'123-456-789',
'edx_video_id': '123-456-789',
'status': 'upload',
'client_video_id': u'Test Video',
'client_video_id': 'Test Video',
'duration': 0,
'encoded_videos': [],
'courses': [six.text_type(self.course.id)]
'courses': [str(self.course.id)]
})
def rename_transcript(self, locator):
@@ -531,13 +530,13 @@ class TestRenameTranscripts(BaseTranscripts):
"""
payload = {}
if locator:
payload.update({'locator': six.text_type(locator)})
payload.update({'locator': str(locator)})
rename_transcript_url = reverse('rename_transcripts')
response = self.client.get(rename_transcript_url, {'data': json.dumps(payload)})
return response
@ddt.data(u'123-456-789', u'')
@ddt.data('123-456-789', '')
def test_rename_transcript_success(self, edx_video_id):
"""
Verify that "use current transcript" in video component basic tab works as
@@ -564,7 +563,7 @@ class TestRenameTranscripts(BaseTranscripts):
self.assertEqual(video.edx_video_id, expected_edx_video_id)
# Verify transcript content
actual_transcript = get_video_transcript_content(video.edx_video_id, language_code=u'en')
actual_transcript = get_video_transcript_content(video.edx_video_id, language_code='en')
actual_sjson_content = json.loads(actual_transcript['content'].decode('utf-8'))
expected_sjson_content = json.loads(self.sjson_subs)
self.assertDictEqual(actual_sjson_content, expected_sjson_content)
@@ -577,7 +576,7 @@ class TestRenameTranscripts(BaseTranscripts):
self.assert_response(
response,
expected_status_code=400,
expected_message=u'Incoming video data is empty.'
expected_message='Incoming video data is empty.'
)
def test_rename_transcript_fails_with_invalid_locator(self):
@@ -588,7 +587,7 @@ class TestRenameTranscripts(BaseTranscripts):
self.assert_response(
response,
expected_status_code=400,
expected_message=u'Cannot find item by locator.'
expected_message='Cannot find item by locator.'
)
def test_rename_transcript_with_non_existent_sub(self):
@@ -605,7 +604,7 @@ class TestRenameTranscripts(BaseTranscripts):
self.assert_response(
response,
expected_status_code=400,
expected_message=u"No such transcript."
expected_message="No such transcript."
)
def test_rename_transcript_fails_on_unknown_category(self):
@@ -619,7 +618,7 @@ class TestRenameTranscripts(BaseTranscripts):
self.assert_response(
response,
expected_status_code=400,
expected_message=u'Transcripts are supported only for "video" modules.'
expected_message='Transcripts are supported only for "video" modules.'
)
@@ -633,17 +632,17 @@ class TestReplaceTranscripts(BaseTranscripts):
Tests for '/transcripts/replace' endpoint.
"""
def setUp(self):
super(TestReplaceTranscripts, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.youtube_id = 'test_yt_id'
# Setup a VEDA produced video and persist `edx_video_id` in VAL.
create_video({
'edx_video_id': u'123-456-789',
'edx_video_id': '123-456-789',
'status': 'upload',
'client_video_id': u'Test Video',
'client_video_id': 'Test Video',
'duration': 0,
'encoded_videos': [],
'courses': [six.text_type(self.course.id)]
'courses': [str(self.course.id)]
})
def replace_transcript(self, locator, youtube_id):
@@ -652,7 +651,7 @@ class TestReplaceTranscripts(BaseTranscripts):
"""
payload = {}
if locator:
payload.update({'locator': six.text_type(locator)})
payload.update({'locator': str(locator)})
if youtube_id:
payload.update({
@@ -668,7 +667,7 @@ class TestReplaceTranscripts(BaseTranscripts):
response = self.client.get(replace_transcript_url, {'data': json.dumps(payload)})
return response
@ddt.data(u'123-456-789', u'')
@ddt.data('123-456-789', '')
def test_replace_transcript_success(self, edx_video_id):
"""
Verify that "import from youtube" in video component basic tab works as
@@ -695,7 +694,7 @@ class TestReplaceTranscripts(BaseTranscripts):
self.assertEqual(video.edx_video_id, expected_edx_video_id)
# Verify transcript content
actual_transcript = get_video_transcript_content(video.edx_video_id, language_code=u'en')
actual_transcript = get_video_transcript_content(video.edx_video_id, language_code='en')
actual_sjson_content = json.loads(actual_transcript['content'].decode('utf-8'))
expected_sjson_content = json.loads(SJSON_TRANSCRIPT_CONTENT)
self.assertDictEqual(actual_sjson_content, expected_sjson_content)
@@ -708,7 +707,7 @@ class TestReplaceTranscripts(BaseTranscripts):
self.assert_response(
response,
expected_status_code=400,
expected_message=u'Incoming video data is empty.'
expected_message='Incoming video data is empty.'
)
def test_replace_transcript_fails_with_invalid_locator(self):
@@ -719,7 +718,7 @@ class TestReplaceTranscripts(BaseTranscripts):
self.assert_response(
response,
expected_status_code=400,
expected_message=u'Cannot find item by locator.'
expected_message='Cannot find item by locator.'
)
def test_replace_transcript_fails_without_yt_id(self):
@@ -730,14 +729,14 @@ class TestReplaceTranscripts(BaseTranscripts):
self.assert_response(
response,
expected_status_code=400,
expected_message=u'YouTube ID is required.'
expected_message='YouTube ID is required.'
)
def test_replace_transcript_no_transcript_on_yt(self):
"""
Verify that replace transcript fails if YouTube does not have transcript for the given youtube id.
"""
error_message = u'YT ID not found.'
error_message = 'YT ID not found.'
patch_path = 'cms.djangoapps.contentstore.views.transcripts_ajax.download_youtube_subs'
with patch(patch_path) as mock_download_youtube_subs:
mock_download_youtube_subs.side_effect = GetTranscriptsFromYouTubeException(error_message)
@@ -754,7 +753,7 @@ class TestReplaceTranscripts(BaseTranscripts):
self.assert_response(
response,
expected_status_code=400,
expected_message=u'Transcripts are supported only for "video" modules.'
expected_message='Transcripts are supported only for "video" modules.'
)
@@ -782,7 +781,7 @@ class TestDownloadTranscripts(BaseTranscripts):
"""
payload = {}
if locator:
payload.update({'locator': six.text_type(locator)})
payload.update({'locator': str(locator)})
download_transcript_url = reverse('download_transcripts')
response = self.client.get(download_transcript_url, payload)
@@ -851,7 +850,7 @@ class TestCheckTranscripts(BaseTranscripts):
"""
def test_success_download_nonyoutube(self):
subs_id = str(uuid4())
self.set_fields_from_xml(self.item, u"""
self.set_fields_from_xml(self.item, """
<video youtube="" sub="{}">
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.webm"/>
@@ -872,7 +871,7 @@ class TestCheckTranscripts(BaseTranscripts):
self.save_subs_to_store(subs, subs_id)
data = {
'locator': six.text_type(self.video_usage_key),
'locator': str(self.video_usage_key),
'videos': [{
'type': 'html5',
'video': subs_id,
@@ -885,15 +884,15 @@ class TestCheckTranscripts(BaseTranscripts):
self.assertDictEqual(
json.loads(resp.content.decode('utf-8')),
{
u'status': u'Success',
u'youtube_local': False,
u'is_youtube_mode': False,
u'youtube_server': False,
u'command': u'found',
u'current_item_subs': six.text_type(subs_id),
u'youtube_diff': True,
u'html5_local': [six.text_type(subs_id)],
u'html5_equal': False,
'status': 'Success',
'youtube_local': False,
'is_youtube_mode': False,
'youtube_server': False,
'command': 'found',
'current_item_subs': str(subs_id),
'youtube_diff': True,
'html5_local': [str(subs_id)],
'html5_equal': False,
}
)
@@ -915,7 +914,7 @@ class TestCheckTranscripts(BaseTranscripts):
self.save_subs_to_store(subs, 'JMD_ifUUfsU')
link = reverse('check_transcripts')
data = {
'locator': six.text_type(self.video_usage_key),
'locator': str(self.video_usage_key),
'videos': [{
'type': 'youtube',
'video': 'JMD_ifUUfsU',
@@ -929,15 +928,15 @@ class TestCheckTranscripts(BaseTranscripts):
self.assertDictEqual(
json.loads(resp.content.decode('utf-8')),
{
u'status': u'Success',
u'youtube_local': True,
u'is_youtube_mode': True,
u'youtube_server': False,
u'command': u'found',
u'current_item_subs': None,
u'youtube_diff': True,
u'html5_local': [],
u'html5_equal': False,
'status': 'Success',
'youtube_local': True,
'is_youtube_mode': True,
'youtube_server': False,
'command': 'found',
'current_item_subs': None,
'youtube_diff': True,
'html5_local': [],
'html5_equal': False,
}
)
@@ -961,7 +960,7 @@ class TestCheckTranscripts(BaseTranscripts):
self.save_subs_to_store(subs, 'good_id_2')
link = reverse('check_transcripts')
data = {
'locator': six.text_type(self.video_usage_key),
'locator': str(self.video_usage_key),
'videos': [{
'type': 'youtube',
'video': 'good_id_2',
@@ -980,15 +979,15 @@ class TestCheckTranscripts(BaseTranscripts):
self.assertDictEqual(
json.loads(resp.content.decode('utf-8')),
{
u'status': u'Success',
u'youtube_local': True,
u'is_youtube_mode': True,
u'youtube_server': True,
u'command': u'replace',
u'current_item_subs': None,
u'youtube_diff': True,
u'html5_local': [],
u'html5_equal': False,
'status': 'Success',
'youtube_local': True,
'is_youtube_mode': True,
'youtube_server': True,
'command': 'replace',
'current_item_subs': None,
'youtube_diff': True,
'html5_local': [],
'html5_equal': False,
}
)
@@ -1023,7 +1022,7 @@ class TestCheckTranscripts(BaseTranscripts):
# Test for raising `ItemNotFoundError` exception.
data = {
'locator': '{0}_{1}'.format(self.video_usage_key, 'BAD_LOCATOR'),
'locator': '{}_{}'.format(self.video_usage_key, 'BAD_LOCATOR'),
'videos': [{
'type': '',
'video': '',
@@ -1037,7 +1036,7 @@ class TestCheckTranscripts(BaseTranscripts):
def test_fail_for_non_video_module(self):
# Not video module: setup
data = {
'parent_locator': six.text_type(self.course.location),
'parent_locator': str(self.course.location),
'category': 'not_video',
'type': 'not_video'
}
@@ -1045,7 +1044,7 @@ class TestCheckTranscripts(BaseTranscripts):
usage_key = self._get_usage_key(resp)
subs_id = str(uuid4())
item = modulestore().get_item(usage_key)
self.set_fields_from_xml(self.item, (u"""
self.set_fields_from_xml(self.item, ("""
<not_video youtube="" sub="{}">
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.webm"/>
@@ -1066,7 +1065,7 @@ class TestCheckTranscripts(BaseTranscripts):
self.save_subs_to_store(subs, subs_id)
data = {
'locator': six.text_type(usage_key),
'locator': str(usage_key),
'videos': [{
'type': '',
'video': '',
@@ -1096,7 +1095,7 @@ class TestCheckTranscripts(BaseTranscripts):
}
# video_transcript_feature.return_value = feature_enabled
self.set_fields_from_xml(self.item, (u"""
self.set_fields_from_xml(self.item, ("""
<video youtube="" sub="" edx_video_id="123">
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4"/>
<source src="http://www.quirksmode.org/html5/videos/big_buck_bunny.webm"/>
@@ -1107,7 +1106,7 @@ class TestCheckTranscripts(BaseTranscripts):
# Make request to check transcript view
data = {
'locator': six.text_type(self.video_usage_key),
'locator': str(self.video_usage_key),
'videos': [{
'type': 'html5',
'video': "",
@@ -1122,14 +1121,14 @@ class TestCheckTranscripts(BaseTranscripts):
self.assertDictEqual(
json.loads(response.content.decode('utf-8')),
{
u'status': u'Success',
u'youtube_local': False,
u'is_youtube_mode': False,
u'youtube_server': False,
u'command': 'found',
u'current_item_subs': None,
u'youtube_diff': True,
u'html5_local': [],
u'html5_equal': False,
'status': 'Success',
'youtube_local': False,
'is_youtube_mode': False,
'youtube_server': False,
'command': 'found',
'current_item_subs': None,
'youtube_diff': True,
'html5_local': [],
'html5_equal': False,
}
)

View File

@@ -16,7 +16,7 @@ class UnitPageTestCase(StudioPageTestCase):
"""
def setUp(self):
super(UnitPageTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.vertical = ItemFactory.create(parent_location=self.sequential.location,
category='vertical', display_name='Unit')
self.video = ItemFactory.create(parent_location=self.vertical.location,

View File

@@ -16,7 +16,7 @@ from common.djangoapps.student.roles import CourseInstructorRole, CourseStaffRol
class UsersTestCase(CourseTestCase): # lint-amnesty, pylint: disable=missing-class-docstring
def setUp(self):
super(UsersTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.ext_user = User.objects.create_user(
"joe", "joe@comedycentral.com", "haha")
self.ext_user.is_active = True

View File

@@ -1,4 +1,3 @@
#-*- coding: utf-8 -*-
"""
Unit tests for video-related REST APIs.
"""
@@ -9,11 +8,12 @@ import json
import re
from contextlib import contextmanager
from datetime import datetime
from io import StringIO
from unittest.mock import Mock, patch
import dateutil.parser
import ddt
import pytz
import six
from django.conf import settings
from django.test.utils import override_settings
from edx_toggles.toggles import LegacyWaffleSwitch
@@ -27,8 +27,6 @@ from edxval.api import (
get_transcript_preferences,
get_video_info
)
from mock import Mock, patch
from six import StringIO
from waffle.testutils import override_flag
from cms.djangoapps.contentstore.models import VideoUploadConfig
@@ -57,7 +55,7 @@ from ..videos import (
VIDEO_IMAGE_UPLOAD_ENABLED_SWITCH = LegacyWaffleSwitch(WAFFLE_SWITCHES, VIDEO_IMAGE_UPLOAD_ENABLED)
class VideoUploadTestBase(object):
class VideoUploadTestBase:
"""
Test cases for the video upload feature
"""
@@ -67,7 +65,7 @@ class VideoUploadTestBase(object):
return reverse_course_url(self.VIEW_NAME, course_key, kwargs) # lint-amnesty, pylint: disable=no-member
def setUp(self):
super(VideoUploadTestBase, self).setUp() # lint-amnesty, pylint: disable=no-member, super-with-arguments
super().setUp() # lint-amnesty, pylint: disable=no-member
self.url = self.get_url_for_course_key(self.course.id)
self.test_token = "test_token"
self.course.video_upload_pipeline = {
@@ -84,7 +82,7 @@ class VideoUploadTestBase(object):
self.store.update_item(self.course2, self.user.id) # lint-amnesty, pylint: disable=no-member
# course ids for videos
course_ids = [six.text_type(self.course.id), six.text_type(self.course2.id)]
course_ids = [str(self.course.id), str(self.course2.id)]
created = datetime.now(pytz.utc)
self.profiles = ["profile1", "profile2"]
@@ -122,7 +120,7 @@ class VideoUploadTestBase(object):
},
{
"edx_video_id": "non-ascii",
"client_video_id": u"nón-ascii-näme.mp4",
"client_video_id": "nón-ascii-näme.mp4",
"duration": 256.0,
"status": "transcode_active",
"courses": course_ids,
@@ -130,7 +128,7 @@ class VideoUploadTestBase(object):
"encoded_videos": [
{
"profile": "profile1",
"url": u"http://example.com/profile1/nón-ascii-näme.mp4",
"url": "http://example.com/profile1/nón-ascii-näme.mp4",
"file_size": 3200,
"bitrate": 100,
},
@@ -140,7 +138,7 @@ class VideoUploadTestBase(object):
# Ensure every status string is tested
self.previous_uploads += [
{
"edx_video_id": "status_test_{}".format(status),
"edx_video_id": f"status_test_{status}",
"client_video_id": "status_test.mp4",
"duration": 3.14,
"status": status,
@@ -206,7 +204,7 @@ class VideoUploadTestMixin(VideoUploadTestBase):
self.assertEqual(self.client.get(self.url).status_code, 404)
class VideoUploadPostTestsMixin(object):
class VideoUploadPostTestsMixin:
"""
Shared test cases for video post tests.
"""
@@ -284,7 +282,7 @@ class VideoUploadPostTestsMixin(object):
'client_video_id',
file_info['file_name']
)
mock_key_instance.set_metadata.assert_any_call('course_key', six.text_type(self.course.id))
mock_key_instance.set_metadata.assert_any_call('course_key', str(self.course.id))
mock_key_instance.generate_url.assert_called_once_with(
KEY_EXPIRATION_IN_SECONDS,
'PUT',
@@ -297,7 +295,7 @@ class VideoUploadPostTestsMixin(object):
self.assertEqual(val_info['client_video_id'], file_info['file_name'])
self.assertEqual(val_info['status'], 'upload')
self.assertEqual(val_info['duration'], 0)
self.assertEqual(val_info['courses'], [{six.text_type(self.course.id): None}])
self.assertEqual(val_info['courses'], [{str(self.course.id): None}])
# Ensure response is correct
response_file = response_obj['files'][i]
@@ -352,7 +350,7 @@ class VideosHandlerTestCase(VideoUploadTestMixin, VideoUploadPostTestsMixin, Cou
original_video = self.previous_uploads[-(i + 1)]
self.assertEqual(
set(response_video.keys()),
set([
{
'edx_video_id',
'client_video_id',
'created',
@@ -362,7 +360,7 @@ class VideosHandlerTestCase(VideoUploadTestMixin, VideoUploadPostTestsMixin, Cou
'transcripts',
'transcription_status',
'error_description'
])
}
)
dateutil.parser.parse(response_video['created'])
for field in ['edx_video_id', 'client_video_id', 'duration']:
@@ -530,7 +528,7 @@ class VideosHandlerTestCase(VideoUploadTestMixin, VideoUploadPostTestsMixin, Cou
"""
Test that video uploads throws error message when file name contains special characters.
"""
file_name = u'test\u2019_file.mp4'
file_name = 'test\u2019_file.mp4'
files = [{'file_name': file_name, 'content_type': 'video/mp4'}]
bucket = Mock()
@@ -543,7 +541,7 @@ class VideosHandlerTestCase(VideoUploadTestMixin, VideoUploadPostTestsMixin, Cou
)
self.assertEqual(response.status_code, 400)
response = json.loads(response.content.decode('utf-8'))
self.assertEqual(response['error'], u'The file name for %s must contain only ASCII characters.' % file_name)
self.assertEqual(response['error'], 'The file name for %s must contain only ASCII characters.' % file_name)
@override_settings(AWS_ACCESS_KEY_ID='test_key_id', AWS_SECRET_ACCESS_KEY='test_secret', AWS_SECURITY_TOKEN='token')
@patch('boto.s3.key.Key')
@@ -784,7 +782,7 @@ class VideosHandlerTestCase(VideoUploadTestMixin, VideoUploadPostTestsMixin, Cou
)
mock_logger.info.assert_called_with(
u'VIDEOS: Video status update with id [%s], status [%s] and message [%s]',
'VIDEOS: Video status update with id [%s], status [%s] and message [%s]',
edx_video_id,
'upload_failed',
'server down'
@@ -1019,7 +1017,7 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase):
{
'extension': '.tiff'
},
u'This image file type is not supported. Supported file types are {supported_file_formats}.'.format(
'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())
)
),
@@ -1028,7 +1026,7 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase):
{
'size': settings.VIDEO_IMAGE_SETTINGS['VIDEO_IMAGE_MAX_BYTES'] + 10
},
u'This image file must be smaller than {image_max_size}.'.format(
'This image file must be smaller than {image_max_size}.'.format(
image_max_size=settings.VIDEO_IMAGE_MAX_FILE_SIZE_MB
)
),
@@ -1036,7 +1034,7 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase):
{
'size': settings.VIDEO_IMAGE_SETTINGS['VIDEO_IMAGE_MIN_BYTES'] - 10
},
u'This image file must be larger than {image_min_size}.'.format(
'This image file must be larger than {image_min_size}.'.format(
image_min_size=settings.VIDEO_IMAGE_MIN_FILE_SIZE_KB
)
),
@@ -1046,7 +1044,7 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase):
'width': 16, # 16x9
'height': 9
},
u'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( # lint-amnesty, pylint: disable=line-too-long
'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( # lint-amnesty, pylint: disable=line-too-long
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,
@@ -1058,7 +1056,7 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase):
'width': settings.VIDEO_IMAGE_MIN_WIDTH - 10,
'height': settings.VIDEO_IMAGE_MIN_HEIGHT
},
u'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( # lint-amnesty, pylint: disable=line-too-long
'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( # lint-amnesty, pylint: disable=line-too-long
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,
@@ -1071,8 +1069,8 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase):
'height': settings.VIDEO_IMAGE_MIN_HEIGHT - 10
},
(
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}.'
'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,
@@ -1086,8 +1084,8 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase):
'height': 100
},
(
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}.'
'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,
@@ -1122,14 +1120,14 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase):
'width': settings.VIDEO_IMAGE_MIN_WIDTH + 100,
'height': settings.VIDEO_IMAGE_MIN_HEIGHT + 200
},
u'This image file must have an aspect ratio of {video_image_aspect_ratio_text}.'.format(
'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
)
),
# Image file name validation
(
{
'prefix': u'nøn-åßç¡¡'
'prefix': 'nøn-åßç¡¡'
},
'The image file name can only contain letters, numbers, hyphens (-), and underscores (_).'
)
@@ -1199,7 +1197,7 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
(
{},
True,
u"Invalid provider None.",
"Invalid provider None.",
400
),
(
@@ -1207,7 +1205,7 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
'provider': ''
},
True,
u"Invalid provider .",
"Invalid provider .",
400
),
(
@@ -1215,7 +1213,7 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
'provider': 'dummy-provider'
},
True,
u"Invalid provider dummy-provider.",
"Invalid provider dummy-provider.",
400
),
(
@@ -1223,7 +1221,7 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
'provider': TranscriptProvider.CIELO24
},
True,
u"Invalid cielo24 fidelity None.",
"Invalid cielo24 fidelity None.",
400
),
(
@@ -1232,7 +1230,7 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
'cielo24_fidelity': 'PROFESSIONAL',
},
True,
u"Invalid cielo24 turnaround None.",
"Invalid cielo24 turnaround None.",
400
),
(
@@ -1243,7 +1241,7 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
'video_source_language': 'en'
},
True,
u"Invalid languages [].",
"Invalid languages [].",
400
),
(
@@ -1254,7 +1252,7 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
'video_source_language': 'es'
},
True,
u"Unsupported source language es.",
"Unsupported source language es.",
400
),
(
@@ -1274,7 +1272,7 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
'provider': TranscriptProvider.THREE_PLAY_MEDIA
},
True,
u"Invalid 3play turnaround None.",
"Invalid 3play turnaround None.",
400
),
(
@@ -1284,7 +1282,7 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
'video_source_language': 'zh',
},
True,
u"Unsupported source language zh.",
"Unsupported source language zh.",
400
),
(
@@ -1375,7 +1373,7 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
Test that transcript handler removes transcript preferences correctly.
"""
# First add course wide transcript preferences.
preferences = create_or_update_transcript_preferences(six.text_type(self.course.id))
preferences = create_or_update_transcript_preferences(str(self.course.id))
# Verify transcript preferences exist
self.assertIsNotNone(preferences)
@@ -1388,7 +1386,7 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
self.assertEqual(response.status_code, 204)
# Verify transcript preferences no loger exist
preferences = get_transcript_preferences(six.text_type(self.course.id))
preferences = get_transcript_preferences(str(self.course.id))
self.assertIsNone(preferences)
def test_remove_transcript_preferences_not_found(self):
@@ -1454,7 +1452,7 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
mock_conn.return_value = Mock(get_bucket=Mock(return_value=bucket))
mock_key_instance = Mock(
generate_url=Mock(
return_value='http://example.com/url_{file_name}'.format(file_name=file_name)
return_value=f'http://example.com/url_{file_name}'
)
)
# If extra calls are made, return a dummy
@@ -1487,7 +1485,7 @@ class VideoUrlsCsvTestCase(VideoUploadTestMixin, CourseTestCase):
VIEW_NAME = "video_encodings_download"
def setUp(self):
super(VideoUrlsCsvTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
VideoUploadConfig(profile_whitelist="profile1").save()
def _check_csv_response(self, expected_profiles):
@@ -1499,37 +1497,32 @@ class VideoUrlsCsvTestCase(VideoUploadTestMixin, CourseTestCase):
self.assertEqual(response.status_code, 200)
self.assertEqual(
response["Content-Disposition"],
u"attachment; filename=\"{course}_video_urls.csv\"".format(course=self.course.id.course)
f"attachment; filename=\"{self.course.id.course}_video_urls.csv\""
)
response_content = b"".join(response.streaming_content)
response_reader = StringIO(response_content.decode('utf-8') if six.PY3 else response_content)
response_reader = StringIO(response_content.decode())
reader = csv.DictReader(response_reader, dialect=csv.excel)
self.assertEqual(
reader.fieldnames,
(
["Name", "Duration", "Date Added", "Video ID", "Status"] +
[u"{} URL".format(profile) for profile in expected_profiles]
[f"{profile} URL" for profile in expected_profiles]
)
)
rows = list(reader)
self.assertEqual(len(rows), len(self.previous_uploads))
for i, row in enumerate(rows):
response_video = {
key.decode("utf-8") if six.PY2 else key: value.decode("utf-8") if six.PY2 else value
for key, value in row.items()
}
response_video = dict(row.items())
# Videos should be returned by creation date descending
original_video = self.previous_uploads[-(i + 1)]
client_video_id = original_video["client_video_id"].encode('utf-8') if six.PY2 \
else original_video["client_video_id"]
self.assertEqual(response_video["Name"].encode('utf-8') if six.PY2
else response_video["Name"], client_video_id)
client_video_id = original_video["client_video_id"]
self.assertEqual(response_video["Name"], client_video_id)
self.assertEqual(response_video["Duration"], str(original_video["duration"]))
dateutil.parser.parse(response_video["Date Added"])
self.assertEqual(response_video["Video ID"], original_video["edx_video_id"])
self.assertEqual(response_video["Status"], convert_video_status(original_video))
for profile in expected_profiles:
response_profile_url = response_video["{} URL".format(profile)]
response_profile_url = response_video[f"{profile} URL"]
original_encoded_for_profile = next(
(
original_encoded
@@ -1539,10 +1532,8 @@ class VideoUrlsCsvTestCase(VideoUploadTestMixin, CourseTestCase):
None
)
if original_encoded_for_profile:
original_encoded_for_profile_url = original_encoded_for_profile["url"].encode('utf-8') if six.PY2 \
else original_encoded_for_profile["url"]
self.assertEqual(response_profile_url.encode('utf-8') if six.PY2 else response_profile_url,
original_encoded_for_profile_url)
original_encoded_for_profile_url = original_encoded_for_profile["url"]
self.assertEqual(response_profile_url, original_encoded_for_profile_url)
else:
self.assertEqual(response_profile_url, "")
@@ -1555,7 +1546,7 @@ class VideoUrlsCsvTestCase(VideoUploadTestMixin, CourseTestCase):
def test_non_ascii_course(self):
course = CourseFactory.create(
number=u"nón-äscii",
number="nón-äscii",
video_upload_pipeline={
"course_video_upload_token": self.test_token,
}
@@ -1564,5 +1555,5 @@ class VideoUrlsCsvTestCase(VideoUploadTestMixin, CourseTestCase):
self.assertEqual(response.status_code, 200)
self.assertEqual(
response["Content-Disposition"],
u"attachment; filename*=utf-8''n%C3%B3n-%C3%A4scii_video_urls.csv"
"attachment; filename*=utf-8''n%C3%B3n-%C3%A4scii_video_urls.csv"
)

View File

@@ -17,7 +17,7 @@ class StudioPageTestCase(CourseTestCase):
"""
def setUp(self):
super(StudioPageTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
super().setUp()
self.chapter = ItemFactory.create(parent_location=self.course.location,
category='chapter', display_name="Week 1")
self.sequential = ItemFactory.create(parent_location=self.chapter.location,
@@ -37,7 +37,7 @@ class StudioPageTestCase(CourseTestCase):
"""
Returns the HTML for the xblock when shown within a unit or container page.
"""
preview_url = '/xblock/{usage_key}/{view_name}'.format(usage_key=xblock.location, view_name=view_name)
preview_url = f'/xblock/{xblock.location}/{view_name}'
resp = self.client.get_json(preview_url)
self.assertEqual(resp.status_code, 200)
resp_content = json.loads(resp.content.decode('utf-8'))

View File

@@ -21,10 +21,10 @@ from edxval.api import (
)
from opaque_keys.edx.keys import CourseKey
from openedx.core.djangoapps.video_config.models import VideoTranscriptEnabledFlag
from openedx.core.djangoapps.video_pipeline.api import update_3rd_party_transcription_service_credentials
from common.djangoapps.student.auth import has_studio_write_access
from common.djangoapps.util.json_request import JsonResponse, expect_json
from openedx.core.djangoapps.video_config.models import VideoTranscriptEnabledFlag
from openedx.core.djangoapps.video_pipeline.api import update_3rd_party_transcription_service_credentials
from xmodule.video_module.transcripts_utils import Transcript, TranscriptsGenerationException
from .videos import TranscriptProvider
@@ -72,14 +72,14 @@ def validate_transcript_credentials(provider, **credentials):
must_have_prop for must_have_prop in must_have_props if must_have_prop not in list(credentials.keys())
]
if missing:
error_message = u'{missing} must be specified.'.format(missing=' and '.join(missing))
error_message = '{missing} must be specified.'.format(missing=' and '.join(missing))
return error_message, validated_credentials
validated_credentials.update({
prop: credentials[prop] for prop in must_have_props
})
else:
error_message = u'Invalid Provider {provider}.'.format(provider=provider)
error_message = f'Invalid Provider {provider}.'
return error_message, validated_credentials
@@ -148,7 +148,7 @@ def transcript_download_handler(request):
missing = [attr for attr in ['edx_video_id', 'language_code'] if attr not in request.GET]
if missing:
return JsonResponse(
{'error': _(u'The following parameters are required: {missing}.').format(missing=', '.join(missing))},
{'error': _('The following parameters are required: {missing}.').format(missing=', '.join(missing))},
status=400
)
@@ -158,7 +158,7 @@ def transcript_download_handler(request):
if transcript:
name_and_extension = os.path.splitext(transcript['file_name'])
basename, file_format = name_and_extension[0], name_and_extension[1][1:]
transcript_filename = '{base_name}.{ext}'.format(base_name=basename, ext=Transcript.SRT)
transcript_filename = f'{basename}.{Transcript.SRT}'
transcript_content = Transcript.convert(
content=transcript['content'],
input_format=file_format,
@@ -166,7 +166,7 @@ def transcript_download_handler(request):
)
# Construct an HTTP response
response = HttpResponse(transcript_content, content_type=Transcript.mime_types[Transcript.SRT])
response['Content-Disposition'] = u'attachment; filename="{filename}"'.format(filename=transcript_filename)
response['Content-Disposition'] = f'attachment; filename="{transcript_filename}"'
else:
response = HttpResponseNotFound()
@@ -188,16 +188,16 @@ def validate_transcript_upload_data(data, files):
must_have_attrs = ['edx_video_id', 'language_code', 'new_language_code']
missing = [attr for attr in must_have_attrs if attr not in data]
if missing:
error = _(u'The following parameters are required: {missing}.').format(missing=', '.join(missing))
error = _('The following parameters are required: {missing}.').format(missing=', '.join(missing))
elif (
data['language_code'] != data['new_language_code'] and
data['new_language_code'] in get_available_transcript_languages(video_id=data['edx_video_id'])
):
error = _(u'A transcript with the "{language_code}" language code already exists.'.format( # lint-amnesty, pylint: disable=translation-of-non-string
error = _('A transcript with the "{language_code}" language code already exists.'.format( # lint-amnesty, pylint: disable=translation-of-non-string
language_code=data['new_language_code']
))
elif 'file' not in files:
error = _(u'A transcript file is required.')
error = _('A transcript file is required.')
return error
@@ -247,7 +247,7 @@ def transcript_upload_handler(request):
response = JsonResponse(status=201)
except (TranscriptsGenerationException, UnicodeDecodeError):
response = JsonResponse(
{'error': _(u'There is a problem with this transcript file. Try to upload a different file.')},
{'error': _('There is a problem with this transcript file. Try to upload a different file.')},
status=400
)

View File

@@ -22,7 +22,6 @@ from django.utils.translation import ugettext as _
from edxval.api import create_external_video, create_or_update_video_transcript
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import UsageKey
from six import text_type
from cms.djangoapps.contentstore.views.videos import TranscriptProvider
from common.djangoapps.student.auth import has_course_author_access
@@ -82,7 +81,7 @@ def link_video_to_component(video_component, user):
"""
edx_video_id = clean_video_id(video_component.edx_video_id)
if not edx_video_id:
edx_video_id = create_external_video(display_name=u'external video')
edx_video_id = create_external_video(display_name='external video')
video_component.edx_video_id = edx_video_id
video_component.save_with_metadata(user)
@@ -146,9 +145,9 @@ def validate_video_module(request, locator):
try:
item = _get_item(request, {'locator': locator})
if item.category != 'video':
error = _(u'Transcripts are supported only for "video" modules.')
error = _('Transcripts are supported only for "video" modules.')
except (InvalidKeyError, ItemNotFoundError):
error = _(u'Cannot find item by locator.')
error = _('Cannot find item by locator.')
return error, item
@@ -169,13 +168,13 @@ def validate_transcript_upload_data(request):
video_locator = data.get('locator')
edx_video_id = data.get('edx_video_id')
if not video_locator:
error = _(u'Video locator is required.')
error = _('Video locator is required.')
elif 'transcript-file' not in files:
error = _(u'A transcript file is required.')
error = _('A transcript file is required.')
elif os.path.splitext(files['transcript-file'].name)[1][1:] != Transcript.SRT:
error = _(u'This transcript file type is not supported.')
error = _('This transcript file type is not supported.')
elif 'edx_video_id' not in data:
error = _(u'Video ID is required.')
error = _('Video ID is required.')
if not error:
error, video = validate_video_module(request, video_locator)
@@ -209,7 +208,7 @@ def upload_transcripts(request):
# check if we need to create an external VAL video to associate the transcript
# and save its ID on the video component.
if not edx_video_id:
edx_video_id = create_external_video(display_name=u'external video')
edx_video_id = create_external_video(display_name='external video')
video.edx_video_id = edx_video_id
video.save_with_metadata(request.user)
@@ -225,11 +224,11 @@ def upload_transcripts(request):
).encode()
transcript_created = create_or_update_video_transcript(
video_id=edx_video_id,
language_code=u'en',
language_code='en',
metadata={
'provider': TranscriptProvider.CUSTOM,
'file_format': Transcript.SJSON,
'language_code': u'en'
'language_code': 'en'
},
file_data=ContentFile(sjson_subs),
)
@@ -240,7 +239,7 @@ def upload_transcripts(request):
except (TranscriptsGenerationException, UnicodeDecodeError):
response = JsonResponse({
'status': _(u'There is a problem with this transcript file. Try to upload a different file.')
'status': _('There is a problem with this transcript file. Try to upload a different file.')
}, status=400)
return response
@@ -258,13 +257,13 @@ def download_transcripts(request):
raise Http404
try:
content, filename, mimetype = get_transcript(video, lang=u'en')
content, filename, mimetype = get_transcript(video, lang='en')
except NotFoundError:
raise Http404 # lint-amnesty, pylint: disable=raise-missing-from
# Construct an HTTP response
response = HttpResponse(content, content_type=mimetype)
response['Content-Disposition'] = u'attachment; filename="{filename}"'.format(filename=filename)
response['Content-Disposition'] = f'attachment; filename="{filename}"'
return response
@@ -310,16 +309,16 @@ def check_transcripts(request): # lint-amnesty, pylint: disable=too-many-statem
try:
__, videos, item = _validate_transcripts_data(request)
except TranscriptsRequestValidationException as e:
return error_response(transcripts_presence, text_type(e))
return error_response(transcripts_presence, str(e))
transcripts_presence['status'] = 'Success'
try:
edx_video_id = clean_video_id(videos.get('edx_video_id'))
get_transcript_from_val(edx_video_id=edx_video_id, lang=u'en')
get_transcript_from_val(edx_video_id=edx_video_id, lang='en')
command = 'found'
except NotFoundError:
filename = 'subs_{0}.srt.sjson'.format(item.sub)
filename = f'subs_{item.sub}.srt.sjson'
content_location = StaticContent.compute_location(item.location.course_key, filename)
try:
local_transcripts = contentstore().find(content_location).data.decode('utf-8')
@@ -333,13 +332,13 @@ def check_transcripts(request): # lint-amnesty, pylint: disable=too-many-statem
transcripts_presence['is_youtube_mode'] = True
# youtube local
filename = 'subs_{0}.srt.sjson'.format(youtube_id)
filename = f'subs_{youtube_id}.srt.sjson'
content_location = StaticContent.compute_location(item.location.course_key, filename)
try:
local_transcripts = contentstore().find(content_location).data.decode('utf-8')
transcripts_presence['youtube_local'] = True
except NotFoundError:
log.debug(u"Can't find transcripts in storage for youtube id: %s", youtube_id)
log.debug("Can't find transcripts in storage for youtube id: %s", youtube_id)
# youtube server
youtube_text_api = copy.deepcopy(settings.YOUTUBE['TEXT_API'])
@@ -367,13 +366,13 @@ def check_transcripts(request): # lint-amnesty, pylint: disable=too-many-statem
# Check for html5 local transcripts presence
html5_subs = []
for html5_id in videos['html5']:
filename = 'subs_{0}.srt.sjson'.format(html5_id)
filename = f'subs_{html5_id}.srt.sjson'
content_location = StaticContent.compute_location(item.location.course_key, filename)
try:
html5_subs.append(contentstore().find(content_location).data)
transcripts_presence['html5_local'].append(html5_id)
except NotFoundError:
log.debug(u"Can't find transcripts in storage for non-youtube video_id: %s", html5_id)
log.debug("Can't find transcripts in storage for non-youtube video_id: %s", html5_id)
if len(html5_subs) == 2: # check html5 transcripts for equality
transcripts_presence['html5_equal'] = (
json.loads(html5_subs[0].decode('utf-8')) == json.loads(html5_subs[1].decode('utf-8'))
@@ -430,12 +429,12 @@ def _transcripts_logic(transcripts_presence, videos):
else: # html5 source have no subtitles
# check if item sub has subtitles
if transcripts_presence['current_item_subs'] and not transcripts_presence['is_youtube_mode']:
log.debug(u"Command is use existing %s subs", transcripts_presence['current_item_subs'])
log.debug("Command is use existing %s subs", transcripts_presence['current_item_subs'])
command = 'use_existing'
else:
command = 'not_found'
log.debug(
u"Resulted command: %s, current transcripts: %s, youtube mode: %s",
"Resulted command: %s, current transcripts: %s, youtube mode: %s",
command,
transcripts_presence['current_item_subs'],
transcripts_presence['is_youtube_mode']
@@ -500,7 +499,7 @@ def validate_transcripts_request(request, include_yt=False, include_html5=False)
# Loads the request data
data = json.loads(request.GET.get('data', '{}'))
if not data:
error = _(u'Incoming video data is empty.')
error = _('Incoming video data is empty.')
else:
error, video = validate_video_module(request, locator=data.get('locator'))
if not error:
@@ -546,7 +545,7 @@ def choose_transcripts(request):
video.location,
subs_id=chosen_html5_id,
file_name=chosen_html5_id,
language=u'en'
language='en'
)
except NotFoundError:
return error_response({}, _('No such transcript.'))
@@ -555,7 +554,7 @@ def choose_transcripts(request):
edx_video_id = link_video_to_component(video, request.user)
# 3. Upload the retrieved transcript to DS for the linked video ID.
success = save_video_transcript(edx_video_id, input_format, transcript_content, language_code=u'en')
success = save_video_transcript(edx_video_id, input_format, transcript_content, language_code='en')
if success:
response = JsonResponse({'edx_video_id': edx_video_id, 'status': 'Success'}, status=200)
else:
@@ -585,7 +584,7 @@ def rename_transcripts(request):
video.location,
subs_id=video.sub,
file_name=video.sub,
language=u'en'
language='en'
)
except NotFoundError:
return error_response({}, _('No such transcript.'))
@@ -594,7 +593,7 @@ def rename_transcripts(request):
edx_video_id = link_video_to_component(video, request.user)
# 3. Upload the retrieved transcript to DS for the linked video ID.
success = save_video_transcript(edx_video_id, input_format, transcript_content, language_code=u'en')
success = save_video_transcript(edx_video_id, input_format, transcript_content, language_code='en')
if success:
response = JsonResponse({'edx_video_id': edx_video_id, 'status': 'Success'}, status=200)
else:
@@ -619,20 +618,20 @@ def replace_transcripts(request):
if error:
response = error_response({}, error)
elif not youtube_id:
response = error_response({}, _(u'YouTube ID is required.'))
response = error_response({}, _('YouTube ID is required.'))
else:
# 1. Download transcript from YouTube.
try:
video = validated_data['video']
transcript_content = download_youtube_subs(youtube_id, video, settings)
except GetTranscriptsFromYouTubeException as e:
return error_response({}, text_type(e))
return error_response({}, str(e))
# 2. Link a video to video component if its not already linked to one.
edx_video_id = link_video_to_component(video, request.user)
# 3. Upload YT transcript to DS for the linked video ID.
success = save_video_transcript(edx_video_id, Transcript.SJSON, transcript_content, language_code=u'en')
success = save_video_transcript(edx_video_id, Transcript.SJSON, transcript_content, language_code='en')
if success:
response = JsonResponse({'edx_video_id': edx_video_id, 'status': 'Success'}, status=200)
else:

View File

@@ -117,7 +117,7 @@ def _course_team_user(request, course_key, email):
user = User.objects.get(email=email)
except Exception: # pylint: disable=broad-except
msg = {
"error": _(u"Could not find user by email address '{email}'.").format(email=email),
"error": _("Could not find user by email address '{email}'.").format(email=email),
}
return JsonResponse(msg, 404)
@@ -161,7 +161,7 @@ def _course_team_user(request, course_key, email):
# can't modify an inactive user but can remove it
if not (user.is_active or new_role is None):
msg = {
"error": _(u'User {email} has registered but has not yet activated their account.').format(email=email),
"error": _('User {email} has registered but has not yet activated their account.').format(email=email),
}
return JsonResponse(msg, 400)

View File

@@ -12,9 +12,7 @@ from contextlib import closing
from datetime import datetime, timedelta
from uuid import uuid4
import six
from boto import s3
from boto.sts import STSConnection # lint-amnesty, pylint: disable=unused-import
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.staticfiles.storage import staticfiles_storage
@@ -23,6 +21,7 @@ from django.urls import reverse
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_noop
from django.views.decorators.http import require_GET, require_http_methods, require_POST
from edx_toggles.toggles import LegacyWaffleFlagNamespace, LegacyWaffleSwitchNamespace
from edxval.api import (
SortDirection,
VideoSortField,
@@ -44,8 +43,8 @@ from rest_framework import status as rest_status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from edx_toggles.toggles import LegacyWaffleFlagNamespace, LegacyWaffleSwitchNamespace
from common.djangoapps.edxmako.shortcuts import render_to_response
from common.djangoapps.util.json_request import JsonResponse, expect_json
from openedx.core.djangoapps.video_config.models import VideoTranscriptEnabledFlag
from openedx.core.djangoapps.video_pipeline.config.waffle import (
DEPRECATE_YOUTUBE,
@@ -54,7 +53,6 @@ from openedx.core.djangoapps.video_pipeline.config.waffle import (
)
from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag
from openedx.core.lib.api.view_utils import view_auth_classes
from common.djangoapps.util.json_request import JsonResponse, expect_json
from xmodule.video_module.transcripts_utils import Transcript
from ..models import VideoUploadConfig
@@ -80,11 +78,11 @@ WAFFLE_SWITCHES = LegacyWaffleSwitchNamespace(name=WAFFLE_NAMESPACE)
VIDEO_IMAGE_UPLOAD_ENABLED = 'video_image_upload_enabled'
# Waffle flag namespace for studio
WAFFLE_STUDIO_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name=u'studio')
WAFFLE_STUDIO_FLAG_NAMESPACE = LegacyWaffleFlagNamespace(name='studio')
ENABLE_VIDEO_UPLOAD_PAGINATION = CourseWaffleFlag(
waffle_namespace=WAFFLE_STUDIO_FLAG_NAMESPACE,
flag_name=u'enable_video_upload_pagination',
flag_name='enable_video_upload_pagination',
module_name=__name__,
)
# Default expiration, in seconds, of one-time URLs used for uploading videos.
@@ -103,7 +101,7 @@ MAX_UPLOAD_HOURS = 24
VIDEOS_PER_PAGE = 100
class TranscriptProvider(object):
class TranscriptProvider:
"""
Transcription Provider Enumeration
"""
@@ -112,7 +110,7 @@ class TranscriptProvider(object):
CUSTOM = 'Custom'
class StatusDisplayStrings(object):
class StatusDisplayStrings:
"""
A class to map status strings as stored in VAL to display strings for the
video upload page
@@ -251,7 +249,7 @@ def video_images_handler(request, course_key_string, edx_video_id=None):
return HttpResponseNotFound()
if 'file' not in request.FILES:
return JsonResponse({'error': _(u'An image file is required.')}, status=400)
return JsonResponse({'error': _('An image file is required.')}, status=400)
image_file = request.FILES['file']
error = validate_video_image(image_file)
@@ -261,7 +259,7 @@ def video_images_handler(request, course_key_string, edx_video_id=None):
with closing(image_file):
image_url = update_video_image(edx_video_id, course_key_string, image_file, image_file.name)
LOGGER.info(
u'VIDEOS: Video image uploaded for edx_video_id [%s] in course [%s]', edx_video_id, course_key_string
'VIDEOS: Video image uploaded for edx_video_id [%s] in course [%s]', edx_video_id, course_key_string
)
return JsonResponse({'image_url': image_url})
@@ -297,17 +295,17 @@ def validate_transcript_preferences(provider, cielo24_fidelity, cielo24_turnarou
# Validate transcription turnaround
if cielo24_turnaround not in transcription_plans[provider]['turnaround']:
error = u'Invalid cielo24 turnaround {}.'.format(cielo24_turnaround)
error = f'Invalid cielo24 turnaround {cielo24_turnaround}.'
return error, preferences
# Validate transcription languages
supported_languages = transcription_plans[provider]['fidelity'][cielo24_fidelity]['languages']
if video_source_language not in supported_languages:
error = u'Unsupported source language {}.'.format(video_source_language)
error = f'Unsupported source language {video_source_language}.'
return error, preferences
if not preferred_languages or not set(preferred_languages) <= set(supported_languages.keys()):
error = 'Invalid languages {}.'.format(preferred_languages)
error = f'Invalid languages {preferred_languages}.'
return error, preferences
# Validated Cielo24 preferences
@@ -318,23 +316,23 @@ def validate_transcript_preferences(provider, cielo24_fidelity, cielo24_turnarou
'preferred_languages': preferred_languages,
}
else:
error = u'Invalid cielo24 fidelity {}.'.format(cielo24_fidelity)
error = f'Invalid cielo24 fidelity {cielo24_fidelity}.'
elif provider == TranscriptProvider.THREE_PLAY_MEDIA:
# Validate transcription turnaround
if three_play_turnaround not in transcription_plans[provider]['turnaround']:
error = u'Invalid 3play turnaround {}.'.format(three_play_turnaround)
error = f'Invalid 3play turnaround {three_play_turnaround}.'
return error, preferences
# Validate transcription languages
valid_translations_map = transcription_plans[provider]['translations']
if video_source_language not in list(valid_translations_map.keys()):
error = u'Unsupported source language {}.'.format(video_source_language)
error = f'Unsupported source language {video_source_language}.'
return error, preferences
valid_target_languages = valid_translations_map[video_source_language]
if not preferred_languages or not set(preferred_languages) <= set(valid_target_languages):
error = u'Invalid languages {}.'.format(preferred_languages)
error = f'Invalid languages {preferred_languages}.'
return error, preferences
# Validated 3PlayMedia preferences
@@ -344,7 +342,7 @@ def validate_transcript_preferences(provider, cielo24_fidelity, cielo24_turnarou
'preferred_languages': preferred_languages,
}
else:
error = u'Invalid provider {}.'.format(provider)
error = f'Invalid provider {provider}.'
return error, preferences
@@ -410,7 +408,7 @@ def video_encodings_download(request, course_key_string):
# Translators: This is the header for a CSV file column
# containing URLs for video encodings for the named profile
# (e.g. desktop, mobile high quality, mobile low quality)
return _(u"{profile_name} URL").format(profile_name=profile)
return _("{profile_name} URL").format(profile_name=profile)
profile_whitelist = VideoUploadConfig.get_profile_whitelist()
videos, __ = _get_videos(course)
@@ -447,10 +445,7 @@ def video_encodings_download(request, course_key_string):
if encoded_video["profile"] in profile_whitelist
]
)
return {
key.encode("utf-8") if six.PY2 else key: value.encode("utf-8") if six.PY2 else value
for key, value in ret.items()
}
return dict(ret.items())
# Write csv to bytes-like object. We need a separate writer and buffer as the csv
# writer writes str and the FileResponse expects a bytes files.
@@ -458,11 +453,7 @@ def video_encodings_download(request, course_key_string):
buffer_writer = codecs.getwriter("utf-8")(buffer)
writer = csv.DictWriter(
buffer_writer,
[
col_name.encode("utf-8") if six.PY2 else col_name
for col_name
in [name_col, duration_col, added_col, video_id_col, status_col] + profile_cols
],
[name_col, duration_col, added_col, video_id_col, status_col] + profile_cols,
dialect=csv.excel
)
writer.writeheader()
@@ -511,7 +502,7 @@ def convert_video_status(video, is_video_encodes_ready=False):
if video['status'] == 'upload' and (now - video['created']) > timedelta(hours=MAX_UPLOAD_HOURS):
new_status = 'upload_failed'
status = StatusDisplayStrings.get(new_status)
message = u'Video with id [%s] is still in upload after [%s] hours, setting status to [%s]' % (
message = 'Video with id [{}] is still in upload after [{}] hours, setting status to [{}]'.format(
video['edx_video_id'], MAX_UPLOAD_HOURS, new_status
)
send_video_status_update([
@@ -536,7 +527,7 @@ def _get_videos(course, pagination_conf=None):
Retrieves the list of videos from VAL corresponding to this course.
"""
videos, pagination_context = get_videos_for_course(
six.text_type(course.id),
str(course.id),
VideoSortField.created,
SortDirection.desc,
pagination_conf
@@ -576,7 +567,7 @@ def _get_index_videos(course, pagination_conf=None):
"""
Returns the information about each video upload required for the video list
"""
course_id = six.text_type(course.id)
course_id = str(course.id)
attrs = [
'edx_video_id', 'client_video_id', 'created', 'duration',
'status', 'courses', 'transcripts', 'transcription_status',
@@ -618,7 +609,7 @@ def get_all_transcript_languages():
all_languages_dict = dict(settings.ALL_LANGUAGES, **third_party_transcription_languages)
# Return combined system settings and 3rd party transcript languages.
all_languages = []
for key, value in sorted(six.iteritems(all_languages_dict), key=lambda k_v: k_v[1]):
for key, value in sorted(all_languages_dict.items(), key=lambda k_v: k_v[1]):
all_languages.append({
'language_code': key,
'language_text': value
@@ -634,9 +625,9 @@ def videos_index_html(course, pagination_conf=None):
previous_uploads, pagination_context = _get_index_videos(course, pagination_conf)
context = {
'context_course': course,
'image_upload_url': reverse_course_url('video_images_handler', six.text_type(course.id)),
'video_handler_url': reverse_course_url('videos_handler', six.text_type(course.id)),
'encodings_download_url': reverse_course_url('video_encodings_download', six.text_type(course.id)),
'image_upload_url': reverse_course_url('video_images_handler', str(course.id)),
'video_handler_url': reverse_course_url('videos_handler', str(course.id)),
'encodings_download_url': reverse_course_url('video_encodings_download', str(course.id)),
'default_video_image_url': _get_default_video_image_url(),
'previous_uploads': previous_uploads,
'concurrent_upload_limit': settings.VIDEO_UPLOAD_PIPELINE.get('CONCURRENT_UPLOAD_LIMIT', 0),
@@ -657,7 +648,7 @@ def videos_index_html(course, pagination_conf=None):
'video_transcript_settings': {
'transcript_download_handler_url': reverse('transcript_download_handler'),
'transcript_upload_handler_url': reverse('transcript_upload_handler'),
'transcript_delete_handler_url': reverse_course_url('transcript_delete_handler', six.text_type(course.id)),
'transcript_delete_handler_url': reverse_course_url('transcript_delete_handler', str(course.id)),
'trancript_download_file_format': Transcript.SRT
},
'pagination_context': pagination_context
@@ -667,15 +658,15 @@ def videos_index_html(course, pagination_conf=None):
context['video_transcript_settings'].update({
'transcript_preferences_handler_url': reverse_course_url(
'transcript_preferences_handler',
six.text_type(course.id)
str(course.id)
),
'transcript_credentials_handler_url': reverse_course_url(
'transcript_credentials_handler',
six.text_type(course.id)
str(course.id)
),
'transcription_plans': get_3rd_party_transcription_plans(),
})
context['active_transcript_preferences'] = get_transcript_preferences(six.text_type(course.id))
context['active_transcript_preferences'] = get_transcript_preferences(str(course.id))
# Cached state for transcript providers' credentials (org-specific)
context['transcript_credentials'] = get_transcript_credentials_state_for_org(course.id.org)
@@ -748,15 +739,15 @@ def videos_post(course, request):
try:
file_name.encode('ascii')
except UnicodeEncodeError:
error_msg = u'The file name for %s must contain only ASCII characters.' % file_name
error_msg = 'The file name for %s must contain only ASCII characters.' % file_name
return {'error': error_msg}, 400
edx_video_id = six.text_type(uuid4())
edx_video_id = str(uuid4())
key = storage_service_key(bucket, file_name=edx_video_id)
metadata_list = [
('client_video_id', file_name),
('course_key', six.text_type(course.id)),
('course_key', str(course.id)),
]
deprecate_youtube = waffle_flags()[DEPRECATE_YOUTUBE]
@@ -769,7 +760,7 @@ def videos_post(course, request):
is_video_transcript_enabled = VideoTranscriptEnabledFlag.feature_enabled(course.id)
if is_video_transcript_enabled:
transcript_preferences = get_transcript_preferences(six.text_type(course.id))
transcript_preferences = get_transcript_preferences(str(course.id))
if transcript_preferences is not None:
metadata_list.append(('transcript_preferences', json.dumps(transcript_preferences)))
@@ -788,7 +779,7 @@ def videos_post(course, request):
'client_video_id': file_name,
'duration': 0,
'encoded_videos': [],
'courses': [six.text_type(course.id)]
'courses': [str(course.id)]
})
resp_files.append({'file_name': file_name, 'upload_url': upload_url, 'edx_video_id': edx_video_id})
@@ -840,7 +831,7 @@ def send_video_status_update(updates):
for update in updates:
update_video_status(update.get('edxVideoId'), update.get('status'))
LOGGER.info(
u'VIDEOS: Video status update with id [%s], status [%s] and message [%s]',
'VIDEOS: Video status update with id [%s], status [%s] and message [%s]',
update.get('edxVideoId'),
update.get('status'),
update.get('message')
@@ -880,7 +871,7 @@ def _update_pagination_context(request):
"""
Updates session with posted value
"""
error_msg = _(u'A non zero positive integer is expected')
error_msg = _('A non zero positive integer is expected')
try:
videos_per_page = int(request.POST.get('value'))
if videos_per_page <= 0: