refactor: pyupgrade in common/lib/xmodule (#26781)
This commit is contained in:
@@ -3,9 +3,9 @@
|
||||
|
||||
import os
|
||||
import unittest
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import ddt
|
||||
from mock import Mock, patch
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from opaque_keys.edx.locator import AssetLocator, CourseLocator
|
||||
from path import Path as path
|
||||
@@ -54,7 +54,7 @@ injected humour and the like).
|
||||
"""
|
||||
|
||||
|
||||
class Content(object):
|
||||
class Content:
|
||||
"""
|
||||
A class with location and content_type members
|
||||
"""
|
||||
@@ -64,7 +64,7 @@ class Content(object):
|
||||
self.data = None
|
||||
|
||||
|
||||
class FakeGridFsItem(object):
|
||||
class FakeGridFsItem:
|
||||
"""
|
||||
This class provides the basic methods to get data from a GridFS item
|
||||
"""
|
||||
@@ -112,19 +112,19 @@ class ContentTest(unittest.TestCase): # lint-amnesty, pylint: disable=missing-c
|
||||
assert content.thumbnail_location is None
|
||||
|
||||
@ddt.data(
|
||||
(u"monsters__.jpg", u"monsters__.jpg"),
|
||||
(u"monsters__.png", u"monsters__-png.jpg"),
|
||||
(u"dots.in.name.jpg", u"dots.in.name.jpg"),
|
||||
(u"dots.in.name.png", u"dots.in.name-png.jpg"),
|
||||
("monsters__.jpg", "monsters__.jpg"),
|
||||
("monsters__.png", "monsters__-png.jpg"),
|
||||
("dots.in.name.jpg", "dots.in.name.jpg"),
|
||||
("dots.in.name.png", "dots.in.name-png.jpg"),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_generate_thumbnail_image(self, original_filename, thumbnail_filename):
|
||||
content_store = ContentStore()
|
||||
content = Content(AssetLocator(CourseLocator(u'mitX', u'800', u'ignore_run'), u'asset', original_filename),
|
||||
content = Content(AssetLocator(CourseLocator('mitX', '800', 'ignore_run'), 'asset', original_filename),
|
||||
None)
|
||||
(thumbnail_content, thumbnail_file_location) = content_store.generate_thumbnail(content)
|
||||
assert thumbnail_content is None
|
||||
assert AssetLocator(CourseLocator(u'mitX', u'800', u'ignore_run'), u'thumbnail', thumbnail_filename) ==\
|
||||
assert AssetLocator(CourseLocator('mitX', '800', 'ignore_run'), 'thumbnail', thumbnail_filename) ==\
|
||||
thumbnail_file_location
|
||||
|
||||
@patch('xmodule.contentstore.content.Image')
|
||||
@@ -135,7 +135,7 @@ class ContentTest(unittest.TestCase): # lint-amnesty, pylint: disable=missing-c
|
||||
image_class_mock.open.return_value = mock_image
|
||||
|
||||
content_store = ContentStore()
|
||||
content = Content(AssetLocator(CourseLocator(u'mitX', u'800', u'ignore_run'), u'asset', "monsters.jpg"),
|
||||
content = Content(AssetLocator(CourseLocator('mitX', '800', 'ignore_run'), 'asset', "monsters.jpg"),
|
||||
"image/jpeg")
|
||||
content.data = b'mock data'
|
||||
content_store.generate_thumbnail(content)
|
||||
@@ -147,13 +147,13 @@ class ContentTest(unittest.TestCase): # lint-amnesty, pylint: disable=missing-c
|
||||
# SVG files should be stored in original form for thumbnail purposes.
|
||||
content_store = ContentStore()
|
||||
content_store.save = Mock()
|
||||
thumbnail_filename = u'test.svg'
|
||||
content = Content(AssetLocator(CourseLocator(u'mitX', u'800', u'ignore_run'), u'asset', u'test.svg'),
|
||||
thumbnail_filename = 'test.svg'
|
||||
content = Content(AssetLocator(CourseLocator('mitX', '800', 'ignore_run'), 'asset', 'test.svg'),
|
||||
'image/svg+xml')
|
||||
content.data = b'mock svg file'
|
||||
(thumbnail_content, thumbnail_file_location) = content_store.generate_thumbnail(content)
|
||||
assert thumbnail_content.data.read() == b'mock svg file'
|
||||
assert AssetLocator(CourseLocator(u'mitX', u'800', u'ignore_run'), u'thumbnail', thumbnail_filename) ==\
|
||||
assert AssetLocator(CourseLocator('mitX', '800', 'ignore_run'), 'thumbnail', thumbnail_filename) ==\
|
||||
thumbnail_file_location
|
||||
|
||||
def test_compute_location(self):
|
||||
@@ -162,13 +162,13 @@ class ContentTest(unittest.TestCase): # lint-amnesty, pylint: disable=missing-c
|
||||
asset_location = StaticContent.compute_location(
|
||||
CourseKey.from_string('mitX/400/ignore'), 'subs__1eo_jXvZnE .srt.sjson'
|
||||
)
|
||||
assert AssetLocator(CourseLocator(u'mitX', u'400', u'ignore', deprecated=True),
|
||||
u'asset', u'subs__1eo_jXvZnE_.srt.sjson') == asset_location
|
||||
assert AssetLocator(CourseLocator('mitX', '400', 'ignore', deprecated=True),
|
||||
'asset', 'subs__1eo_jXvZnE_.srt.sjson') == asset_location
|
||||
|
||||
def test_get_location_from_path(self):
|
||||
asset_location = StaticContent.get_location_from_path(u'/c4x/a/b/asset/images_course_image.jpg')
|
||||
assert AssetLocator(CourseLocator(u'a', u'b', None, deprecated=True),
|
||||
u'asset', u'images_course_image.jpg', deprecated=True) == asset_location
|
||||
asset_location = StaticContent.get_location_from_path('/c4x/a/b/asset/images_course_image.jpg')
|
||||
assert AssetLocator(CourseLocator('a', 'b', None, deprecated=True),
|
||||
'asset', 'images_course_image.jpg', deprecated=True) == asset_location
|
||||
|
||||
def test_static_content_stream_stream_data(self):
|
||||
"""
|
||||
@@ -210,8 +210,8 @@ class ContentTest(unittest.TestCase): # lint-amnesty, pylint: disable=missing-c
|
||||
"""
|
||||
Test that only one filename starts with 000.
|
||||
"""
|
||||
output_root = path(u'common/static/xmodule/descriptors/js')
|
||||
output_root = path('common/static/xmodule/descriptors/js')
|
||||
file_owners = _write_js(output_root, _list_descriptors(), 'get_studio_view_js')
|
||||
js_file_paths = set(file_path for file_path in sum(list(file_owners.values()), []) if os.path.basename(file_path).startswith('000-')) # lint-amnesty, pylint: disable=line-too-long
|
||||
js_file_paths = {file_path for file_path in sum(list(file_owners.values()), []) if os.path.basename(file_path).startswith('000-')} # lint-amnesty, pylint: disable=line-too-long
|
||||
assert len(js_file_paths) == 1
|
||||
assert 'XModule.Descriptor = (function() {' in open(js_file_paths.pop()).read()
|
||||
|
||||
@@ -44,7 +44,7 @@ class CourseMetadataUtilsTestCase(TestCase):
|
||||
"""
|
||||
Set up module store testing capabilities and initialize test courses.
|
||||
"""
|
||||
super(CourseMetadataUtilsTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
mongo_builder = MongoModulestoreBuilder()
|
||||
split_builder = VersioningModulestoreBuilder()
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
import itertools
|
||||
import unittest
|
||||
from datetime import datetime, timedelta
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
import ddt
|
||||
from dateutil import parser
|
||||
from django.conf import settings
|
||||
from django.test import override_settings
|
||||
from fs.memoryfs import MemoryFS
|
||||
from mock import Mock, patch
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from pytz import utc
|
||||
from xblock.runtime import DictKeyValueStore, KvsFieldData
|
||||
@@ -46,7 +47,7 @@ class DummySystem(ImportSystem): # lint-amnesty, pylint: disable=abstract-metho
|
||||
course_dir = "test_dir"
|
||||
error_tracker = Mock()
|
||||
|
||||
super(DummySystem, self).__init__( # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(
|
||||
xmlstore=xmlstore,
|
||||
course_id=course_id,
|
||||
course_dir=course_dir,
|
||||
@@ -62,7 +63,7 @@ def get_dummy_course(start, announcement=None, is_new=None, advertised_start=Non
|
||||
system = DummySystem(load_error_modules=True)
|
||||
|
||||
def to_attrb(n, v):
|
||||
return '' if v is None else '{0}="{1}"'.format(n, v).lower()
|
||||
return '' if v is None else f'{n}="{v}"'.lower()
|
||||
|
||||
is_new = to_attrb('is_new', is_new)
|
||||
announcement = to_attrb('announcement', announcement)
|
||||
@@ -100,7 +101,7 @@ class HasEndedMayCertifyTestCase(unittest.TestCase):
|
||||
"""Double check the semantics around when to finalize courses."""
|
||||
|
||||
def setUp(self):
|
||||
super(HasEndedMayCertifyTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
system = DummySystem(load_error_modules=True) # lint-amnesty, pylint: disable=unused-variable
|
||||
#sample_xml = """
|
||||
@@ -161,7 +162,7 @@ class IsNewCourseTestCase(unittest.TestCase):
|
||||
"""Make sure the property is_new works on courses"""
|
||||
|
||||
def setUp(self):
|
||||
super(IsNewCourseTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
# Needed for test_is_newish
|
||||
datetime_patcher = patch.object(
|
||||
@@ -210,13 +211,13 @@ class IsNewCourseTestCase(unittest.TestCase):
|
||||
for a, b, assertion in dates:
|
||||
a_score = get_dummy_course(start=a[0], announcement=a[1], advertised_start=a[2]).sorting_score
|
||||
b_score = get_dummy_course(start=b[0], announcement=b[1], advertised_start=b[2]).sorting_score
|
||||
print("Comparing %s to %s" % (a, b))
|
||||
print(f"Comparing {a} to {b}")
|
||||
assertion(a_score, b_score)
|
||||
|
||||
start_advertised_settings = [
|
||||
# start, advertised, result, is_still_default, date_time_result
|
||||
('2012-12-02T12:00', None, 'Dec 02, 2012', False, u'Dec 02, 2012 at 12:00 UTC'),
|
||||
('2012-12-02T12:00', '2011-11-01T12:00', 'Nov 01, 2011', False, u'Nov 01, 2011 at 12:00 UTC'),
|
||||
('2012-12-02T12:00', None, 'Dec 02, 2012', False, 'Dec 02, 2012 at 12:00 UTC'),
|
||||
('2012-12-02T12:00', '2011-11-01T12:00', 'Nov 01, 2011', False, 'Nov 01, 2011 at 12:00 UTC'),
|
||||
('2012-12-02T12:00', 'Spring 2012', 'Spring 2012', False, 'Spring 2012'),
|
||||
('2012-12-02T12:00', 'November, 2011', 'November, 2011', False, 'November, 2011'),
|
||||
(xmodule.course_module.CourseFields.start.default, None, 'TBD', True, 'TBD'),
|
||||
@@ -231,12 +232,12 @@ class IsNewCourseTestCase(unittest.TestCase):
|
||||
def test_display_organization(self):
|
||||
descriptor = get_dummy_course(start='2012-12-02T12:00', is_new=True)
|
||||
assert descriptor.location.org != descriptor.display_org_with_default
|
||||
assert descriptor.display_org_with_default == '{0}_display'.format(ORG)
|
||||
assert descriptor.display_org_with_default == f'{ORG}_display'
|
||||
|
||||
def test_display_coursenumber(self):
|
||||
descriptor = get_dummy_course(start='2012-12-02T12:00', is_new=True)
|
||||
assert descriptor.location.course != descriptor.display_number_with_default
|
||||
assert descriptor.display_number_with_default == '{0}_display'.format(COURSE)
|
||||
assert descriptor.display_number_with_default == f'{COURSE}_display'
|
||||
|
||||
def test_is_newish(self):
|
||||
descriptor = get_dummy_course(start='2012-12-02T12:00', is_new=True)
|
||||
@@ -274,7 +275,7 @@ class TeamsConfigurationTestCase(unittest.TestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TeamsConfigurationTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.course = get_dummy_course('2012-12-02T12:00')
|
||||
self.course.teams_configuration = TeamsConfig(None)
|
||||
self.count = itertools.count()
|
||||
@@ -290,9 +291,9 @@ class TeamsConfigurationTestCase(unittest.TestCase):
|
||||
def make_topic(self):
|
||||
""" Make a sample topic dictionary. """
|
||||
next_num = next(self.count)
|
||||
topic_id = "topic_id_{}".format(next_num)
|
||||
name = "Name {}".format(next_num)
|
||||
description = "Description {}".format(next_num)
|
||||
topic_id = f"topic_id_{next_num}"
|
||||
name = f"Name {next_num}"
|
||||
description = f"Description {next_num}"
|
||||
return {
|
||||
"name": name,
|
||||
"description": description,
|
||||
@@ -363,7 +364,7 @@ class SelfPacedTestCase(unittest.TestCase):
|
||||
"""Tests for self-paced courses."""
|
||||
|
||||
def setUp(self):
|
||||
super(SelfPacedTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.course = get_dummy_course('2012-12-02T12:00')
|
||||
|
||||
def test_default(self):
|
||||
@@ -427,7 +428,7 @@ class ProctoringProviderTestCase(unittest.TestCase):
|
||||
"""
|
||||
Initialize dummy testing course.
|
||||
"""
|
||||
super(ProctoringProviderTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.proctoring_provider = xmodule.course_module.ProctoringProvider()
|
||||
|
||||
def test_from_json_with_platform_default(self):
|
||||
@@ -447,7 +448,7 @@ class ProctoringProviderTestCase(unittest.TestCase):
|
||||
throws a ValueError with the correct error message.
|
||||
"""
|
||||
provider = 'invalid-provider'
|
||||
allowed_proctoring_providers = [u'mock', u'mock_proctoring_without_rules']
|
||||
allowed_proctoring_providers = ['mock', 'mock_proctoring_without_rules']
|
||||
|
||||
with pytest.raises(ValueError) as context_manager:
|
||||
self.proctoring_provider.from_json(provider)
|
||||
|
||||
@@ -11,9 +11,9 @@ submissions" setting is set to different values
|
||||
import datetime
|
||||
import textwrap
|
||||
import unittest
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
from mock import Mock
|
||||
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
|
||||
from pytz import UTC
|
||||
from xblock.field_data import DictFieldData
|
||||
@@ -26,7 +26,7 @@ from xmodule.capa_module import ProblemBlock
|
||||
from . import get_test_system
|
||||
|
||||
|
||||
class CapaFactoryWithDelay(object):
|
||||
class CapaFactoryWithDelay:
|
||||
"""
|
||||
Create problem modules class, specialized for delay_between_attempts
|
||||
test cases. This factory seems different enough from the one in
|
||||
@@ -88,7 +88,7 @@ class CapaFactoryWithDelay(object):
|
||||
Optional parameters here are cut down to what we actually use vs. the regular CapaFactory.
|
||||
"""
|
||||
location = BlockUsageLocator(CourseLocator('edX', 'capa_test', 'run', deprecated=True),
|
||||
'problem', 'SampleProblem{0}'.format(cls.next_num()), deprecated=True)
|
||||
'problem', f'SampleProblem{cls.next_num()}', deprecated=True)
|
||||
field_data = {'data': cls.sample_problem_xml}
|
||||
|
||||
if max_attempts is not None:
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
import logging
|
||||
import os
|
||||
import unittest
|
||||
from unittest.mock import Mock
|
||||
|
||||
from mock import Mock
|
||||
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
|
||||
from pkg_resources import resource_string
|
||||
from xblock.field_data import DictFieldData
|
||||
@@ -21,7 +21,7 @@ class TabsEditingDescriptorTestCase(unittest.TestCase):
|
||||
""" Testing TabsEditingDescriptor"""
|
||||
|
||||
def setUp(self):
|
||||
super(TabsEditingDescriptorTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
system = get_test_descriptor_system()
|
||||
system.render_template = Mock(return_value="<div>Test Template HTML</div>")
|
||||
self.tabs = [
|
||||
|
||||
@@ -4,9 +4,9 @@ Tests for ErrorBlock and NonStaffErrorBlock
|
||||
|
||||
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, Mock, patch
|
||||
|
||||
import pytest
|
||||
from mock import MagicMock, Mock, patch
|
||||
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
|
||||
from xblock.field_data import DictFieldData
|
||||
from xblock.fields import ScopeIds
|
||||
@@ -27,7 +27,7 @@ class SetupTestErrorBlock(unittest.TestCase):
|
||||
self.system = get_test_system()
|
||||
self.course_id = CourseLocator('org', 'course', 'run')
|
||||
self.location = self.course_id.make_usage_key('foo', 'bar')
|
||||
self.valid_xml = u"<problem>ABC \N{SNOWMAN}</problem>"
|
||||
self.valid_xml = "<problem>ABC \N{SNOWMAN}</problem>"
|
||||
self.error_msg = "Error"
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ class TestNonStaffErrorBlock(SetupTestErrorBlock):
|
||||
|
||||
class BrokenModule(XModule): # lint-amnesty, pylint: disable=abstract-method
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(BrokenModule, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(*args, **kwargs)
|
||||
raise Exception("This is a broken xmodule")
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests of XML export
|
||||
"""
|
||||
@@ -9,17 +8,16 @@ import unittest
|
||||
from datetime import datetime, timedelta, tzinfo
|
||||
from tempfile import mkdtemp
|
||||
from textwrap import dedent
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
import ddt
|
||||
import lxml.etree
|
||||
import mock
|
||||
import pytz
|
||||
import six
|
||||
from django.utils.translation import ugettext_lazy
|
||||
from fs.osfs import OSFS
|
||||
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
|
||||
from path import Path as path
|
||||
from six import text_type
|
||||
from xblock.core import XBlock
|
||||
from xblock.fields import Integer, Scope, String
|
||||
from xblock.test.tools import blocks_are_equivalent
|
||||
@@ -34,7 +32,7 @@ def strip_filenames(descriptor):
|
||||
"""
|
||||
Recursively strips 'filename' from all children's definitions.
|
||||
"""
|
||||
print("strip filename from {desc}".format(desc=text_type(descriptor.location)))
|
||||
print("strip filename from {desc}".format(desc=str(descriptor.location)))
|
||||
if descriptor._field_data.has(descriptor, 'filename'): # lint-amnesty, pylint: disable=protected-access
|
||||
descriptor._field_data.delete(descriptor, 'filename') # lint-amnesty, pylint: disable=protected-access
|
||||
|
||||
@@ -67,7 +65,7 @@ class RoundTripTestCase(unittest.TestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(RoundTripTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.maxDiff = None
|
||||
self.temp_dir = mkdtemp()
|
||||
self.addCleanup(shutil.rmtree, self.temp_dir)
|
||||
@@ -75,14 +73,14 @@ class RoundTripTestCase(unittest.TestCase):
|
||||
@mock.patch('xmodule.video_module.video_module.edxval_api', None)
|
||||
@mock.patch('xmodule.course_module.requests.get')
|
||||
@ddt.data(
|
||||
u"toy",
|
||||
u"simple",
|
||||
u"conditional_and_poll",
|
||||
u"conditional",
|
||||
u"self_assessment",
|
||||
u"test_exam_registration",
|
||||
u"word_cloud",
|
||||
u"pure_xblock",
|
||||
"toy",
|
||||
"simple",
|
||||
"conditional_and_poll",
|
||||
"conditional",
|
||||
"self_assessment",
|
||||
"test_exam_registration",
|
||||
"word_cloud",
|
||||
"pure_xblock",
|
||||
)
|
||||
@XBlock.register_temp_plugin(PureXBlock, 'pure')
|
||||
def test_export_roundtrip(self, course_dir, mock_get):
|
||||
@@ -95,7 +93,7 @@ class RoundTripTestCase(unittest.TestCase):
|
||||
""").strip()
|
||||
|
||||
root_dir = path(self.temp_dir)
|
||||
print("Copying test course to temp dir {0}".format(root_dir))
|
||||
print(f"Copying test course to temp dir {root_dir}")
|
||||
|
||||
data_dir = path(DATA_DIR)
|
||||
shutil.copytree(data_dir / course_dir, root_dir / course_dir)
|
||||
@@ -137,8 +135,7 @@ class RoundTripTestCase(unittest.TestCase):
|
||||
course_id = initial_course.id
|
||||
|
||||
print("Checking key equality")
|
||||
six.assertCountEqual(
|
||||
self,
|
||||
self.assertCountEqual(
|
||||
list(initial_import.modules[course_id].keys()),
|
||||
list(second_import.modules[course_id].keys())
|
||||
)
|
||||
@@ -156,7 +153,7 @@ class TestEdxJsonEncoder(unittest.TestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestEdxJsonEncoder, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
self.encoder = EdxJSONEncoder()
|
||||
|
||||
@@ -175,10 +172,10 @@ class TestEdxJsonEncoder(unittest.TestCase):
|
||||
|
||||
def test_encode_location(self):
|
||||
loc = BlockUsageLocator(CourseLocator('org', 'course', 'run'), 'category', 'name')
|
||||
assert text_type(loc) == self.encoder.default(loc)
|
||||
assert str(loc) == self.encoder.default(loc)
|
||||
|
||||
loc = BlockUsageLocator(CourseLocator('org', 'course', 'run', branch='version'), 'category', 'name')
|
||||
assert text_type(loc) == self.encoder.default(loc)
|
||||
assert str(loc) == self.encoder.default(loc)
|
||||
|
||||
def test_encode_naive_datetime(self):
|
||||
assert '2013-05-03T10:20:30.000100' == self.encoder.default(datetime(2013, 5, 3, 10, 20, 30, 100))
|
||||
@@ -204,7 +201,7 @@ class TestEdxJsonEncoder(unittest.TestCase):
|
||||
"""
|
||||
|
||||
# Initializing a lazy text object with Unicode
|
||||
unicode_text = u"Your 𝓟𝓵𝓪𝓽𝓯𝓸𝓻𝓶 Name Here"
|
||||
unicode_text = "Your 𝓟𝓵𝓪𝓽𝓯𝓸𝓻𝓶 Name Here"
|
||||
lazy_text = ugettext_lazy(unicode_text) # lint-amnesty, pylint: disable=translation-of-non-string
|
||||
|
||||
assert unicode_text == self.encoder.default(lazy_text)
|
||||
|
||||
@@ -78,7 +78,7 @@ class DateTest(unittest.TestCase): # lint-amnesty, pylint: disable=missing-clas
|
||||
"""
|
||||
now = datetime.datetime.now(UTC)
|
||||
delta = now - datetime.datetime.fromtimestamp(0, UTC)
|
||||
assert DateTest.date.from_json((delta.total_seconds() * 1000)) == now
|
||||
assert DateTest.date.from_json(delta.total_seconds() * 1000) == now
|
||||
yesterday = datetime.datetime.now(UTC) - datetime.timedelta(days=-1)
|
||||
assert DateTest.date.from_json(yesterday) == yesterday
|
||||
|
||||
|
||||
@@ -8,8 +8,6 @@ from datetime import datetime, timedelta
|
||||
import pytest
|
||||
import ddt
|
||||
from pytz import UTC
|
||||
import six
|
||||
from six import text_type
|
||||
|
||||
from lms.djangoapps.grades.scores import compute_percent
|
||||
from xmodule import graders
|
||||
@@ -68,7 +66,7 @@ class GraderTest(unittest.TestCase):
|
||||
'Midterm': {},
|
||||
}
|
||||
|
||||
class MockGrade(object):
|
||||
class MockGrade:
|
||||
"""
|
||||
Mock class for SubsectionGrade object.
|
||||
"""
|
||||
@@ -319,25 +317,25 @@ class GraderTest(unittest.TestCase):
|
||||
(
|
||||
# empty
|
||||
{},
|
||||
u"Configuration has no appropriate grader class."
|
||||
"Configuration has no appropriate grader class."
|
||||
),
|
||||
(
|
||||
# no min_count
|
||||
{'type': "Homework", 'drop_count': 0},
|
||||
u"Configuration has no appropriate grader class."
|
||||
"Configuration has no appropriate grader class."
|
||||
),
|
||||
(
|
||||
# no drop_count
|
||||
{'type': "Homework", 'min_count': 0},
|
||||
# pylint: disable=line-too-long
|
||||
u"__init__() takes at least 4 arguments (3 given)" if six.PY2 else u"__init__() missing 1 required positional argument: 'drop_count'"
|
||||
"__init__() missing 1 required positional argument: 'drop_count'"
|
||||
),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_grader_with_invalid_conf(self, invalid_conf, expected_error_message):
|
||||
with pytest.raises(ValueError) as error:
|
||||
graders.grader_from_conf([invalid_conf])
|
||||
assert expected_error_message in text_type(error.value)
|
||||
assert expected_error_message in str(error.value)
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@@ -347,7 +345,7 @@ class ShowCorrectnessTest(unittest.TestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(ShowCorrectnessTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
now = datetime.now(UTC)
|
||||
day_delta = timedelta(days=1)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# lint-amnesty, pylint: disable=missing-module-docstring
|
||||
|
||||
import unittest
|
||||
from unittest.mock import Mock
|
||||
|
||||
import ddt
|
||||
from django.test.utils import override_settings
|
||||
from mock import Mock
|
||||
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
|
||||
from xblock.field_data import DictFieldData
|
||||
from xblock.fields import ScopeIds
|
||||
@@ -312,7 +312,7 @@ class CourseInfoBlockTestCase(unittest.TestCase):
|
||||
],
|
||||
'hidden_updates': [],
|
||||
}
|
||||
template_name = "{0}/course_updates.html".format(info_module.TEMPLATE_DIR)
|
||||
template_name = f"{info_module.TEMPLATE_DIR}/course_updates.html"
|
||||
info_module.get_html()
|
||||
# Assertion to validate that render function is called with the expected context
|
||||
info_module.system.render_template.assert_called_once_with(
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
# lint-amnesty, pylint: disable=missing-module-docstring
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
|
||||
import datetime
|
||||
from tempfile import mkdtemp
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import ddt
|
||||
from django.test import TestCase
|
||||
from fs.osfs import OSFS
|
||||
from lxml import etree
|
||||
from mock import Mock, patch
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
|
||||
from pytz import UTC
|
||||
from six import text_type
|
||||
from xblock.core import XBlock
|
||||
from xblock.fields import Integer, Scope, String
|
||||
from xblock.runtime import DictKeyValueStore, KvsFieldData
|
||||
@@ -44,7 +42,7 @@ class DummySystem(ImportSystem): # lint-amnesty, pylint: disable=abstract-metho
|
||||
course_dir = "test_dir"
|
||||
error_tracker = Mock()
|
||||
|
||||
super(DummySystem, self).__init__( # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(
|
||||
xmlstore=xmlstore,
|
||||
course_id=course_id,
|
||||
course_dir=course_dir,
|
||||
@@ -68,7 +66,7 @@ class BaseCourseTestCase(TestCase):
|
||||
|
||||
def get_course(self, name):
|
||||
"""Get a test course by directory name. If there's more than one, error."""
|
||||
print("Importing {0}".format(name))
|
||||
print(f"Importing {name}")
|
||||
|
||||
modulestore = XMLModuleStore(
|
||||
DATA_DIR,
|
||||
@@ -126,7 +124,7 @@ class ImportTestCase(BaseCourseTestCase): # lint-amnesty, pylint: disable=missi
|
||||
'''Check that malformed xml loads as an ErrorBlock.'''
|
||||
|
||||
# Use an exotic character to also flush out Unicode issues.
|
||||
bad_xml = u'''<sequential display_name="oops\N{SNOWMAN}"><video url="hi"></sequential>'''
|
||||
bad_xml = '''<sequential display_name="oops\N{SNOWMAN}"><video url="hi"></sequential>'''
|
||||
system = self.get_system()
|
||||
|
||||
descriptor = system.process_xml(bad_xml)
|
||||
@@ -208,7 +206,7 @@ class ImportTestCase(BaseCourseTestCase): # lint-amnesty, pylint: disable=missi
|
||||
|
||||
# Now export and check things
|
||||
file_system = OSFS(mkdtemp())
|
||||
descriptor.runtime.export_fs = file_system.makedir(u'course', recreate=True)
|
||||
descriptor.runtime.export_fs = file_system.makedir('course', recreate=True)
|
||||
node = etree.Element('unknown')
|
||||
descriptor.add_xml_to_node(node)
|
||||
|
||||
@@ -220,7 +218,7 @@ class ImportTestCase(BaseCourseTestCase): # lint-amnesty, pylint: disable=missi
|
||||
assert node.attrib['org'] == ORG
|
||||
|
||||
# Does the course still have unicorns?
|
||||
with descriptor.runtime.export_fs.open(u'course/{course_run}.xml'.format(course_run=course_run)) as f:
|
||||
with descriptor.runtime.export_fs.open(f'course/{course_run}.xml') as f:
|
||||
course_xml = etree.fromstring(f.read())
|
||||
|
||||
assert course_xml.attrib['unicorn'] == unicorn_color
|
||||
@@ -234,7 +232,7 @@ class ImportTestCase(BaseCourseTestCase): # lint-amnesty, pylint: disable=missi
|
||||
|
||||
# Does the chapter tag now have a due attribute?
|
||||
# hardcoded path to child
|
||||
with descriptor.runtime.export_fs.open(u'chapter/ch.xml') as f:
|
||||
with descriptor.runtime.export_fs.open('chapter/ch.xml') as f:
|
||||
chapter_xml = etree.fromstring(f.read())
|
||||
assert chapter_xml.tag == 'chapter'
|
||||
assert 'due' not in chapter_xml.attrib
|
||||
@@ -426,11 +424,11 @@ class ImportTestCase(BaseCourseTestCase): # lint-amnesty, pylint: disable=missi
|
||||
"""]
|
||||
|
||||
for xml_str in yes:
|
||||
print("should be True for {0}".format(xml_str))
|
||||
print(f"should be True for {xml_str}")
|
||||
assert is_pointer_tag(etree.fromstring(xml_str))
|
||||
|
||||
for xml_str in no:
|
||||
print("should be False for {0}".format(xml_str))
|
||||
print(f"should be False for {xml_str}")
|
||||
assert not is_pointer_tag(etree.fromstring(xml_str))
|
||||
|
||||
def test_metadata_inherit(self):
|
||||
@@ -441,7 +439,7 @@ class ImportTestCase(BaseCourseTestCase): # lint-amnesty, pylint: disable=missi
|
||||
|
||||
def check_for_key(key, node, value):
|
||||
"recursive check for presence of key"
|
||||
print("Checking {0}".format(text_type(node.location)))
|
||||
print("Checking {}".format(str(node.location)))
|
||||
assert getattr(node, key) == value
|
||||
for c in node.get_children():
|
||||
check_for_key(key, c, value)
|
||||
@@ -561,7 +559,7 @@ class ImportTestCase(BaseCourseTestCase): # lint-amnesty, pylint: disable=missi
|
||||
in modulestore.get_course_errors(course.id)
|
||||
]
|
||||
|
||||
assert any((((expect in msg) or (expect in err)) for (msg, err) in errors))
|
||||
assert any(((expect in msg) or (expect in err)) for (msg, err) in errors)
|
||||
chapters = course.get_children()
|
||||
assert len(chapters) == 4
|
||||
|
||||
@@ -584,7 +582,7 @@ class ImportTestCase(BaseCourseTestCase): # lint-amnesty, pylint: disable=missi
|
||||
for i in (2, 3):
|
||||
video = sections[i]
|
||||
# Name should be 'video_{hash}'
|
||||
print("video {0} url_name: {1}".format(i, video.url_name))
|
||||
print(f"video {i} url_name: {video.url_name}")
|
||||
assert len(video.url_name) == (len('video_') + 12)
|
||||
|
||||
def test_poll_and_conditional_import(self):
|
||||
@@ -608,7 +606,7 @@ class ImportTestCase(BaseCourseTestCase): # lint-amnesty, pylint: disable=missi
|
||||
assert module.poll_answer == ''
|
||||
assert module.poll_answers == {}
|
||||
assert module.answers ==\
|
||||
[{'text': u'Yes', 'id': 'Yes'}, {'text': u'No', 'id': 'No'}, {'text': u"Don't know", 'id': 'Dont_know'}]
|
||||
[{'text': 'Yes', 'id': 'Yes'}, {'text': 'No', 'id': 'No'}, {'text': "Don't know", 'id': 'Dont_know'}]
|
||||
|
||||
def test_error_on_import(self):
|
||||
'''Check that when load_error_module is false, an exception is raised, rather than returning an ErrorBlock'''
|
||||
|
||||
@@ -4,8 +4,8 @@ Tests that check that we ignore the appropriate files when importing courses.
|
||||
|
||||
|
||||
import unittest
|
||||
from unittest.mock import Mock
|
||||
|
||||
from mock import Mock
|
||||
from opaque_keys.edx.locator import CourseLocator
|
||||
|
||||
from xmodule.modulestore.tests.utils import (
|
||||
@@ -26,7 +26,7 @@ class IgnoredFilesTestCase(unittest.TestCase):
|
||||
dict_list = [DOT_FILES_DICT, TILDA_FILES_DICT]
|
||||
|
||||
def setUp(self):
|
||||
super(IgnoredFilesTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
for dictionary in self.dict_list:
|
||||
self.addCleanup(remove_temp_files_from_list, list(dictionary.keys()), self.course_dir / "static")
|
||||
add_temp_files_from_dict(dictionary, self.course_dir / "static")
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Basic unit tests for LibraryContentBlock
|
||||
|
||||
Higher-level tests are in `cms/djangoapps/contentstore/tests/test_libraries.py`.
|
||||
"""
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
|
||||
import six
|
||||
from bson.objectid import ObjectId
|
||||
from fs.memoryfs import MemoryFS
|
||||
from lxml import etree
|
||||
from mock import Mock, patch
|
||||
from search.search_engine_base import SearchEngine
|
||||
from six.moves import range
|
||||
from web_fragments.fragment import Fragment
|
||||
from xblock.runtime import Runtime as VanillaRuntime
|
||||
|
||||
@@ -35,12 +31,12 @@ class LibraryContentTest(MixedSplitTestCase):
|
||||
Base class for tests of LibraryContentBlock (library_content_block.py)
|
||||
"""
|
||||
def setUp(self):
|
||||
super(LibraryContentTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
self.tools = LibraryToolsService(self.store, self.user_id)
|
||||
self.library = LibraryFactory.create(modulestore=self.store)
|
||||
self.lib_blocks = [
|
||||
self.make_block("html", self.library, data="Hello world from block {}".format(i))
|
||||
self.make_block("html", self.library, data=f"Hello world from block {i}")
|
||||
for i in range(1, 5)
|
||||
]
|
||||
self.course = CourseFactory.create(modulestore=self.store)
|
||||
@@ -51,7 +47,7 @@ class LibraryContentTest(MixedSplitTestCase):
|
||||
"library_content",
|
||||
self.vertical,
|
||||
max_count=1,
|
||||
source_library_id=six.text_type(self.library.location.library_key)
|
||||
source_library_id=str(self.library.location.library_key)
|
||||
)
|
||||
|
||||
def _bind_course_module(self, module):
|
||||
@@ -138,7 +134,7 @@ class TestLibraryContentExportImport(LibraryContentTest):
|
||||
assert imported_lc_block.children == lc_block.children
|
||||
|
||||
|
||||
class LibraryContentBlockTestMixin(object):
|
||||
class LibraryContentBlockTestMixin:
|
||||
"""
|
||||
Basic unit tests for LibraryContentBlock
|
||||
"""
|
||||
@@ -222,7 +218,7 @@ class LibraryContentBlockTestMixin(object):
|
||||
assert 'invalid' in result.summary.text
|
||||
|
||||
# When source_library_id is set but the block needs to be updated, the summary should say so:
|
||||
self.lc_block.source_library_id = six.text_type(self.library.location.library_key)
|
||||
self.lc_block.source_library_id = str(self.library.location.library_key)
|
||||
result = self.lc_block.validate()
|
||||
assert not result
|
||||
# Validation fails due to at least one warning/message
|
||||
@@ -385,7 +381,7 @@ class TestLibraryContentBlockWithSearchIndex(LibraryContentBlockTestMixin, Libra
|
||||
|
||||
def setUp(self):
|
||||
""" Sets up search engine mock """
|
||||
super(TestLibraryContentBlockWithSearchIndex, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
search_index_mock.search = Mock(side_effect=self._get_search_response)
|
||||
|
||||
|
||||
@@ -427,7 +423,7 @@ class TestLibraryContentAnalytics(LibraryContentTest):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestLibraryContentAnalytics, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.publisher = Mock()
|
||||
self.lc_block.refresh_children()
|
||||
self.lc_block = self.store.get_item(self.lc_block.location)
|
||||
@@ -441,8 +437,8 @@ class TestLibraryContentAnalytics(LibraryContentTest):
|
||||
assert self.publisher.called
|
||||
assert len(self.publisher.call_args[0]) == 3 # pylint:disable=unsubscriptable-object
|
||||
_, event_name, event_data = self.publisher.call_args[0] # pylint:disable=unsubscriptable-object
|
||||
assert event_name == 'edx.librarycontentblock.content.{}'.format(event_type)
|
||||
assert event_data['location'] == six.text_type(self.lc_block.location)
|
||||
assert event_name == f'edx.librarycontentblock.content.{event_type}'
|
||||
assert event_data['location'] == str(self.lc_block.location)
|
||||
return event_data
|
||||
|
||||
def test_assigned_event(self):
|
||||
@@ -455,13 +451,13 @@ class TestLibraryContentAnalytics(LibraryContentTest):
|
||||
assert isinstance(child_lib_version, ObjectId)
|
||||
event_data = self._assert_event_was_published("assigned")
|
||||
block_info = {
|
||||
"usage_key": six.text_type(child.location),
|
||||
"original_usage_key": six.text_type(child_lib_location),
|
||||
"original_usage_version": six.text_type(child_lib_version),
|
||||
"usage_key": str(child.location),
|
||||
"original_usage_key": str(child_lib_location),
|
||||
"original_usage_version": str(child_lib_version),
|
||||
"descendants": [],
|
||||
}
|
||||
assert event_data ==\
|
||||
{'location': six.text_type(self.lc_block.location),
|
||||
{'location': str(self.lc_block.location),
|
||||
'added': [block_info],
|
||||
'result': [block_info],
|
||||
'previous_count': 0, 'max_count': 1}
|
||||
@@ -473,7 +469,7 @@ class TestLibraryContentAnalytics(LibraryContentTest):
|
||||
assert len(children) == 2
|
||||
child, new_child = children if children[0].location == child.location else reversed(children)
|
||||
event_data = self._assert_event_was_published("assigned")
|
||||
assert event_data['added'][0]['usage_key'] == six.text_type(new_child.location)
|
||||
assert event_data['added'][0]['usage_key'] == str(new_child.location)
|
||||
assert len(event_data['result']) == 2
|
||||
assert event_data['previous_count'] == 1
|
||||
assert event_data['max_count'] == 2
|
||||
@@ -521,7 +517,7 @@ class TestLibraryContentAnalytics(LibraryContentTest):
|
||||
for block_list in (event_data["added"], event_data["result"]):
|
||||
assert len(block_list) == 1
|
||||
# main_vertical is the only root block added, and is the only result.
|
||||
assert block_list[0]['usage_key'] == six.text_type(course_usage_main_vertical)
|
||||
assert block_list[0]['usage_key'] == str(course_usage_main_vertical)
|
||||
|
||||
# Check that "descendants" is a flat, unordered list of all of main_vertical's descendants:
|
||||
descendants_expected = (
|
||||
@@ -531,10 +527,10 @@ class TestLibraryContentAnalytics(LibraryContentTest):
|
||||
)
|
||||
descendant_data_expected = {}
|
||||
for lib_key, course_usage_key in descendants_expected:
|
||||
descendant_data_expected[six.text_type(course_usage_key)] = {
|
||||
"usage_key": six.text_type(course_usage_key),
|
||||
"original_usage_key": six.text_type(lib_key),
|
||||
"original_usage_version": six.text_type(self.store.get_block_original_usage(course_usage_key)[1]),
|
||||
descendant_data_expected[str(course_usage_key)] = {
|
||||
"usage_key": str(course_usage_key),
|
||||
"original_usage_key": str(lib_key),
|
||||
"original_usage_version": str(self.store.get_block_original_usage(course_usage_key)[1]),
|
||||
}
|
||||
assert len(block_list[0]['descendants']) == len(descendant_data_expected)
|
||||
for descendant in block_list[0]["descendants"]:
|
||||
@@ -585,12 +581,12 @@ class TestLibraryContentAnalytics(LibraryContentTest):
|
||||
assert len(children) == 1
|
||||
event_data = self._assert_event_was_published("removed")
|
||||
assert event_data['removed'] ==\
|
||||
[{'usage_key': six.text_type(deleted_block_key),
|
||||
[{'usage_key': str(deleted_block_key),
|
||||
'original_usage_key': None,
|
||||
'original_usage_version': None,
|
||||
'descendants': []}]
|
||||
assert event_data['result'] ==\
|
||||
[{'usage_key': six.text_type(keep_block_key),
|
||||
'original_usage_key': six.text_type(keep_block_lib_usage_key),
|
||||
'original_usage_version': six.text_type(keep_block_lib_version), 'descendants': []}]
|
||||
[{'usage_key': str(keep_block_key),
|
||||
'original_usage_key': str(keep_block_lib_usage_key),
|
||||
'original_usage_version': str(keep_block_lib_version), 'descendants': []}]
|
||||
assert event_data['reason'] == 'invalid'
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Basic unit tests for LibraryRoot
|
||||
"""
|
||||
|
||||
|
||||
from mock import patch
|
||||
from six.moves import range
|
||||
from unittest.mock import patch
|
||||
from web_fragments.fragment import Fragment
|
||||
from xblock.runtime import Runtime as VanillaRuntime
|
||||
|
||||
@@ -34,7 +32,7 @@ class TestLibraryRoot(MixedSplitTestCase):
|
||||
We have to patch the runtime (module system) in order to be able to
|
||||
render blocks in our test environment.
|
||||
"""
|
||||
message = u"Hello world"
|
||||
message = "Hello world"
|
||||
library = LibraryFactory.create(modulestore=self.store)
|
||||
# Add one HTML block to the library:
|
||||
ItemFactory.create(
|
||||
|
||||
@@ -28,7 +28,7 @@ class LibrarySourcedBlockTestCase(ContentLibrariesRestApiTest):
|
||||
user_id=self.user.id,
|
||||
modulestore=self.store
|
||||
)
|
||||
self.submit_url = '/xblock/{0}/handler/submit_studio_edits'.format(self.source_block.scope_ids.usage_id)
|
||||
self.submit_url = f'/xblock/{self.source_block.scope_ids.usage_id}/handler/submit_studio_edits'
|
||||
|
||||
def test_block_views(self):
|
||||
# Create a blockstore content library
|
||||
@@ -52,7 +52,7 @@ class LibrarySourcedBlockTestCase(ContentLibrariesRestApiTest):
|
||||
# Create a blockstore content library
|
||||
library = self._create_library(slug="testlib2_preview", title="Test Library 2", description="Testing XBlocks")
|
||||
# Add content to the library
|
||||
blocks = [self._add_block_to_library(library["id"], "html", "block_{0}".format(i))["id"] for i in range(11)]
|
||||
blocks = [self._add_block_to_library(library["id"], "html", f"block_{i}")["id"] for i in range(11)]
|
||||
|
||||
# Import the html blocks from the library to the course
|
||||
post_data = {"values": {"source_block_ids": blocks}, "defaults": ["display_name"]}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Tests for LTI Xmodule LTIv2.0 functional logic."""
|
||||
|
||||
|
||||
import datetime
|
||||
import textwrap
|
||||
import unittest
|
||||
from unittest.mock import Mock
|
||||
|
||||
from mock import Mock
|
||||
from pytz import UTC
|
||||
from xblock.field_data import DictFieldData
|
||||
|
||||
@@ -20,7 +19,7 @@ class LTI20RESTResultServiceTest(unittest.TestCase):
|
||||
"""Logic tests for LTI module. LTI2.0 REST ResultService"""
|
||||
|
||||
def setUp(self):
|
||||
super(LTI20RESTResultServiceTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.system = get_test_system()
|
||||
self.environ = {'wsgi.url_scheme': 'http', 'REQUEST_METHOD': 'POST'}
|
||||
self.system.get_real_user = Mock()
|
||||
@@ -43,9 +42,9 @@ class LTI20RESTResultServiceTest(unittest.TestCase):
|
||||
self.xmodule.lti_id = "lti_id"
|
||||
|
||||
test_cases = ( # (before sanitize, after sanitize)
|
||||
(u"plaintext", u"plaintext"),
|
||||
(u"a <script>alert(3)</script>", u"a <script>alert(3)</script>"), # encodes scripts
|
||||
(u"<b>bold 包</b>", u"<b>bold 包</b>"), # unicode, and <b> tags pass through
|
||||
("plaintext", "plaintext"),
|
||||
("a <script>alert(3)</script>", "a <script>alert(3)</script>"), # encodes scripts
|
||||
("<b>bold 包</b>", "<b>bold 包</b>"), # unicode, and <b> tags pass through
|
||||
)
|
||||
for case in test_cases:
|
||||
self.xmodule.score_comment = case[0]
|
||||
@@ -56,7 +55,7 @@ class LTI20RESTResultServiceTest(unittest.TestCase):
|
||||
Input with bad content type
|
||||
"""
|
||||
with self.assertRaisesRegex(LTIError, "Content-Type must be"):
|
||||
request = Mock(headers={u'Content-Type': u'Non-existent'})
|
||||
request = Mock(headers={'Content-Type': 'Non-existent'})
|
||||
self.xmodule.verify_lti_2_0_result_rest_headers(request)
|
||||
|
||||
def test_lti20_rest_failed_oauth_body_verify(self):
|
||||
@@ -66,7 +65,7 @@ class LTI20RESTResultServiceTest(unittest.TestCase):
|
||||
err_msg = "OAuth body verification failed"
|
||||
self.xmodule.verify_oauth_body_sign = Mock(side_effect=LTIError(err_msg))
|
||||
with self.assertRaisesRegex(LTIError, err_msg):
|
||||
request = Mock(headers={u'Content-Type': u'application/vnd.ims.lis.v2.result+json'})
|
||||
request = Mock(headers={'Content-Type': 'application/vnd.ims.lis.v2.result+json'})
|
||||
self.xmodule.verify_lti_2_0_result_rest_headers(request)
|
||||
|
||||
def test_lti20_rest_good_headers(self):
|
||||
@@ -75,21 +74,21 @@ class LTI20RESTResultServiceTest(unittest.TestCase):
|
||||
"""
|
||||
self.xmodule.verify_oauth_body_sign = Mock(return_value=True)
|
||||
|
||||
request = Mock(headers={u'Content-Type': u'application/vnd.ims.lis.v2.result+json'})
|
||||
request = Mock(headers={'Content-Type': 'application/vnd.ims.lis.v2.result+json'})
|
||||
self.xmodule.verify_lti_2_0_result_rest_headers(request)
|
||||
# We just want the above call to complete without exceptions, and to have called verify_oauth_body_sign
|
||||
assert self.xmodule.verify_oauth_body_sign.called
|
||||
|
||||
BAD_DISPATCH_INPUTS = [
|
||||
None,
|
||||
u"",
|
||||
u"abcd"
|
||||
u"notuser/abcd"
|
||||
u"user/"
|
||||
u"user//"
|
||||
u"user/gbere/"
|
||||
u"user/gbere/xsdf"
|
||||
u"user/ಠ益ಠ" # not alphanumeric
|
||||
"",
|
||||
"abcd"
|
||||
"notuser/abcd"
|
||||
"user/"
|
||||
"user//"
|
||||
"user/gbere/"
|
||||
"user/gbere/xsdf"
|
||||
"user/ಠ益ಠ" # not alphanumeric
|
||||
]
|
||||
|
||||
def test_lti20_rest_bad_dispatch(self):
|
||||
@@ -102,8 +101,8 @@ class LTI20RESTResultServiceTest(unittest.TestCase):
|
||||
self.xmodule.parse_lti_2_0_handler_suffix(einput)
|
||||
|
||||
GOOD_DISPATCH_INPUTS = [
|
||||
(u"user/abcd3", u"abcd3"),
|
||||
(u"user/Äbcdè2", u"Äbcdè2"), # unicode, just to make sure
|
||||
("user/abcd3", "abcd3"),
|
||||
("user/Äbcdè2", "Äbcdè2"), # unicode, just to make sure
|
||||
]
|
||||
|
||||
def test_lti20_rest_good_dispatch(self):
|
||||
@@ -117,40 +116,40 @@ class LTI20RESTResultServiceTest(unittest.TestCase):
|
||||
BAD_JSON_INPUTS = [
|
||||
# (bad inputs, error message expected)
|
||||
([
|
||||
u"kk", # ValueError
|
||||
u"{{}", # ValueError
|
||||
u"{}}", # ValueError
|
||||
"kk", # ValueError
|
||||
"{{}", # ValueError
|
||||
"{}}", # ValueError
|
||||
3, # TypeError
|
||||
{}, # TypeError
|
||||
], u"Supplied JSON string in request body could not be decoded"),
|
||||
], "Supplied JSON string in request body could not be decoded"),
|
||||
([
|
||||
u"3", # valid json, not array or object
|
||||
u"[]", # valid json, array too small
|
||||
u"[3, {}]", # valid json, 1st element not an object
|
||||
], u"Supplied JSON string is a list that does not contain an object as the first element"),
|
||||
"3", # valid json, not array or object
|
||||
"[]", # valid json, array too small
|
||||
"[3, {}]", # valid json, 1st element not an object
|
||||
], "Supplied JSON string is a list that does not contain an object as the first element"),
|
||||
([
|
||||
u'{"@type": "NOTResult"}', # @type key must have value 'Result'
|
||||
], u"JSON object does not contain correct @type attribute"),
|
||||
'{"@type": "NOTResult"}', # @type key must have value 'Result'
|
||||
], "JSON object does not contain correct @type attribute"),
|
||||
([
|
||||
# @context missing
|
||||
u'{"@type": "Result", "resultScore": 0.1}',
|
||||
], u"JSON object does not contain required key"),
|
||||
'{"@type": "Result", "resultScore": 0.1}',
|
||||
], "JSON object does not contain required key"),
|
||||
([
|
||||
u'''
|
||||
'''
|
||||
{"@type": "Result",
|
||||
"@context": "http://purl.imsglobal.org/ctx/lis/v2/Result",
|
||||
"resultScore": 100}''' # score out of range
|
||||
], u"score value outside the permitted range of 0-1."),
|
||||
], "score value outside the permitted range of 0-1."),
|
||||
([
|
||||
u'''
|
||||
'''
|
||||
{"@type": "Result",
|
||||
"@context": "http://purl.imsglobal.org/ctx/lis/v2/Result",
|
||||
"resultScore": "1b"}''', # score ValueError
|
||||
u'''
|
||||
'''
|
||||
{"@type": "Result",
|
||||
"@context": "http://purl.imsglobal.org/ctx/lis/v2/Result",
|
||||
"resultScore": {}}''', # score TypeError
|
||||
], u"Could not convert resultScore to float"),
|
||||
], "Could not convert resultScore to float"),
|
||||
]
|
||||
|
||||
def test_lti20_bad_json(self):
|
||||
@@ -163,20 +162,20 @@ class LTI20RESTResultServiceTest(unittest.TestCase):
|
||||
self.xmodule.parse_lti_2_0_result_json(einput)
|
||||
|
||||
GOOD_JSON_INPUTS = [
|
||||
(u'''
|
||||
('''
|
||||
{"@type": "Result",
|
||||
"@context": "http://purl.imsglobal.org/ctx/lis/v2/Result",
|
||||
"resultScore": 0.1}''', u""), # no comment means we expect ""
|
||||
(u'''
|
||||
"resultScore": 0.1}''', ""), # no comment means we expect ""
|
||||
('''
|
||||
[{"@type": "Result",
|
||||
"@context": "http://purl.imsglobal.org/ctx/lis/v2/Result",
|
||||
"@id": "anon_id:abcdef0123456789",
|
||||
"resultScore": 0.1}]''', u""), # OK to have array of objects -- just take the first. @id is okay too
|
||||
(u'''
|
||||
"resultScore": 0.1}]''', ""), # OK to have array of objects -- just take the first. @id is okay too
|
||||
('''
|
||||
{"@type": "Result",
|
||||
"@context": "http://purl.imsglobal.org/ctx/lis/v2/Result",
|
||||
"resultScore": 0.1,
|
||||
"comment": "ಠ益ಠ"}''', u"ಠ益ಠ"), # unicode comment
|
||||
"comment": "ಠ益ಠ"}''', "ಠ益ಠ"), # unicode comment
|
||||
]
|
||||
|
||||
def test_lti20_good_json(self):
|
||||
@@ -188,7 +187,7 @@ class LTI20RESTResultServiceTest(unittest.TestCase):
|
||||
assert score == 0.1
|
||||
assert comment == expected_comment
|
||||
|
||||
GOOD_JSON_PUT = textwrap.dedent(u"""
|
||||
GOOD_JSON_PUT = textwrap.dedent("""
|
||||
{"@type": "Result",
|
||||
"@context": "http://purl.imsglobal.org/ctx/lis/v2/Result",
|
||||
"@id": "anon_id:abcdef0123456789",
|
||||
@@ -196,14 +195,14 @@ class LTI20RESTResultServiceTest(unittest.TestCase):
|
||||
"comment": "ಠ益ಠ"}
|
||||
""").encode('utf-8')
|
||||
|
||||
GOOD_JSON_PUT_LIKE_DELETE = textwrap.dedent(u"""
|
||||
GOOD_JSON_PUT_LIKE_DELETE = textwrap.dedent("""
|
||||
{"@type": "Result",
|
||||
"@context": "http://purl.imsglobal.org/ctx/lis/v2/Result",
|
||||
"@id": "anon_id:abcdef0123456789",
|
||||
"comment": "ಠ益ಠ"}
|
||||
""").encode('utf-8')
|
||||
|
||||
def get_signed_lti20_mock_request(self, body, method=u'PUT'):
|
||||
def get_signed_lti20_mock_request(self, body, method='PUT'):
|
||||
"""
|
||||
Example of signed from LTI 2.0 Provider. Signatures and hashes are example only and won't verify
|
||||
"""
|
||||
@@ -211,15 +210,15 @@ class LTI20RESTResultServiceTest(unittest.TestCase):
|
||||
mock_request.headers = {
|
||||
'Content-Type': 'application/vnd.ims.lis.v2.result+json',
|
||||
'Authorization': (
|
||||
u'OAuth oauth_nonce="135685044251684026041377608307", '
|
||||
u'oauth_timestamp="1234567890", oauth_version="1.0", '
|
||||
u'oauth_signature_method="HMAC-SHA1", '
|
||||
u'oauth_consumer_key="test_client_key", '
|
||||
u'oauth_signature="my_signature%3D", '
|
||||
u'oauth_body_hash="gz+PeJZuF2//n9hNUnDj2v5kN70="'
|
||||
'OAuth oauth_nonce="135685044251684026041377608307", '
|
||||
'oauth_timestamp="1234567890", oauth_version="1.0", '
|
||||
'oauth_signature_method="HMAC-SHA1", '
|
||||
'oauth_consumer_key="test_client_key", '
|
||||
'oauth_signature="my_signature%3D", '
|
||||
'oauth_body_hash="gz+PeJZuF2//n9hNUnDj2v5kN70="'
|
||||
)
|
||||
}
|
||||
mock_request.url = u'http://testurl'
|
||||
mock_request.url = 'http://testurl'
|
||||
mock_request.http_method = method
|
||||
mock_request.method = method
|
||||
mock_request.body = body
|
||||
@@ -234,7 +233,7 @@ class LTI20RESTResultServiceTest(unittest.TestCase):
|
||||
"""
|
||||
self.system.get_real_user = Mock(return_value=self.USER_STANDIN)
|
||||
self.xmodule.max_score = Mock(return_value=1.0)
|
||||
self.xmodule.get_client_key_secret = Mock(return_value=('test_client_key', u'test_client_secret'))
|
||||
self.xmodule.get_client_key_secret = Mock(return_value=('test_client_key', 'test_client_secret'))
|
||||
self.xmodule.verify_oauth_body_sign = Mock()
|
||||
|
||||
def test_lti20_put_like_delete_success(self):
|
||||
@@ -243,16 +242,16 @@ class LTI20RESTResultServiceTest(unittest.TestCase):
|
||||
"""
|
||||
self.setup_system_xmodule_mocks_for_lti20_request_test()
|
||||
SCORE = 0.55 # pylint: disable=invalid-name
|
||||
COMMENT = u"ಠ益ಠ" # pylint: disable=invalid-name
|
||||
COMMENT = "ಠ益ಠ" # pylint: disable=invalid-name
|
||||
self.xmodule.module_score = SCORE
|
||||
self.xmodule.score_comment = COMMENT
|
||||
mock_request = self.get_signed_lti20_mock_request(self.GOOD_JSON_PUT_LIKE_DELETE)
|
||||
# Now call the handler
|
||||
response = self.xmodule.lti_2_0_result_rest_handler(mock_request, u"user/abcd")
|
||||
response = self.xmodule.lti_2_0_result_rest_handler(mock_request, "user/abcd")
|
||||
# Now assert there's no score
|
||||
assert response.status_code == 200
|
||||
assert self.xmodule.module_score is None
|
||||
assert self.xmodule.score_comment == u''
|
||||
assert self.xmodule.score_comment == ''
|
||||
(_, evt_type, called_grade_obj), _ = self.system.publish.call_args # pylint: disable=unpacking-non-sequence
|
||||
assert called_grade_obj ==\
|
||||
{'user_id': self.USER_STANDIN.id, 'value': None, 'max_value': None, 'score_deleted': True}
|
||||
@@ -264,16 +263,16 @@ class LTI20RESTResultServiceTest(unittest.TestCase):
|
||||
"""
|
||||
self.setup_system_xmodule_mocks_for_lti20_request_test()
|
||||
SCORE = 0.55 # pylint: disable=invalid-name
|
||||
COMMENT = u"ಠ益ಠ" # pylint: disable=invalid-name
|
||||
COMMENT = "ಠ益ಠ" # pylint: disable=invalid-name
|
||||
self.xmodule.module_score = SCORE
|
||||
self.xmodule.score_comment = COMMENT
|
||||
mock_request = self.get_signed_lti20_mock_request(b"", method=u'DELETE')
|
||||
mock_request = self.get_signed_lti20_mock_request(b"", method='DELETE')
|
||||
# Now call the handler
|
||||
response = self.xmodule.lti_2_0_result_rest_handler(mock_request, u"user/abcd")
|
||||
response = self.xmodule.lti_2_0_result_rest_handler(mock_request, "user/abcd")
|
||||
# Now assert there's no score
|
||||
assert response.status_code == 200
|
||||
assert self.xmodule.module_score is None
|
||||
assert self.xmodule.score_comment == u''
|
||||
assert self.xmodule.score_comment == ''
|
||||
(_, evt_type, called_grade_obj), _ = self.system.publish.call_args # pylint: disable=unpacking-non-sequence
|
||||
assert called_grade_obj ==\
|
||||
{'user_id': self.USER_STANDIN.id, 'value': None, 'max_value': None, 'score_deleted': True}
|
||||
@@ -286,11 +285,11 @@ class LTI20RESTResultServiceTest(unittest.TestCase):
|
||||
self.setup_system_xmodule_mocks_for_lti20_request_test()
|
||||
mock_request = self.get_signed_lti20_mock_request(self.GOOD_JSON_PUT)
|
||||
# Now call the handler
|
||||
response = self.xmodule.lti_2_0_result_rest_handler(mock_request, u"user/abcd")
|
||||
response = self.xmodule.lti_2_0_result_rest_handler(mock_request, "user/abcd")
|
||||
# Now assert
|
||||
assert response.status_code == 200
|
||||
assert self.xmodule.module_score == 0.1
|
||||
assert self.xmodule.score_comment == u'ಠ益ಠ'
|
||||
assert self.xmodule.score_comment == 'ಠ益ಠ'
|
||||
(_, evt_type, called_grade_obj), _ = self.system.publish.call_args # pylint: disable=unpacking-non-sequence
|
||||
assert evt_type == 'grade'
|
||||
assert called_grade_obj ==\
|
||||
@@ -301,9 +300,9 @@ class LTI20RESTResultServiceTest(unittest.TestCase):
|
||||
The happy path for LTI 2.0 GET when there's no score
|
||||
"""
|
||||
self.setup_system_xmodule_mocks_for_lti20_request_test()
|
||||
mock_request = self.get_signed_lti20_mock_request(b"", method=u'GET')
|
||||
mock_request = self.get_signed_lti20_mock_request(b"", method='GET')
|
||||
# Now call the handler
|
||||
response = self.xmodule.lti_2_0_result_rest_handler(mock_request, u"user/abcd")
|
||||
response = self.xmodule.lti_2_0_result_rest_handler(mock_request, "user/abcd")
|
||||
# Now assert
|
||||
assert response.status_code == 200
|
||||
assert response.json == {'@context': 'http://purl.imsglobal.org/ctx/lis/v2/Result', '@type': 'Result'}
|
||||
@@ -314,12 +313,12 @@ class LTI20RESTResultServiceTest(unittest.TestCase):
|
||||
"""
|
||||
self.setup_system_xmodule_mocks_for_lti20_request_test()
|
||||
SCORE = 0.55 # pylint: disable=invalid-name
|
||||
COMMENT = u"ಠ益ಠ" # pylint: disable=invalid-name
|
||||
COMMENT = "ಠ益ಠ" # pylint: disable=invalid-name
|
||||
self.xmodule.module_score = SCORE
|
||||
self.xmodule.score_comment = COMMENT
|
||||
mock_request = self.get_signed_lti20_mock_request(b"", method=u'GET')
|
||||
mock_request = self.get_signed_lti20_mock_request(b"", method='GET')
|
||||
# Now call the handler
|
||||
response = self.xmodule.lti_2_0_result_rest_handler(mock_request, u"user/abcd")
|
||||
response = self.xmodule.lti_2_0_result_rest_handler(mock_request, "user/abcd")
|
||||
# Now assert
|
||||
assert response.status_code == 200
|
||||
assert response.json ==\
|
||||
@@ -336,7 +335,7 @@ class LTI20RESTResultServiceTest(unittest.TestCase):
|
||||
mock_request = self.get_signed_lti20_mock_request(self.GOOD_JSON_PUT)
|
||||
for bad_method in self.UNSUPPORTED_HTTP_METHODS:
|
||||
mock_request.method = bad_method
|
||||
response = self.xmodule.lti_2_0_result_rest_handler(mock_request, u"user/abcd")
|
||||
response = self.xmodule.lti_2_0_result_rest_handler(mock_request, "user/abcd")
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_lti20_request_handler_bad_headers(self):
|
||||
@@ -346,7 +345,7 @@ class LTI20RESTResultServiceTest(unittest.TestCase):
|
||||
self.setup_system_xmodule_mocks_for_lti20_request_test()
|
||||
self.xmodule.verify_lti_2_0_result_rest_headers = Mock(side_effect=LTIError())
|
||||
mock_request = self.get_signed_lti20_mock_request(self.GOOD_JSON_PUT)
|
||||
response = self.xmodule.lti_2_0_result_rest_handler(mock_request, u"user/abcd")
|
||||
response = self.xmodule.lti_2_0_result_rest_handler(mock_request, "user/abcd")
|
||||
assert response.status_code == 401
|
||||
|
||||
def test_lti20_request_handler_bad_dispatch_user(self):
|
||||
@@ -365,7 +364,7 @@ class LTI20RESTResultServiceTest(unittest.TestCase):
|
||||
self.setup_system_xmodule_mocks_for_lti20_request_test()
|
||||
self.xmodule.parse_lti_2_0_result_json = Mock(side_effect=LTIError())
|
||||
mock_request = self.get_signed_lti20_mock_request(self.GOOD_JSON_PUT)
|
||||
response = self.xmodule.lti_2_0_result_rest_handler(mock_request, u"user/abcd")
|
||||
response = self.xmodule.lti_2_0_result_rest_handler(mock_request, "user/abcd")
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_lti20_request_handler_bad_user(self):
|
||||
@@ -375,7 +374,7 @@ class LTI20RESTResultServiceTest(unittest.TestCase):
|
||||
self.setup_system_xmodule_mocks_for_lti20_request_test()
|
||||
self.system.get_real_user = Mock(return_value=None)
|
||||
mock_request = self.get_signed_lti20_mock_request(self.GOOD_JSON_PUT)
|
||||
response = self.xmodule.lti_2_0_result_rest_handler(mock_request, u"user/abcd")
|
||||
response = self.xmodule.lti_2_0_result_rest_handler(mock_request, "user/abcd")
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_lti20_request_handler_grade_past_due(self):
|
||||
@@ -386,5 +385,5 @@ class LTI20RESTResultServiceTest(unittest.TestCase):
|
||||
self.xmodule.due = datetime.datetime.now(UTC)
|
||||
self.xmodule.accept_grades_past_due = False
|
||||
mock_request = self.get_signed_lti20_mock_request(self.GOOD_JSON_PUT)
|
||||
response = self.xmodule.lti_2_0_result_rest_handler(mock_request, u"user/abcd")
|
||||
response = self.xmodule.lti_2_0_result_rest_handler(mock_request, "user/abcd")
|
||||
assert response.status_code == 404
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Test for LTI Xmodule functional logic."""
|
||||
|
||||
|
||||
@@ -6,14 +5,13 @@ import datetime
|
||||
import textwrap
|
||||
import unittest
|
||||
from copy import copy
|
||||
from unittest.mock import Mock, PropertyMock, patch
|
||||
from urllib import parse
|
||||
|
||||
import pytest
|
||||
import six
|
||||
from lxml import etree
|
||||
from mock import Mock, PropertyMock, patch
|
||||
from opaque_keys.edx.locator import BlockUsageLocator
|
||||
from pytz import UTC
|
||||
from six import text_type
|
||||
from webob.request import Request
|
||||
from xblock.field_data import DictFieldData
|
||||
from xblock.fields import ScopeIds
|
||||
@@ -31,7 +29,7 @@ class LTIBlockTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.environ = {'wsgi.url_scheme': 'http', 'REQUEST_METHOD': 'POST'}
|
||||
self.request_body_xml_template = textwrap.dedent(u"""
|
||||
self.request_body_xml_template = textwrap.dedent("""
|
||||
<?xml version = "1.0" encoding = "UTF-8"?>
|
||||
<imsx_POXEnvelopeRequest xmlns = "{namespace}">
|
||||
<imsx_POXHeader>
|
||||
@@ -69,11 +67,11 @@ class LTIBlockTest(unittest.TestCase):
|
||||
ScopeIds(None, None, None, BlockUsageLocator(self.system.course_id, 'lti', 'name'))
|
||||
)
|
||||
self.lti_id = self.xmodule.lti_id
|
||||
self.unquoted_resource_link_id = u'{}-i4x-2-3-lti-31de800015cf4afb973356dbe81496df'.format(
|
||||
self.unquoted_resource_link_id = '{}-i4x-2-3-lti-31de800015cf4afb973356dbe81496df'.format(
|
||||
self.xmodule.runtime.hostname
|
||||
)
|
||||
|
||||
sourced_id = u':'.join(six.moves.urllib.parse.quote(i) for i in (self.lti_id, self.unquoted_resource_link_id, self.user_id)) # lint-amnesty, pylint: disable=line-too-long
|
||||
sourced_id = ':'.join(parse.quote(i) for i in (self.lti_id, self.unquoted_resource_link_id, self.user_id)) # lint-amnesty, pylint: disable=line-too-long
|
||||
|
||||
self.defaults = {
|
||||
'namespace': "http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0",
|
||||
@@ -121,7 +119,7 @@ class LTIBlockTest(unittest.TestCase):
|
||||
|
||||
@patch(
|
||||
'xmodule.lti_module.LTIBlock.get_client_key_secret',
|
||||
return_value=('test_client_key', u'test_client_secret')
|
||||
return_value=('test_client_key', 'test_client_secret')
|
||||
)
|
||||
def test_authorization_header_not_present(self, _get_key_secret):
|
||||
"""
|
||||
@@ -145,7 +143,7 @@ class LTIBlockTest(unittest.TestCase):
|
||||
|
||||
@patch(
|
||||
'xmodule.lti_module.LTIBlock.get_client_key_secret',
|
||||
return_value=('test_client_key', u'test_client_secret')
|
||||
return_value=('test_client_key', 'test_client_secret')
|
||||
)
|
||||
def test_authorization_header_empty(self, _get_key_secret):
|
||||
"""
|
||||
@@ -234,14 +232,11 @@ class LTIBlockTest(unittest.TestCase):
|
||||
request.body = self.get_request_body(params={'grade': '0,5'})
|
||||
response = self.xmodule.grade_handler(request, '')
|
||||
real_response = self.get_response_values(response)
|
||||
if six.PY2:
|
||||
msg = u'invalid literal for float(): 0,5'
|
||||
else:
|
||||
msg = u"could not convert string to float: '0,5'"
|
||||
msg = "could not convert string to float: '0,5'"
|
||||
expected_response = {
|
||||
'action': None,
|
||||
'code_major': 'failure',
|
||||
'description': u'Request body XML parsing error: {}'.format(msg),
|
||||
'description': f'Request body XML parsing error: {msg}',
|
||||
'messageIdentifier': 'unknown',
|
||||
}
|
||||
assert response.status_code == 200
|
||||
@@ -292,7 +287,7 @@ class LTIBlockTest(unittest.TestCase):
|
||||
assert self.xmodule.module_score == float(self.defaults['grade'])
|
||||
|
||||
def test_user_id(self):
|
||||
expected_user_id = text_type(six.moves.urllib.parse.quote(self.xmodule.runtime.anonymous_student_id))
|
||||
expected_user_id = str(parse.quote(self.xmodule.runtime.anonymous_student_id))
|
||||
real_user_id = self.xmodule.get_user_id()
|
||||
assert real_user_id == expected_user_id
|
||||
|
||||
@@ -311,13 +306,13 @@ class LTIBlockTest(unittest.TestCase):
|
||||
def test_resource_link_id(self):
|
||||
with patch('xmodule.lti_module.LTIBlock.location', new_callable=PropertyMock):
|
||||
self.xmodule.location.html_id = lambda: 'i4x-2-3-lti-31de800015cf4afb973356dbe81496df'
|
||||
expected_resource_link_id = text_type(six.moves.urllib.parse.quote(self.unquoted_resource_link_id))
|
||||
expected_resource_link_id = str(parse.quote(self.unquoted_resource_link_id))
|
||||
real_resource_link_id = self.xmodule.get_resource_link_id()
|
||||
assert real_resource_link_id == expected_resource_link_id
|
||||
|
||||
def test_lis_result_sourcedid(self):
|
||||
expected_sourced_id = u':'.join(six.moves.urllib.parse.quote(i) for i in (
|
||||
text_type(self.system.course_id),
|
||||
expected_sourced_id = ':'.join(parse.quote(i) for i in (
|
||||
str(self.system.course_id),
|
||||
self.xmodule.get_resource_link_id(),
|
||||
self.user_id
|
||||
))
|
||||
@@ -377,7 +372,7 @@ class LTIBlockTest(unittest.TestCase):
|
||||
@patch('xmodule.lti_module.signature.verify_hmac_sha1', Mock(return_value=True))
|
||||
@patch(
|
||||
'xmodule.lti_module.LTIBlock.get_client_key_secret',
|
||||
Mock(return_value=('test_client_key', u'test_client_secret'))
|
||||
Mock(return_value=('test_client_key', 'test_client_secret'))
|
||||
)
|
||||
def test_successful_verify_oauth_body_sign(self):
|
||||
"""
|
||||
@@ -385,9 +380,9 @@ class LTIBlockTest(unittest.TestCase):
|
||||
"""
|
||||
self.xmodule.verify_oauth_body_sign(self.get_signed_grade_mock_request())
|
||||
|
||||
@patch('xmodule.lti_module.LTIBlock.get_outcome_service_url', Mock(return_value=u'https://testurl/'))
|
||||
@patch('xmodule.lti_module.LTIBlock.get_outcome_service_url', Mock(return_value='https://testurl/'))
|
||||
@patch('xmodule.lti_module.LTIBlock.get_client_key_secret',
|
||||
Mock(return_value=(u'__consumer_key__', u'__lti_secret__')))
|
||||
Mock(return_value=('__consumer_key__', '__lti_secret__')))
|
||||
def test_failed_verify_oauth_body_sign_proxy_mangle_url(self):
|
||||
"""
|
||||
Oauth signing verify fail.
|
||||
@@ -406,30 +401,30 @@ class LTIBlockTest(unittest.TestCase):
|
||||
"""
|
||||
mock_request = Mock()
|
||||
mock_request.headers = {
|
||||
u'X-Requested-With': u'XMLHttpRequest',
|
||||
u'Content-Type': u'application/x-www-form-urlencoded',
|
||||
u'Authorization': (
|
||||
u'OAuth realm="https://testurl/", oauth_body_hash="wwzA3s8gScKD1VpJ7jMt9b%2BMj9Q%3D",'
|
||||
u'oauth_nonce="18821463", oauth_timestamp="1409321145", '
|
||||
u'oauth_consumer_key="__consumer_key__", oauth_signature_method="HMAC-SHA1", '
|
||||
u'oauth_version="1.0", oauth_signature="fHsE1hhIz76/msUoMR3Lyb7Aou4%3D"'
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization': (
|
||||
'OAuth realm="https://testurl/", oauth_body_hash="wwzA3s8gScKD1VpJ7jMt9b%2BMj9Q%3D",'
|
||||
'oauth_nonce="18821463", oauth_timestamp="1409321145", '
|
||||
'oauth_consumer_key="__consumer_key__", oauth_signature_method="HMAC-SHA1", '
|
||||
'oauth_version="1.0", oauth_signature="fHsE1hhIz76/msUoMR3Lyb7Aou4%3D"'
|
||||
)
|
||||
}
|
||||
mock_request.url = u'https://testurl'
|
||||
mock_request.http_method = u'POST'
|
||||
mock_request.url = 'https://testurl'
|
||||
mock_request.http_method = 'POST'
|
||||
mock_request.method = mock_request.http_method
|
||||
|
||||
mock_request.body = (
|
||||
u'<?xml version=\'1.0\' encoding=\'utf-8\'?>\n'
|
||||
u'<imsx_POXEnvelopeRequest xmlns="http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0">'
|
||||
u'<imsx_POXHeader><imsx_POXRequestHeaderInfo><imsx_version>V1.0</imsx_version>'
|
||||
u'<imsx_messageIdentifier>edX_fix</imsx_messageIdentifier></imsx_POXRequestHeaderInfo>'
|
||||
u'</imsx_POXHeader><imsx_POXBody><replaceResultRequest><resultRecord><sourcedGUID>'
|
||||
u'<sourcedId>MITxLTI/MITxLTI/201x:localhost%3A8000-i4x-MITxLTI-MITxLTI-lti-3751833a214a4f66a0d18f63234207f2'
|
||||
u':363979ef768ca171b50f9d1bfb322131</sourcedId>'
|
||||
u'</sourcedGUID><result><resultScore><language>en</language><textString>0.32</textString></resultScore>'
|
||||
u'</result></resultRecord></replaceResultRequest></imsx_POXBody></imsx_POXEnvelopeRequest>'
|
||||
).encode('utf-8')
|
||||
b'<?xml version=\'1.0\' encoding=\'utf-8\'?>\n'
|
||||
b'<imsx_POXEnvelopeRequest xmlns="http://www.imsglobal.org/services/ltiv1p1/xsd/imsoms_v1p0">'
|
||||
b'<imsx_POXHeader><imsx_POXRequestHeaderInfo><imsx_version>V1.0</imsx_version>'
|
||||
b'<imsx_messageIdentifier>edX_fix</imsx_messageIdentifier></imsx_POXRequestHeaderInfo>'
|
||||
b'</imsx_POXHeader><imsx_POXBody><replaceResultRequest><resultRecord><sourcedGUID>'
|
||||
b'<sourcedId>MITxLTI/MITxLTI/201x:localhost%3A8000-i4x-MITxLTI-MITxLTI-lti-3751833a214a4f66a0d18f63234207f2'
|
||||
b':363979ef768ca171b50f9d1bfb322131</sourcedId>'
|
||||
b'</sourcedGUID><result><resultScore><language>en</language><textString>0.32</textString></resultScore>'
|
||||
b'</result></resultRecord></replaceResultRequest></imsx_POXBody></imsx_POXEnvelopeRequest>'
|
||||
)
|
||||
|
||||
return mock_request
|
||||
|
||||
@@ -459,7 +454,7 @@ class LTIBlockTest(unittest.TestCase):
|
||||
@patch('xmodule.lti_module.signature.verify_hmac_sha1', Mock(return_value=False))
|
||||
@patch(
|
||||
'xmodule.lti_module.LTIBlock.get_client_key_secret',
|
||||
Mock(return_value=('test_client_key', u'test_client_secret'))
|
||||
Mock(return_value=('test_client_key', 'test_client_secret'))
|
||||
)
|
||||
def test_failed_verify_oauth_body_sign(self):
|
||||
"""
|
||||
@@ -480,15 +475,15 @@ class LTIBlockTest(unittest.TestCase):
|
||||
mock_request.headers = {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Authorization': u'OAuth oauth_nonce="135685044251684026041377608307", \
|
||||
'Authorization': 'OAuth oauth_nonce="135685044251684026041377608307", \
|
||||
oauth_timestamp="1234567890", oauth_version="1.0", \
|
||||
oauth_signature_method="HMAC-SHA1", \
|
||||
oauth_consumer_key="test_client_key", \
|
||||
oauth_signature="my_signature%3D", \
|
||||
oauth_body_hash="JEpIArlNCeV4ceXxric8gJQCnBw="'
|
||||
}
|
||||
mock_request.url = u'http://testurl'
|
||||
mock_request.http_method = u'POST'
|
||||
mock_request.url = 'http://testurl'
|
||||
mock_request.http_method = 'POST'
|
||||
|
||||
params = {}
|
||||
if not namespace_lti_v1p1:
|
||||
@@ -508,7 +503,7 @@ class LTIBlockTest(unittest.TestCase):
|
||||
self.xmodule.oauth_params = Mock()
|
||||
self.xmodule.get_input_fields()
|
||||
self.xmodule.oauth_params.assert_called_with(
|
||||
{u'custom_test_custom_params': u'test_custom_param_value'},
|
||||
{'custom_test_custom_params': 'test_custom_param_value'},
|
||||
'test_client_key', 'test_client_secret'
|
||||
)
|
||||
|
||||
@@ -537,4 +532,4 @@ class LTIBlockTest(unittest.TestCase):
|
||||
"""
|
||||
Tests that LTI parameter context_id is equal to course_id.
|
||||
"""
|
||||
assert text_type(self.system.course_id) == self.xmodule.context_id
|
||||
assert str(self.system.course_id) == self.xmodule.context_id
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
|
||||
from unittest import TestCase
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
from mock import Mock
|
||||
|
||||
from xmodule.mako_module import MakoModuleDescriptor
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Test for Poll Xmodule functional logic."""
|
||||
|
||||
import json
|
||||
import unittest
|
||||
|
||||
from mock import Mock
|
||||
from unittest.mock import Mock
|
||||
|
||||
from xblock.field_data import DictFieldData
|
||||
from xblock.fields import ScopeIds
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
|
||||
import unittest
|
||||
from unittest.mock import Mock
|
||||
|
||||
from mock import Mock
|
||||
from xblock.field_data import DictFieldData
|
||||
|
||||
from xmodule import x_module
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"""
|
||||
Test cases covering workflows and behaviors for the Randomize XModule
|
||||
"""
|
||||
from unittest.mock import Mock
|
||||
|
||||
from fs.memoryfs import MemoryFS
|
||||
from lxml import etree
|
||||
from mock import Mock
|
||||
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from xmodule.modulestore.tests.utils import MixedSplitTestCase
|
||||
@@ -33,7 +33,7 @@ class RandomizeBlockTest(MixedSplitTestCase):
|
||||
display_name="Hello Randomize",
|
||||
)
|
||||
self.child_blocks = [
|
||||
self.make_block("html", self.randomize_block, display_name="Hello HTML {}".format(i))
|
||||
self.make_block("html", self.randomize_block, display_name=f"Hello HTML {i}")
|
||||
for i in range(1, 4)
|
||||
]
|
||||
|
||||
|
||||
@@ -14,12 +14,12 @@ class ResourceTemplatesTests(unittest.TestCase):
|
||||
"""
|
||||
|
||||
def test_templates(self):
|
||||
expected = set([
|
||||
expected = {
|
||||
'latex_html.yaml',
|
||||
'zooming_image.yaml',
|
||||
'announcement.yaml',
|
||||
'anon_user_id.yaml'])
|
||||
got = set((t['template_id'] for t in TestClass.templates()))
|
||||
'anon_user_id.yaml'}
|
||||
got = {t['template_id'] for t in TestClass.templates()}
|
||||
assert expected == got
|
||||
|
||||
def test_templates_no_suchdir(self):
|
||||
|
||||
@@ -7,14 +7,12 @@ Tests for sequence module.
|
||||
import ast
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import ddt
|
||||
import six
|
||||
from django.test.utils import override_settings
|
||||
from django.utils.timezone import now
|
||||
from freezegun import freeze_time
|
||||
from mock import Mock, patch
|
||||
from six.moves import range
|
||||
from web_fragments.fragment import Fragment
|
||||
|
||||
from edx_toggles.toggles.testutils import override_waffle_flag
|
||||
@@ -136,7 +134,7 @@ class SequenceBlockTestCase(XModuleXmlImportTest):
|
||||
"""
|
||||
Verifies that the rendered view contains the expected position.
|
||||
"""
|
||||
assert "'position': {}".format(expected_position) in rendered_html
|
||||
assert f"'position': {expected_position}" in rendered_html
|
||||
|
||||
def test_student_view_init(self):
|
||||
module_system = get_test_system()
|
||||
@@ -158,7 +156,7 @@ class SequenceBlockTestCase(XModuleXmlImportTest):
|
||||
view=view
|
||||
)
|
||||
self._assert_view_at_position(html, expected_position=1)
|
||||
assert six.text_type(self.sequence_3_1.location) in html
|
||||
assert str(self.sequence_3_1.location) in html
|
||||
assert "'gated': False" in html
|
||||
assert "'next_url': 'NextSequential'" in html
|
||||
assert "'prev_url': 'PrevSequential'" in html
|
||||
@@ -236,7 +234,7 @@ class SequenceBlockTestCase(XModuleXmlImportTest):
|
||||
def test_tooltip(self):
|
||||
html = self._get_rendered_view(self.sequence_3_1, requested_child=None)
|
||||
for child in self.sequence_3_1.children:
|
||||
assert "'page_title': '{}'".format(child.block_id) in html
|
||||
assert f"'page_title': '{child.block_id}'" in html
|
||||
|
||||
def test_hidden_content_before_due(self):
|
||||
html = self._get_rendered_view(self.sequence_4_1)
|
||||
@@ -291,7 +289,7 @@ class SequenceBlockTestCase(XModuleXmlImportTest):
|
||||
assert html['gated_content']['gated']
|
||||
assert 'PrereqUrl' == html['gated_content']['prereq_url']
|
||||
assert 'PrereqSectionName' == html['gated_content']['prereq_section_name']
|
||||
assert six.text_type(sequence.display_name) in html['gated_content']['gated_section_name']
|
||||
assert str(sequence.display_name) in html['gated_content']['gated_section_name']
|
||||
assert 'NextSequential' == html['next_url']
|
||||
assert 'PrevSequential' == html['prev_url']
|
||||
|
||||
@@ -303,7 +301,7 @@ class SequenceBlockTestCase(XModuleXmlImportTest):
|
||||
html = self.get_context_dict_from_string(html)
|
||||
assert 'This section is a prerequisite. You must complete this section in order to unlock additional content.' == html['banner_text']
|
||||
assert not html['gated_content']['gated']
|
||||
assert six.text_type(sequence.location) == html['item_id']
|
||||
assert str(sequence.location) == html['item_id']
|
||||
assert html['gated_content']['prereq_url'] is None
|
||||
assert html['gated_content']['prereq_section_name'] is None
|
||||
assert 'NextSequential' == html['next_url']
|
||||
@@ -316,7 +314,7 @@ class SequenceBlockTestCase(XModuleXmlImportTest):
|
||||
assert 'seq_module.html' in html
|
||||
assert "'banner_text': None" in html
|
||||
assert "'gated': False" in html
|
||||
assert six.text_type(sequence.location) in html
|
||||
assert str(sequence.location) in html
|
||||
assert "'prereq_url': None" in html
|
||||
assert "'prereq_section_name': None" in html
|
||||
assert "'next_url': 'NextSequential'" in html
|
||||
@@ -377,7 +375,7 @@ class SequenceBlockTestCase(XModuleXmlImportTest):
|
||||
targeted vertical through ajax call
|
||||
"""
|
||||
for child in self.sequence_3_1.get_children():
|
||||
usage_key = six.text_type(child.location)
|
||||
usage_key = str(child.location)
|
||||
completion_return = json.loads(self.sequence_3_1.handle_ajax(
|
||||
'get_completion',
|
||||
{'usage_key': usage_key}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
"""
|
||||
Tests for SettingsService
|
||||
"""
|
||||
|
||||
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
from django.test import TestCase
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
|
||||
from config_models.models import ConfigurationModel
|
||||
from django.conf import settings
|
||||
@@ -20,7 +18,7 @@ from xmodule.services import ConfigurationService, SettingsService, TeamsConfigu
|
||||
from openedx.core.lib.teams_config import TeamsConfig
|
||||
|
||||
|
||||
class _DummyBlock(object):
|
||||
class _DummyBlock:
|
||||
""" Dummy Xblock class """
|
||||
pass # lint-amnesty, pylint: disable=unnecessary-pass
|
||||
|
||||
@@ -33,7 +31,7 @@ class DummyConfig(ConfigurationModel):
|
||||
app_label = 'xmoduletestservices'
|
||||
|
||||
|
||||
class DummyUnexpected(object):
|
||||
class DummyUnexpected:
|
||||
"""
|
||||
Dummy Unexpected Class
|
||||
"""
|
||||
@@ -49,7 +47,7 @@ class TestSettingsService(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
""" Setting up tests """
|
||||
super(TestSettingsService, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.settings_service = SettingsService()
|
||||
self.xblock_mock = mock.Mock()
|
||||
self.xblock_mock.block_settings_key = self.xblock_setting_key1
|
||||
@@ -128,7 +126,7 @@ class MockConfigurationService(TeamsConfigurationService):
|
||||
Mock ConfigurationService for testing.
|
||||
"""
|
||||
def __init__(self, course, **kwargs): # lint-amnesty, pylint: disable=unused-argument
|
||||
super(MockConfigurationService, self).__init__() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__()
|
||||
self._course = course
|
||||
|
||||
def get_course(self, course_id):
|
||||
@@ -141,7 +139,7 @@ class ConfigurationServiceBaseClass(TestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(ConfigurationServiceBaseClass, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
|
||||
self.teams_config = TeamsConfig(
|
||||
{'max_size': 2, 'topics': [{'id': 'topic', 'name': 'Topic', 'description': 'A Topic'}]}
|
||||
|
||||
@@ -2,12 +2,11 @@
|
||||
Tests for the Split Testing Module
|
||||
"""
|
||||
import json
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import ddt
|
||||
import lxml
|
||||
import six
|
||||
from fs.memoryfs import MemoryFS
|
||||
from mock import Mock, patch
|
||||
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
from xmodule.modulestore.tests.utils import MixedSplitTestCase
|
||||
@@ -97,8 +96,8 @@ class SplitTestBlockTest(XModuleXmlImportTest, PartitionTestCase):
|
||||
UserPartition(
|
||||
MINIMUM_STATIC_PARTITION_ID, 'second_partition', 'Second Partition',
|
||||
[
|
||||
Group(six.text_type(MINIMUM_STATIC_PARTITION_ID + 1), 'abel'),
|
||||
Group(six.text_type(MINIMUM_STATIC_PARTITION_ID + 2), 'baker'), Group("103", 'charlie')
|
||||
Group(str(MINIMUM_STATIC_PARTITION_ID + 1), 'abel'),
|
||||
Group(str(MINIMUM_STATIC_PARTITION_ID + 2), 'baker'), Group("103", 'charlie')
|
||||
],
|
||||
MockUserPartitionScheme()
|
||||
)
|
||||
@@ -403,10 +402,10 @@ class SplitTestBlockStudioTest(SplitTestBlockTest):
|
||||
assert len(validation.messages) == 0
|
||||
verify_validation_message(
|
||||
validation.summary,
|
||||
u"The experiment is not associated with a group configuration.",
|
||||
"The experiment is not associated with a group configuration.",
|
||||
StudioValidationMessage.NOT_CONFIGURED,
|
||||
'edit-button',
|
||||
u"Select a Group Configuration",
|
||||
"Select a Group Configuration",
|
||||
)
|
||||
|
||||
# Verify the messages for a correctly configured split_test
|
||||
@@ -427,14 +426,14 @@ class SplitTestBlockStudioTest(SplitTestBlockTest):
|
||||
assert len(validation.messages) == 1
|
||||
verify_validation_message(
|
||||
validation.messages[0],
|
||||
u"The experiment does not contain all of the groups in the configuration.",
|
||||
"The experiment does not contain all of the groups in the configuration.",
|
||||
StudioValidationMessage.ERROR,
|
||||
expected_action_runtime_event='add-missing-groups',
|
||||
expected_action_label=u"Add Missing Groups"
|
||||
expected_action_label="Add Missing Groups"
|
||||
)
|
||||
verify_summary_message(
|
||||
validation.summary,
|
||||
u"This content experiment has issues that affect content visibility.",
|
||||
"This content experiment has issues that affect content visibility.",
|
||||
StudioValidationMessage.ERROR
|
||||
)
|
||||
# Verify the messages for a split test with children that are not associated with any group
|
||||
@@ -446,12 +445,12 @@ class SplitTestBlockStudioTest(SplitTestBlockTest):
|
||||
assert len(validation.messages) == 1
|
||||
verify_validation_message(
|
||||
validation.messages[0],
|
||||
u"The experiment has an inactive group. Move content into active groups, then delete the inactive group.",
|
||||
"The experiment has an inactive group. Move content into active groups, then delete the inactive group.",
|
||||
StudioValidationMessage.WARNING
|
||||
)
|
||||
verify_summary_message(
|
||||
validation.summary,
|
||||
u"This content experiment has issues that affect content visibility.",
|
||||
"This content experiment has issues that affect content visibility.",
|
||||
StudioValidationMessage.WARNING
|
||||
)
|
||||
# Verify the messages for a split test with both missing and inactive children
|
||||
@@ -463,20 +462,20 @@ class SplitTestBlockStudioTest(SplitTestBlockTest):
|
||||
assert len(validation.messages) == 2
|
||||
verify_validation_message(
|
||||
validation.messages[0],
|
||||
u"The experiment does not contain all of the groups in the configuration.",
|
||||
"The experiment does not contain all of the groups in the configuration.",
|
||||
StudioValidationMessage.ERROR,
|
||||
expected_action_runtime_event='add-missing-groups',
|
||||
expected_action_label=u"Add Missing Groups"
|
||||
expected_action_label="Add Missing Groups"
|
||||
)
|
||||
verify_validation_message(
|
||||
validation.messages[1],
|
||||
u"The experiment has an inactive group. Move content into active groups, then delete the inactive group.",
|
||||
"The experiment has an inactive group. Move content into active groups, then delete the inactive group.",
|
||||
StudioValidationMessage.WARNING
|
||||
)
|
||||
# With two messages of type error and warning priority given to error.
|
||||
verify_summary_message(
|
||||
validation.summary,
|
||||
u"This content experiment has issues that affect content visibility.",
|
||||
"This content experiment has issues that affect content visibility.",
|
||||
StudioValidationMessage.ERROR
|
||||
)
|
||||
|
||||
@@ -486,13 +485,13 @@ class SplitTestBlockStudioTest(SplitTestBlockTest):
|
||||
assert len(validation.messages) == 1
|
||||
verify_validation_message(
|
||||
validation.messages[0],
|
||||
u"The experiment uses a deleted group configuration. "
|
||||
u"Select a valid group configuration or delete this experiment.",
|
||||
"The experiment uses a deleted group configuration. "
|
||||
"Select a valid group configuration or delete this experiment.",
|
||||
StudioValidationMessage.ERROR
|
||||
)
|
||||
verify_summary_message(
|
||||
validation.summary,
|
||||
u"This content experiment has issues that affect content visibility.",
|
||||
"This content experiment has issues that affect content visibility.",
|
||||
StudioValidationMessage.ERROR
|
||||
)
|
||||
|
||||
@@ -508,13 +507,13 @@ class SplitTestBlockStudioTest(SplitTestBlockTest):
|
||||
assert len(validation.messages) == 1
|
||||
verify_validation_message(
|
||||
validation.messages[0],
|
||||
u"The experiment uses a group configuration that is not supported for experiments. "
|
||||
u"Select a valid group configuration or delete this experiment.",
|
||||
"The experiment uses a group configuration that is not supported for experiments. "
|
||||
"Select a valid group configuration or delete this experiment.",
|
||||
StudioValidationMessage.ERROR
|
||||
)
|
||||
verify_summary_message(
|
||||
validation.summary,
|
||||
u"This content experiment has issues that affect content visibility.",
|
||||
"This content experiment has issues that affect content visibility.",
|
||||
StudioValidationMessage.ERROR
|
||||
)
|
||||
|
||||
@@ -539,7 +538,7 @@ class SplitTestBlockExportImportTest(MixedSplitTestCase):
|
||||
user_partition_id=2,
|
||||
)
|
||||
self.child_blocks = [
|
||||
self.make_block("html", self.split_test_block, display_name="Hello HTML {}".format(i))
|
||||
self.make_block("html", self.split_test_block, display_name=f"Hello HTML {i}")
|
||||
for i in range(1, 3)
|
||||
]
|
||||
self.split_test_block.group_id_to_child = {
|
||||
|
||||
@@ -10,7 +10,7 @@ from xmodule.stringify import stringify_children
|
||||
|
||||
def test_stringify():
|
||||
text = 'Hi <div x="foo">there <span>Bruce</span><b>!</b></div>'
|
||||
html = '''<html a="b" foo="bar">{0}</html>'''.format(text)
|
||||
html = f'''<html a="b" foo="bar">{text}</html>'''
|
||||
xml = etree.fromstring(html)
|
||||
out = stringify_children(xml)
|
||||
assert out == text
|
||||
|
||||
@@ -5,8 +5,8 @@ Tests for the Unit XBlock
|
||||
import re
|
||||
import unittest
|
||||
from xml.dom import minidom
|
||||
from unittest.mock import patch
|
||||
|
||||
from mock import patch
|
||||
from web_fragments.fragment import Fragment
|
||||
from xblock.core import XBlock
|
||||
from xblock.completable import XBlockCompletionMode
|
||||
|
||||
@@ -4,8 +4,7 @@ Tests for extended due date utilities.
|
||||
|
||||
|
||||
import unittest
|
||||
|
||||
import mock
|
||||
from unittest import mock
|
||||
|
||||
from ..util import duedate
|
||||
|
||||
|
||||
@@ -21,16 +21,16 @@ class StudioValidationMessageTest(unittest.TestCase):
|
||||
Test that `TypeError`s are thrown for bad input parameters.
|
||||
"""
|
||||
with pytest.raises(TypeError):
|
||||
StudioValidationMessage("unknown type", u"Unknown type info")
|
||||
StudioValidationMessage("unknown type", "Unknown type info")
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
StudioValidationMessage(StudioValidationMessage.WARNING, u"bad warning", action_class=0)
|
||||
StudioValidationMessage(StudioValidationMessage.WARNING, "bad warning", action_class=0)
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
StudioValidationMessage(StudioValidationMessage.WARNING, u"bad warning", action_runtime_event=0)
|
||||
StudioValidationMessage(StudioValidationMessage.WARNING, "bad warning", action_runtime_event=0)
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
StudioValidationMessage(StudioValidationMessage.WARNING, u"bad warning", action_label=b"Non-unicode string")
|
||||
StudioValidationMessage(StudioValidationMessage.WARNING, "bad warning", action_label=b"Non-unicode string")
|
||||
|
||||
def test_to_json(self):
|
||||
"""
|
||||
@@ -38,22 +38,22 @@ class StudioValidationMessageTest(unittest.TestCase):
|
||||
"""
|
||||
assert \
|
||||
{'type': StudioValidationMessage.NOT_CONFIGURED,
|
||||
'text': u'Not Configured message', 'action_label': u'Action label'} == \
|
||||
'text': 'Not Configured message', 'action_label': 'Action label'} == \
|
||||
StudioValidationMessage(StudioValidationMessage.NOT_CONFIGURED,
|
||||
u'Not Configured message', action_label=u'Action label').to_json()
|
||||
'Not Configured message', action_label='Action label').to_json()
|
||||
|
||||
assert \
|
||||
{'type': StudioValidationMessage.WARNING,
|
||||
'text': u'Warning message',
|
||||
'text': 'Warning message',
|
||||
'action_class': 'class-for-action'} ==\
|
||||
StudioValidationMessage(StudioValidationMessage.WARNING, u'Warning message',
|
||||
StudioValidationMessage(StudioValidationMessage.WARNING, 'Warning message',
|
||||
action_class='class-for-action').to_json()
|
||||
|
||||
assert \
|
||||
{'type': StudioValidationMessage.ERROR,
|
||||
'text': u'Error message', 'action_runtime_event': 'do-fix-up'} ==\
|
||||
'text': 'Error message', 'action_runtime_event': 'do-fix-up'} ==\
|
||||
StudioValidationMessage(StudioValidationMessage.ERROR,
|
||||
u'Error message', action_runtime_event='do-fix-up').to_json()
|
||||
'Error message', action_runtime_event='do-fix-up').to_json()
|
||||
|
||||
|
||||
class StudioValidationTest(unittest.TestCase):
|
||||
@@ -63,7 +63,7 @@ class StudioValidationTest(unittest.TestCase):
|
||||
|
||||
def test_copy(self):
|
||||
validation = Validation("id")
|
||||
validation.add(ValidationMessage(ValidationMessage.ERROR, u"Error message"))
|
||||
validation.add(ValidationMessage(ValidationMessage.ERROR, "Error message"))
|
||||
|
||||
studio_validation = StudioValidation.copy(validation)
|
||||
assert isinstance(studio_validation, StudioValidation)
|
||||
@@ -71,7 +71,7 @@ class StudioValidationTest(unittest.TestCase):
|
||||
assert 1 == len(studio_validation.messages)
|
||||
expected = {
|
||||
"type": StudioValidationMessage.ERROR,
|
||||
"text": u"Error message"
|
||||
"text": "Error message"
|
||||
}
|
||||
assert expected == studio_validation.messages[0].to_json()
|
||||
assert studio_validation.summary is None
|
||||
@@ -79,7 +79,7 @@ class StudioValidationTest(unittest.TestCase):
|
||||
def test_copy_studio_validation(self):
|
||||
validation = StudioValidation("id")
|
||||
validation.add(
|
||||
StudioValidationMessage(StudioValidationMessage.WARNING, u"Warning message", action_label=u"Action Label")
|
||||
StudioValidationMessage(StudioValidationMessage.WARNING, "Warning message", action_label="Action Label")
|
||||
)
|
||||
|
||||
validation_copy = StudioValidation.copy(validation)
|
||||
@@ -87,8 +87,8 @@ class StudioValidationTest(unittest.TestCase):
|
||||
assert 1 == len(validation_copy.messages)
|
||||
expected = {
|
||||
"type": StudioValidationMessage.WARNING,
|
||||
"text": u"Warning message",
|
||||
"action_label": u"Action Label"
|
||||
"text": "Warning message",
|
||||
"action_label": "Action Label"
|
||||
}
|
||||
assert expected == validation_copy.messages[0].to_json()
|
||||
|
||||
@@ -105,13 +105,13 @@ class StudioValidationTest(unittest.TestCase):
|
||||
assert validation.empty
|
||||
assert validation
|
||||
|
||||
validation.add(StudioValidationMessage(StudioValidationMessage.ERROR, u"Error message"))
|
||||
validation.add(StudioValidationMessage(StudioValidationMessage.ERROR, "Error message"))
|
||||
assert not validation.empty
|
||||
assert not validation
|
||||
|
||||
validation_with_summary = StudioValidation("id")
|
||||
validation_with_summary.set_summary(
|
||||
StudioValidationMessage(StudioValidationMessage.NOT_CONFIGURED, u"Summary message")
|
||||
StudioValidationMessage(StudioValidationMessage.NOT_CONFIGURED, "Summary message")
|
||||
)
|
||||
assert not validation.empty
|
||||
assert not validation
|
||||
@@ -121,33 +121,33 @@ class StudioValidationTest(unittest.TestCase):
|
||||
Test the behavior of calling `add_messages` with combination of `StudioValidation` instances.
|
||||
"""
|
||||
validation_1 = StudioValidation("id")
|
||||
validation_1.set_summary(StudioValidationMessage(StudioValidationMessage.WARNING, u"Summary message"))
|
||||
validation_1.add(StudioValidationMessage(StudioValidationMessage.ERROR, u"Error message"))
|
||||
validation_1.set_summary(StudioValidationMessage(StudioValidationMessage.WARNING, "Summary message"))
|
||||
validation_1.add(StudioValidationMessage(StudioValidationMessage.ERROR, "Error message"))
|
||||
|
||||
validation_2 = StudioValidation("id")
|
||||
validation_2.set_summary(StudioValidationMessage(StudioValidationMessage.ERROR, u"Summary 2 message"))
|
||||
validation_2.add(StudioValidationMessage(StudioValidationMessage.NOT_CONFIGURED, u"Not configured"))
|
||||
validation_2.set_summary(StudioValidationMessage(StudioValidationMessage.ERROR, "Summary 2 message"))
|
||||
validation_2.add(StudioValidationMessage(StudioValidationMessage.NOT_CONFIGURED, "Not configured"))
|
||||
|
||||
validation_1.add_messages(validation_2)
|
||||
assert 2 == len(validation_1.messages)
|
||||
|
||||
assert StudioValidationMessage.ERROR == validation_1.messages[0].type
|
||||
assert u'Error message' == validation_1.messages[0].text
|
||||
assert 'Error message' == validation_1.messages[0].text
|
||||
|
||||
assert StudioValidationMessage.NOT_CONFIGURED == validation_1.messages[1].type
|
||||
assert u'Not configured' == validation_1.messages[1].text
|
||||
assert 'Not configured' == validation_1.messages[1].text
|
||||
|
||||
assert StudioValidationMessage.WARNING == validation_1.summary.type
|
||||
assert u'Summary message' == validation_1.summary.text
|
||||
assert 'Summary message' == validation_1.summary.text
|
||||
|
||||
def test_set_summary_accepts_validation_message(self):
|
||||
"""
|
||||
Test that `set_summary` accepts a ValidationMessage.
|
||||
"""
|
||||
validation = StudioValidation("id")
|
||||
validation.set_summary(ValidationMessage(ValidationMessage.WARNING, u"Summary message"))
|
||||
validation.set_summary(ValidationMessage(ValidationMessage.WARNING, "Summary message"))
|
||||
assert ValidationMessage.WARNING == validation.summary.type
|
||||
assert u'Summary message' == validation.summary.text
|
||||
assert 'Summary message' == validation.summary.text
|
||||
|
||||
def test_set_summary_errors(self):
|
||||
"""
|
||||
@@ -171,24 +171,24 @@ class StudioValidationTest(unittest.TestCase):
|
||||
validation.add(
|
||||
StudioValidationMessage(
|
||||
StudioValidationMessage.ERROR,
|
||||
u"Error message",
|
||||
action_label=u"Action label",
|
||||
"Error message",
|
||||
action_label="Action label",
|
||||
action_class="edit-button"
|
||||
)
|
||||
)
|
||||
validation.add(
|
||||
StudioValidationMessage(
|
||||
StudioValidationMessage.NOT_CONFIGURED,
|
||||
u"Not configured message",
|
||||
action_label=u"Action label",
|
||||
"Not configured message",
|
||||
action_label="Action label",
|
||||
action_runtime_event="make groups"
|
||||
)
|
||||
)
|
||||
validation.set_summary(
|
||||
StudioValidationMessage(
|
||||
StudioValidationMessage.WARNING,
|
||||
u"Summary message",
|
||||
action_label=u"Summary label",
|
||||
"Summary message",
|
||||
action_label="Summary label",
|
||||
action_runtime_event="fix everything"
|
||||
)
|
||||
)
|
||||
@@ -200,21 +200,21 @@ class StudioValidationTest(unittest.TestCase):
|
||||
"messages": [
|
||||
{
|
||||
"type": "error",
|
||||
"text": u"Error message",
|
||||
"action_label": u"Action label",
|
||||
"text": "Error message",
|
||||
"action_label": "Action label",
|
||||
"action_class": "edit-button"
|
||||
},
|
||||
{
|
||||
"type": "not-configured",
|
||||
"text": u"Not configured message",
|
||||
"action_label": u"Action label",
|
||||
"text": "Not configured message",
|
||||
"action_label": "Action label",
|
||||
"action_runtime_event": "make groups"
|
||||
}
|
||||
],
|
||||
"summary": {
|
||||
"type": "warning",
|
||||
"text": u"Summary message",
|
||||
"action_label": u"Summary label",
|
||||
"text": "Summary message",
|
||||
"action_label": "Summary label",
|
||||
"action_runtime_event": "fix everything"
|
||||
},
|
||||
"empty": False
|
||||
|
||||
@@ -8,12 +8,11 @@ Tests for vertical module.
|
||||
from collections import namedtuple
|
||||
from datetime import datetime, timedelta
|
||||
import json
|
||||
import pytz
|
||||
import six
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytz
|
||||
import ddt
|
||||
from fs.memoryfs import MemoryFS
|
||||
from mock import Mock, patch
|
||||
|
||||
from . import get_test_system
|
||||
from .helpers import StubUserService
|
||||
@@ -36,7 +35,7 @@ def get_json_request(data):
|
||||
)
|
||||
|
||||
|
||||
class StubCompletionService(object):
|
||||
class StubCompletionService:
|
||||
"""
|
||||
A stub implementation of the CompletionService for testing without access to django
|
||||
"""
|
||||
@@ -86,7 +85,7 @@ class BaseVerticalBlockTest(XModuleXmlImportTest):
|
||||
test_problem = 'Test Problem'
|
||||
|
||||
def setUp(self):
|
||||
super(BaseVerticalBlockTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
# construct module
|
||||
course = xml.CourseFactory.build()
|
||||
sequence = xml.SequenceFactory.build(parent=course)
|
||||
@@ -126,7 +125,7 @@ class VerticalBlockTestCase(BaseVerticalBlockTest):
|
||||
Assert content has/hasn't all the bookmark info.
|
||||
"""
|
||||
assertion('bookmark_id', content)
|
||||
assertion('{},{}'.format(self.username, six.text_type(self.vertical.location)), content)
|
||||
assertion('{},{}'.format(self.username, str(self.vertical.location)), content)
|
||||
assertion('bookmarked', content)
|
||||
assertion('show_bookmark_button', content)
|
||||
|
||||
@@ -170,8 +169,8 @@ class VerticalBlockTestCase(BaseVerticalBlockTest):
|
||||
if context:
|
||||
assert "'has_assignments': True" in html
|
||||
assert "'subsection_format': '{}'".format(context['format']) in html
|
||||
assert "'completed': {}".format((completion_value == 1)) in html
|
||||
assert "'past_due': {}".format((self.vertical.due < now)) in html
|
||||
assert "'completed': {}".format(completion_value == 1) in html
|
||||
assert "'past_due': {}".format(self.vertical.due < now) in html
|
||||
|
||||
@ddt.data(True, False)
|
||||
def test_render_problem_without_score(self, has_score):
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# pylint: disable=protected-access
|
||||
"""Test for Video Xmodule functional logic.
|
||||
These test data read from xml, not from mongo.
|
||||
@@ -21,19 +20,18 @@ import shutil
|
||||
import unittest
|
||||
from tempfile import mkdtemp
|
||||
from uuid import uuid4
|
||||
from unittest.mock import ANY, MagicMock, Mock, patch
|
||||
|
||||
import pytest
|
||||
import ddt
|
||||
import httpretty
|
||||
import six
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from fs.osfs import OSFS
|
||||
from lxml import etree
|
||||
from mock import ANY, MagicMock, Mock, patch
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from opaque_keys.edx.locator import CourseLocator
|
||||
from six.moves import zip
|
||||
from xblock.field_data import DictFieldData
|
||||
from xblock.fields import ScopeIds
|
||||
|
||||
@@ -79,9 +77,9 @@ MOCKED_YOUTUBE_TRANSCRIPT_API_RESPONSE = '''
|
||||
'''
|
||||
|
||||
ALL_LANGUAGES = (
|
||||
[u"en", u"English"],
|
||||
[u"eo", u"Esperanto"],
|
||||
[u"ur", u"Urdu"]
|
||||
["en", "English"],
|
||||
["eo", "Esperanto"],
|
||||
["ur", "Urdu"]
|
||||
)
|
||||
|
||||
|
||||
@@ -174,7 +172,7 @@ class VideoBlockTestBase(unittest.TestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(VideoBlockTestBase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.descriptor = instantiate_descriptor()
|
||||
|
||||
def assertXmlEqual(self, expected, xml):
|
||||
@@ -233,7 +231,7 @@ class TestCreateYouTubeUrl(VideoBlockTestBase):
|
||||
Test that passing unicode to `create_youtube_url` doesn't throw
|
||||
an error.
|
||||
"""
|
||||
self.descriptor.create_youtube_url(u"üñîçø∂é")
|
||||
self.descriptor.create_youtube_url("üñîçø∂é")
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@@ -617,7 +615,7 @@ class VideoBlockImportTestCase(TestCase):
|
||||
assert edx_video_id == 'test_edx_video_id'
|
||||
assert static_dir == EXPORT_IMPORT_STATIC_DIR
|
||||
assert resource_fs is not None
|
||||
assert external_transcripts == {u'en': [u'subs_3_yD_cEKoCk.srt.sjson']}
|
||||
assert external_transcripts == {'en': ['subs_3_yD_cEKoCk.srt.sjson']}
|
||||
assert course_id == 'test_course_id'
|
||||
return edx_video_id
|
||||
|
||||
@@ -646,7 +644,7 @@ class VideoBlockImportTestCase(TestCase):
|
||||
edx_video_id,
|
||||
module_system.resources_fs,
|
||||
EXPORT_IMPORT_STATIC_DIR,
|
||||
{u'en': [u'subs_3_yD_cEKoCk.srt.sjson']},
|
||||
{'en': ['subs_3_yD_cEKoCk.srt.sjson']},
|
||||
course_id='test_course_id'
|
||||
)
|
||||
|
||||
@@ -672,7 +670,7 @@ class VideoExportTestCase(VideoBlockTestBase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(VideoExportTestCase, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.temp_dir = mkdtemp()
|
||||
self.file_system = OSFS(self.temp_dir)
|
||||
self.addCleanup(shutil.rmtree, self.temp_dir)
|
||||
@@ -682,7 +680,7 @@ class VideoExportTestCase(VideoBlockTestBase):
|
||||
"""
|
||||
Test that we write the correct XML on export.
|
||||
"""
|
||||
edx_video_id = u'test_edx_video_id'
|
||||
edx_video_id = 'test_edx_video_id'
|
||||
mock_val_api.export_to_xml = Mock(
|
||||
return_value=dict(
|
||||
xml=etree.Element('video_asset'),
|
||||
@@ -733,7 +731,7 @@ class VideoExportTestCase(VideoBlockTestBase):
|
||||
video_id=edx_video_id,
|
||||
static_dir=EXPORT_IMPORT_STATIC_DIR,
|
||||
resource_fs=self.file_system,
|
||||
course_id=six.text_type(self.descriptor.runtime.course_id.for_branch(None)),
|
||||
course_id=str(self.descriptor.runtime.course_id.for_branch(None)),
|
||||
)
|
||||
|
||||
@patch('xmodule.video_module.video_module.edxval_api')
|
||||
@@ -814,9 +812,9 @@ class VideoExportTestCase(VideoBlockTestBase):
|
||||
"""
|
||||
Test XML export handles the unicode characters.
|
||||
"""
|
||||
self.descriptor.display_name = u'这是文'
|
||||
self.descriptor.display_name = '这是文'
|
||||
xml = self.descriptor.definition_to_xml(self.file_system)
|
||||
assert xml.get('display_name') == u'这是文'
|
||||
assert xml.get('display_name') == '这是文'
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@@ -875,15 +873,15 @@ class VideoBlockStudentViewDataTestCase(unittest.TestCase):
|
||||
def test_student_view_data_with_hls_flag(self, mock_get_video_info, mock_get_video_transcript_content):
|
||||
mock_get_video_info.return_value = {
|
||||
'url': '/edxval/video/example',
|
||||
'edx_video_id': u'example_id',
|
||||
'edx_video_id': 'example_id',
|
||||
'duration': 111.0,
|
||||
'client_video_id': u'The example video',
|
||||
'client_video_id': 'The example video',
|
||||
'encoded_videos': [
|
||||
{
|
||||
'url': u'http://www.meowmix.com',
|
||||
'url': 'http://www.meowmix.com',
|
||||
'file_size': 25556,
|
||||
'bitrate': 9600,
|
||||
'profile': u'hls'
|
||||
'profile': 'hls'
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -901,7 +899,7 @@ class VideoBlockStudentViewDataTestCase(unittest.TestCase):
|
||||
descriptor.runtime.course_id = MagicMock()
|
||||
descriptor.runtime.handler_url = MagicMock()
|
||||
student_view_data = descriptor.student_view_data()
|
||||
expected_video_data = {u'hls': {'url': u'http://www.meowmix.com', 'file_size': 25556}}
|
||||
expected_video_data = {'hls': {'url': 'http://www.meowmix.com', 'file_size': 25556}}
|
||||
self.assertDictEqual(student_view_data.get('encoded_videos'), expected_video_data)
|
||||
|
||||
|
||||
@@ -985,7 +983,7 @@ class VideoBlockIndexingTestCase(unittest.TestCase):
|
||||
</video>
|
||||
'''
|
||||
yt_subs_id = 'OEoXaMPEzfM'
|
||||
url = 'http://video.google.com/timedtext?lang=en&v={}'.format(yt_subs_id)
|
||||
url = f'http://video.google.com/timedtext?lang=en&v={yt_subs_id}'
|
||||
httpretty.register_uri(
|
||||
method=httpretty.GET,
|
||||
uri=url,
|
||||
@@ -1020,7 +1018,7 @@ class VideoBlockIndexingTestCase(unittest.TestCase):
|
||||
</video>
|
||||
'''
|
||||
yt_subs_id = 'OEoXaMPEzfM'
|
||||
url = 'http://video.google.com/timedtext?lang=en&v={}'.format(yt_subs_id)
|
||||
url = f'http://video.google.com/timedtext?lang=en&v={yt_subs_id}'
|
||||
httpretty.register_uri(
|
||||
method=httpretty.GET,
|
||||
uri=url,
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Test for Word cloud Xmodule functional logic."""
|
||||
|
||||
import json
|
||||
from unittest.mock import Mock
|
||||
|
||||
from django.test import TestCase
|
||||
from fs.memoryfs import MemoryFS
|
||||
from lxml import etree
|
||||
from mock import Mock
|
||||
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
|
||||
from webob.multidict import MultiDict
|
||||
from xblock.field_data import DictFieldData
|
||||
@@ -57,7 +56,7 @@ class WordCloudBlockTest(TestCase):
|
||||
node = etree.Element("unknown_root")
|
||||
# This will export the olx to a separate file.
|
||||
block.add_xml_to_node(node)
|
||||
with runtime.export_fs.open(u'word_cloud/block_id.xml') as f:
|
||||
with runtime.export_fs.open('word_cloud/block_id.xml') as f:
|
||||
exported_xml = f.read()
|
||||
|
||||
assert exported_xml == original_xml
|
||||
@@ -103,7 +102,7 @@ class WordCloudBlockTest(TestCase):
|
||||
{'text': 'sun', 'size': 1, 'percent': 4.0}]
|
||||
)
|
||||
|
||||
assert 100.0 == sum((i['percent'] for i in response['top_words']))
|
||||
assert 100.0 == sum(i['percent'] for i in response['top_words'])
|
||||
|
||||
def test_indexibility(self):
|
||||
"""
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Tests for the wrapping layer that provides the XBlock API using XModule/Descriptor
|
||||
functionality
|
||||
@@ -8,6 +7,7 @@ functionality
|
||||
|
||||
|
||||
from unittest.case import SkipTest, TestCase
|
||||
from unittest.mock import Mock
|
||||
|
||||
import ddt
|
||||
import webob
|
||||
@@ -23,9 +23,7 @@ from factory import (
|
||||
)
|
||||
from fs.memoryfs import MemoryFS
|
||||
from lxml import etree
|
||||
from mock import Mock
|
||||
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
|
||||
from six.moves import range
|
||||
from xblock.core import XBlock
|
||||
from xblock.field_data import DictFieldData
|
||||
from xblock.fields import ScopeIds
|
||||
@@ -69,7 +67,7 @@ CONTAINER_XMODULES = {
|
||||
ConditionalBlock: [{}],
|
||||
CourseBlock: [{}],
|
||||
RandomizeBlock: [{'display_name': 'Test String Display'}],
|
||||
SequenceBlock: [{'display_name': u'Test Unicode हिंदी Display'}],
|
||||
SequenceBlock: [{'display_name': 'Test Unicode हिंदी Display'}],
|
||||
VerticalBlock: [{}],
|
||||
WrapperBlock: [{}],
|
||||
}
|
||||
@@ -98,7 +96,7 @@ class ModuleSystemFactory(Factory):
|
||||
performed by :func:`xmodule.tests.get_test_system`, so
|
||||
arguments for that function are valid factory attributes.
|
||||
"""
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
model = ModuleSystem
|
||||
|
||||
@classmethod
|
||||
@@ -114,7 +112,7 @@ class DescriptorSystemFactory(Factory):
|
||||
performed by :func:`xmodule.tests.get_test_descriptor_system`, so
|
||||
arguments for that function are valid factory attributes.
|
||||
"""
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
model = DescriptorSystem
|
||||
|
||||
@classmethod
|
||||
@@ -185,7 +183,7 @@ class LeafDescriptorFactory(Factory):
|
||||
Factory to generate leaf XModuleDescriptors.
|
||||
"""
|
||||
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
model = XModuleDescriptor
|
||||
|
||||
runtime = SubFactory(DescriptorSystemFactory)
|
||||
@@ -260,7 +258,7 @@ class ContainerModuleFactory(LeafModuleFactory):
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class XBlockWrapperTestMixin(object):
|
||||
class XBlockWrapperTestMixin:
|
||||
"""
|
||||
This is a mixin for building tests of the implementation of the XBlock
|
||||
api by wrapping XModule native functions.
|
||||
@@ -374,7 +372,7 @@ class TestXModuleHandler(TestCase):
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestXModuleHandler, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.module = XModule(descriptor=Mock(), field_data=Mock(), runtime=Mock(), scope_ids=Mock())
|
||||
self.module.handle_ajax = Mock(return_value='{}')
|
||||
self.request = webob.Request({})
|
||||
@@ -393,7 +391,7 @@ class TestXModuleHandler(TestCase):
|
||||
assert response.body.decode('utf-8') == '{}'
|
||||
|
||||
@ddt.data(
|
||||
u'{"test_key": "test_value"}',
|
||||
'{"test_key": "test_value"}',
|
||||
'{"test_key": "test_value"}',
|
||||
)
|
||||
def test_xmodule_handler_with_data(self, response_data):
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
|
||||
|
||||
import unittest
|
||||
from unittest.mock import Mock
|
||||
|
||||
from mock import Mock
|
||||
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
|
||||
from six.moves import range
|
||||
from xblock.field_data import DictFieldData
|
||||
from xblock.fields import Any, Boolean, Dict, Float, Integer, List, Scope, String
|
||||
from xblock.runtime import DictKeyValueStore, KvsFieldData
|
||||
@@ -28,7 +27,7 @@ class CrazyJsonString(String):
|
||||
return value + " JSON"
|
||||
|
||||
|
||||
class TestFields(object):
|
||||
class TestFields:
|
||||
# Will be returned by editable_metadata_fields.
|
||||
max_attempts = Integer(scope=Scope.settings, default=1000, values={'min': 1, 'max': 10})
|
||||
# Will not be returned by editable_metadata_fields because filtered out by non_editable_metadata_fields.
|
||||
@@ -73,7 +72,7 @@ class InheritingFieldDataTest(unittest.TestCase):
|
||||
not_inherited = String(scope=Scope.settings, default="nothing")
|
||||
|
||||
def setUp(self):
|
||||
super(InheritingFieldDataTest, self).setUp() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().setUp()
|
||||
self.dummy_course_key = CourseLocator('test_org', 'test_123', 'test_run')
|
||||
self.system = get_test_descriptor_system()
|
||||
self.all_blocks = {}
|
||||
@@ -164,7 +163,7 @@ class InheritingFieldDataTest(unittest.TestCase):
|
||||
parent.inherited = "Changed!"
|
||||
assert parent.inherited == 'Changed!'
|
||||
for child_num in range(10):
|
||||
usage_id = self.get_usage_id("vertical", "child_{}".format(child_num))
|
||||
usage_id = self.get_usage_id("vertical", f"child_{child_num}")
|
||||
child = self.get_a_block(usage_id=usage_id)
|
||||
child.parent = parent.location
|
||||
assert child.inherited == 'Changed!'
|
||||
@@ -343,7 +342,7 @@ class EditableMetadataFieldsTest(unittest.TestCase):
|
||||
class TestModuleDescriptor(TestFields, XmlDescriptor): # lint-amnesty, pylint: disable=abstract-method
|
||||
@property
|
||||
def non_editable_metadata_fields(self):
|
||||
non_editable_fields = super(TestModuleDescriptor, self).non_editable_metadata_fields # lint-amnesty, pylint: disable=super-with-arguments
|
||||
non_editable_fields = super().non_editable_metadata_fields
|
||||
non_editable_fields.append(TestModuleDescriptor.due)
|
||||
return non_editable_fields
|
||||
|
||||
|
||||
@@ -4,12 +4,11 @@ Xml parsing tests for XModules
|
||||
|
||||
|
||||
import pprint
|
||||
from unittest.mock import Mock
|
||||
|
||||
from django.test import TestCase
|
||||
from lxml import etree
|
||||
from mock import Mock
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from six import text_type
|
||||
from xblock.runtime import DictKeyValueStore, KvsFieldData
|
||||
|
||||
from xmodule.mako_module import MakoDescriptorSystem
|
||||
@@ -30,7 +29,7 @@ class InMemorySystem(XMLParsingSystem, MakoDescriptorSystem): # pylint: disable
|
||||
"""Return the policy data for the specified usage"""
|
||||
return xml_import_data.policy.get(policy_key(usage_id), {})
|
||||
|
||||
super(InMemorySystem, self).__init__( # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(
|
||||
get_policy=get_policy,
|
||||
process_xml=self.process_xml,
|
||||
load_item=self.load_item,
|
||||
@@ -50,12 +49,12 @@ class InMemorySystem(XMLParsingSystem, MakoDescriptorSystem): # pylint: disable
|
||||
None,
|
||||
CourseLocationManager(self.course_id),
|
||||
)
|
||||
self._descriptors[text_type(descriptor.location)] = descriptor
|
||||
self._descriptors[str(descriptor.location)] = descriptor
|
||||
return descriptor
|
||||
|
||||
def load_item(self, location, for_parent=None): # pylint: disable=method-hidden, unused-argument
|
||||
"""Return the descriptor loaded for `location`"""
|
||||
return self._descriptors[text_type(location)]
|
||||
return self._descriptors[str(location)]
|
||||
|
||||
|
||||
class XModuleXmlImportTest(TestCase):
|
||||
|
||||
@@ -16,7 +16,7 @@ from xmodule.modulestore.inheritance import InheritanceMixin
|
||||
from xmodule.x_module import XModuleMixin
|
||||
|
||||
|
||||
class XmlImportData(object):
|
||||
class XmlImportData:
|
||||
"""
|
||||
Class to capture all of the data needed to actually run an XML import,
|
||||
so that the Factories have something to generate
|
||||
@@ -49,7 +49,7 @@ class XmlImportData(object):
|
||||
return etree.tostring(self._xml_node)
|
||||
|
||||
def __repr__(self):
|
||||
return u"XmlImportData{!r}".format((
|
||||
return "XmlImportData{!r}".format((
|
||||
self._xml_node, self._xml_string, self.course_id,
|
||||
self.default_class, self.policy,
|
||||
self.filesystem, self.parent, self.xblock_mixins,
|
||||
@@ -67,7 +67,7 @@ class XmlImportFactory(Factory):
|
||||
Factory for generating XmlImportData's, which can hold all the data needed
|
||||
to run an XModule XML import
|
||||
"""
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
model = XmlImportData
|
||||
|
||||
filesystem = OSFS(mkdtemp())
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
|
||||
import logging
|
||||
|
||||
import six
|
||||
from xmodule.fields import Timedelta
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TimeInfo(object):
|
||||
class TimeInfo:
|
||||
"""
|
||||
This is a simple object that calculates and stores datetime information for an XModule
|
||||
based on the due date and the grace period string
|
||||
@@ -29,11 +28,11 @@ class TimeInfo(object):
|
||||
self.display_due_date = None
|
||||
|
||||
if grace_period_string_or_timedelta is not None and self.display_due_date:
|
||||
if isinstance(grace_period_string_or_timedelta, six.string_types):
|
||||
if isinstance(grace_period_string_or_timedelta, str):
|
||||
try:
|
||||
self.grace_period = TimeInfo._delta_standin.from_json(grace_period_string_or_timedelta)
|
||||
except:
|
||||
log.error("Error parsing the grace period {0}".format(grace_period_string_or_timedelta))
|
||||
log.error(f"Error parsing the grace period {grace_period_string_or_timedelta}")
|
||||
raise
|
||||
else:
|
||||
self.grace_period = grace_period_string_or_timedelta
|
||||
|
||||
@@ -61,7 +61,7 @@ class UnitBlock(XBlock):
|
||||
"""
|
||||
# return key/value fields in a Python dict object
|
||||
# values may be numeric / string or dict
|
||||
xblock_body = super(UnitBlock, self).index_dictionary() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
xblock_body = super().index_dictionary()
|
||||
index_body = {
|
||||
"display_name": self.display_name,
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ def get_short_labeler(prefix):
|
||||
`prefix` to an assignment index.
|
||||
"""
|
||||
def labeler(index):
|
||||
return u"{prefix} {index:02d}".format(prefix=prefix, index=index)
|
||||
return f"{prefix} {index:02d}"
|
||||
return labeler
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import re
|
||||
|
||||
import six
|
||||
from django.conf import settings
|
||||
|
||||
DEFAULT_PYTHON_LIB_FILENAME = 'python_lib.zip'
|
||||
@@ -28,7 +27,7 @@ def can_execute_unsafe_code(course_id):
|
||||
# To others using this: the code as-is is brittle and likely to be changed in the future,
|
||||
# as per the TODO, so please consider carefully before adding more values to COURSES_WITH_UNSAFE_CODE
|
||||
for regex in getattr(settings, 'COURSES_WITH_UNSAFE_CODE', []):
|
||||
if re.match(regex, six.text_type(course_id)):
|
||||
if re.match(regex, str(course_id)):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
"""
|
||||
Extension of XBlock Validation class to include information for presentation in Studio.
|
||||
"""
|
||||
|
||||
|
||||
import six
|
||||
from xblock.validation import Validation, ValidationMessage
|
||||
|
||||
|
||||
@@ -33,17 +30,17 @@ class StudioValidationMessage(ValidationMessage):
|
||||
action_runtime_event (str): An event name to be triggered on the xblock client-side runtime when
|
||||
the "fix-up" action is clicked (optional).
|
||||
"""
|
||||
super(StudioValidationMessage, self).__init__(message_type, message_text) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(message_type, message_text)
|
||||
if action_label is not None:
|
||||
if not isinstance(action_label, six.text_type):
|
||||
if not isinstance(action_label, str):
|
||||
raise TypeError("Action label must be unicode.")
|
||||
self.action_label = action_label
|
||||
if action_class is not None:
|
||||
if not isinstance(action_class, six.string_types):
|
||||
if not isinstance(action_class, str):
|
||||
raise TypeError("Action class must be a string.")
|
||||
self.action_class = action_class
|
||||
if action_runtime_event is not None:
|
||||
if not isinstance(action_runtime_event, six.string_types):
|
||||
if not isinstance(action_runtime_event, str):
|
||||
raise TypeError("Action runtime event must be a string.")
|
||||
self.action_runtime_event = action_runtime_event
|
||||
|
||||
@@ -54,7 +51,7 @@ class StudioValidationMessage(ValidationMessage):
|
||||
Returns:
|
||||
dict: A dict representation that is json-serializable.
|
||||
"""
|
||||
serialized = super(StudioValidationMessage, self).to_json() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
serialized = super().to_json()
|
||||
if hasattr(self, "action_label"):
|
||||
serialized["action_label"] = self.action_label
|
||||
if hasattr(self, "action_class"):
|
||||
@@ -94,7 +91,7 @@ class StudioValidation(Validation):
|
||||
Args:
|
||||
xblock_id (object): An identification object that must support conversion to unicode.
|
||||
"""
|
||||
super(StudioValidation, self).__init__(xblock_id) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(xblock_id)
|
||||
self.summary = None
|
||||
|
||||
def set_summary(self, message):
|
||||
@@ -116,7 +113,7 @@ class StudioValidation(Validation):
|
||||
Returns:
|
||||
bool: True iff this instance has no validation issues and therefore has no messages or summary.
|
||||
"""
|
||||
return super(StudioValidation, self).empty and not self.summary # lint-amnesty, pylint: disable=super-with-arguments
|
||||
return super().empty and not self.summary
|
||||
|
||||
def to_json(self):
|
||||
"""
|
||||
@@ -125,7 +122,7 @@ class StudioValidation(Validation):
|
||||
Returns:
|
||||
dict: A dict representation that is json-serializable.
|
||||
"""
|
||||
serialized = super(StudioValidation, self).to_json() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
serialized = super().to_json()
|
||||
if self.summary:
|
||||
serialized["summary"] = self.summary.to_json()
|
||||
return serialized
|
||||
|
||||
@@ -9,7 +9,6 @@ from datetime import datetime
|
||||
from functools import reduce
|
||||
|
||||
import pytz
|
||||
import six
|
||||
from lxml import etree
|
||||
from web_fragments.fragment import Fragment
|
||||
|
||||
@@ -94,7 +93,7 @@ class VerticalBlock(SequenceFields, XModuleFields, StudioEditableBlock, XmlParse
|
||||
fragment.add_fragment_resources(rendered_child)
|
||||
|
||||
contents.append({
|
||||
'id': six.text_type(child.location),
|
||||
'id': str(child.location),
|
||||
'content': rendered_child.content
|
||||
})
|
||||
|
||||
@@ -120,8 +119,8 @@ class VerticalBlock(SequenceFields, XModuleFields, StudioEditableBlock, XmlParse
|
||||
'show_bookmark_button': child_context.get('show_bookmark_button', not is_child_of_vertical),
|
||||
'show_title': child_context.get('show_title', True),
|
||||
'bookmarked': child_context['bookmarked'],
|
||||
'bookmark_id': u"{},{}".format(
|
||||
child_context['username'], six.text_type(self.location)), # pylint: disable=no-member
|
||||
'bookmark_id': "{},{}".format(
|
||||
child_context['username'], str(self.location)), # pylint: disable=no-member
|
||||
})
|
||||
|
||||
fragment.add_content(self.system.render_template('vert_module.html', fragment_context))
|
||||
@@ -172,7 +171,7 @@ class VerticalBlock(SequenceFields, XModuleFields, StudioEditableBlock, XmlParse
|
||||
"""
|
||||
Returns the highest priority icon class.
|
||||
"""
|
||||
child_classes = set(child.get_icon_class() for child in self.get_children())
|
||||
child_classes = {child.get_icon_class() for child in self.get_children()}
|
||||
new_class = 'other'
|
||||
for higher_class in CLASS_PRIORITY:
|
||||
if higher_class in child_classes:
|
||||
@@ -189,7 +188,7 @@ class VerticalBlock(SequenceFields, XModuleFields, StudioEditableBlock, XmlParse
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
log.exception("Unable to load child when parsing Vertical. Continuing...")
|
||||
if system.error_tracker is not None:
|
||||
system.error_tracker(u"ERROR: {0}".format(exc))
|
||||
system.error_tracker(f"ERROR: {exc}")
|
||||
continue
|
||||
return {}, children
|
||||
|
||||
@@ -204,14 +203,14 @@ class VerticalBlock(SequenceFields, XModuleFields, StudioEditableBlock, XmlParse
|
||||
"""
|
||||
Gather all fields which can't be edited.
|
||||
"""
|
||||
non_editable_fields = super(VerticalBlock, self).non_editable_metadata_fields # lint-amnesty, pylint: disable=super-with-arguments
|
||||
non_editable_fields = super().non_editable_metadata_fields
|
||||
non_editable_fields.extend([
|
||||
self.fields['due'], # lint-amnesty, pylint: disable=unsubscriptable-object
|
||||
])
|
||||
return non_editable_fields
|
||||
|
||||
def studio_view(self, context):
|
||||
fragment = super(VerticalBlock, self).studio_view(context) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
fragment = super().studio_view(context)
|
||||
# This continues to use the old XModuleDescriptor javascript code to enabled studio editing.
|
||||
# TODO: Remove this when studio better supports editing of pure XBlocks.
|
||||
fragment.add_javascript('VerticalBlock = XModule.Descriptor;')
|
||||
@@ -224,7 +223,7 @@ class VerticalBlock(SequenceFields, XModuleFields, StudioEditableBlock, XmlParse
|
||||
# return key/value fields in a Python dict object
|
||||
# values may be numeric / string or dict
|
||||
# default implementation is an empty dict
|
||||
xblock_body = super(VerticalBlock, self).index_dictionary() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
xblock_body = super().index_dictionary()
|
||||
index_body = {
|
||||
"display_name": self.display_name,
|
||||
}
|
||||
|
||||
@@ -12,14 +12,11 @@ from functools import wraps
|
||||
|
||||
import requests
|
||||
import simplejson as json
|
||||
import six
|
||||
from django.conf import settings
|
||||
from lxml import etree
|
||||
from opaque_keys.edx.locator import BundleDefinitionLocator
|
||||
from pysrt import SubRipFile, SubRipItem, SubRipTime
|
||||
from pysrt.srtexc import Error
|
||||
from six import text_type
|
||||
from six.moves import range, zip
|
||||
|
||||
from openedx.core.djangolib import blockstore_cache
|
||||
from openedx.core.lib import blockstore_api
|
||||
@@ -71,7 +68,7 @@ def exception_decorator(func):
|
||||
try:
|
||||
return func(*args, **kwds)
|
||||
except (TranscriptsGenerationException, UnicodeDecodeError) as ex:
|
||||
log.exception(text_type(ex))
|
||||
log.exception(str(ex))
|
||||
raise NotFoundError # lint-amnesty, pylint: disable=raise-missing-from
|
||||
return wrapper
|
||||
|
||||
@@ -254,7 +251,7 @@ def generate_subs_from_source(speed_subs, subs_type, subs_filedata, item, langua
|
||||
srt_subs_obj = SubRipFile.from_string(subs_filedata)
|
||||
except Exception as ex:
|
||||
msg = _("Something wrong with SubRip transcripts file during parsing. Inner message is {error_message}").format(
|
||||
error_message=text_type(ex)
|
||||
error_message=str(ex)
|
||||
)
|
||||
raise TranscriptsGenerationException(msg) # lint-amnesty, pylint: disable=raise-missing-from
|
||||
if not srt_subs_obj:
|
||||
@@ -274,7 +271,7 @@ def generate_subs_from_source(speed_subs, subs_type, subs_filedata, item, langua
|
||||
'end': sub_ends,
|
||||
'text': sub_texts}
|
||||
|
||||
for speed, subs_id in six.iteritems(speed_subs):
|
||||
for speed, subs_id in speed_subs.items():
|
||||
save_subs_to_store(
|
||||
generate_subs(speed, 1, subs),
|
||||
subs_id,
|
||||
@@ -308,7 +305,7 @@ def generate_srt_from_sjson(sjson_subs, speed):
|
||||
end=SubRipTime(milliseconds=sjson_speed_1['end'][i]),
|
||||
text=sjson_speed_1['text'][i]
|
||||
)
|
||||
output += (six.text_type(item))
|
||||
output += (str(item))
|
||||
output += '\n'
|
||||
return output
|
||||
|
||||
@@ -346,7 +343,7 @@ def copy_or_rename_transcript(new_name, old_name, item, delete_old=False, user=N
|
||||
If `old_name` is not found in storage, raises `NotFoundError`.
|
||||
If `delete_old` is True, removes `old_name` files from storage.
|
||||
"""
|
||||
filename = u'subs_{0}.srt.sjson'.format(old_name)
|
||||
filename = f'subs_{old_name}.srt.sjson'
|
||||
content_location = StaticContent.compute_location(item.location.course_key, filename)
|
||||
transcripts = contentstore().find(content_location).data.decode('utf-8')
|
||||
save_subs_to_store(json.loads(transcripts), new_name, item)
|
||||
@@ -447,7 +444,7 @@ def manage_video_subtitles_save(item, user, old_metadata=None, generate_translat
|
||||
generate_sjson_for_all_speeds(
|
||||
item,
|
||||
item.transcripts[lang],
|
||||
{speed: subs_id for subs_id, speed in six.iteritems(youtube_speed_dict(item))},
|
||||
{speed: subs_id for subs_id, speed in youtube_speed_dict(item).items()},
|
||||
lang,
|
||||
)
|
||||
except TranscriptException as ex: # lint-amnesty, pylint: disable=unused-variable
|
||||
@@ -472,9 +469,9 @@ def subs_filename(subs_id, lang='en'):
|
||||
Generate proper filename for storage.
|
||||
"""
|
||||
if lang == 'en':
|
||||
return u'subs_{0}.srt.sjson'.format(subs_id)
|
||||
return f'subs_{subs_id}.srt.sjson'
|
||||
else:
|
||||
return u'{0}_subs_{1}.srt.sjson'.format(lang, subs_id)
|
||||
return f'{lang}_subs_{subs_id}.srt.sjson'
|
||||
|
||||
|
||||
def generate_sjson_for_all_speeds(item, user_filename, result_subs_dict, lang):
|
||||
@@ -489,7 +486,7 @@ def generate_sjson_for_all_speeds(item, user_filename, result_subs_dict, lang):
|
||||
srt_transcripts = contentstore().find(Transcript.asset_location(item.location, user_filename))
|
||||
except NotFoundError as ex:
|
||||
raise TranscriptException(_("{exception_message}: Can't find uploaded transcripts: {user_filename}").format( # lint-amnesty, pylint: disable=raise-missing-from
|
||||
exception_message=text_type(ex),
|
||||
exception_message=str(ex),
|
||||
user_filename=user_filename
|
||||
))
|
||||
|
||||
@@ -545,7 +542,7 @@ def get_video_ids_info(edx_video_id, youtube_id_1_0, html5_sources):
|
||||
Returns:
|
||||
tuple: external or internal, video ids list
|
||||
"""
|
||||
clean = lambda item: item.strip() if isinstance(item, six.string_types) else item
|
||||
clean = lambda item: item.strip() if isinstance(item, str) else item
|
||||
external = not bool(clean(edx_video_id))
|
||||
|
||||
video_ids = [edx_video_id, youtube_id_1_0] + get_html5_ids(html5_sources)
|
||||
@@ -617,23 +614,23 @@ def convert_video_transcript(file_name, content, output_format):
|
||||
"""
|
||||
name_and_extension = os.path.splitext(file_name)
|
||||
basename, input_format = name_and_extension[0], name_and_extension[1][1:]
|
||||
filename = u'{base_name}.{ext}'.format(base_name=basename, ext=output_format)
|
||||
filename = f'{basename}.{output_format}'
|
||||
converted_transcript = Transcript.convert(content, input_format=input_format, output_format=output_format)
|
||||
|
||||
return dict(filename=filename, content=converted_transcript)
|
||||
|
||||
|
||||
class Transcript(object):
|
||||
class Transcript:
|
||||
"""
|
||||
Container for transcript methods.
|
||||
"""
|
||||
SRT = u'srt'
|
||||
TXT = u'txt'
|
||||
SJSON = u'sjson'
|
||||
SRT = 'srt'
|
||||
TXT = 'txt'
|
||||
SJSON = 'sjson'
|
||||
mime_types = {
|
||||
SRT: u'application/x-subrip; charset=utf-8',
|
||||
TXT: u'text/plain; charset=utf-8',
|
||||
SJSON: u'application/json',
|
||||
SRT: 'application/x-subrip; charset=utf-8',
|
||||
TXT: 'text/plain; charset=utf-8',
|
||||
SJSON: 'application/json',
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
@@ -655,7 +652,7 @@ class Transcript(object):
|
||||
|
||||
if input_format == 'srt':
|
||||
# Standardize content into bytes for later decoding.
|
||||
if isinstance(content, text_type):
|
||||
if isinstance(content, str):
|
||||
content = content.encode('utf-8')
|
||||
|
||||
if output_format == 'txt':
|
||||
@@ -670,7 +667,7 @@ class Transcript(object):
|
||||
error_handling=SubRipFile.ERROR_RAISE
|
||||
)
|
||||
except Error as ex: # Base exception from pysrt
|
||||
raise TranscriptsGenerationException(text_type(ex)) # lint-amnesty, pylint: disable=raise-missing-from
|
||||
raise TranscriptsGenerationException(str(ex)) # lint-amnesty, pylint: disable=raise-missing-from
|
||||
|
||||
return json.dumps(generate_sjson_from_srt(srt_subs))
|
||||
|
||||
@@ -731,7 +728,7 @@ class Transcript(object):
|
||||
return StaticContent.compute_location(location.course_key, filename)
|
||||
|
||||
|
||||
class VideoTranscriptsMixin(object):
|
||||
class VideoTranscriptsMixin:
|
||||
"""Mixin class for transcript functionality.
|
||||
|
||||
This is necessary for VideoBlock.
|
||||
@@ -764,7 +761,7 @@ class VideoTranscriptsMixin(object):
|
||||
if sub:
|
||||
all_langs.update({'en': sub})
|
||||
|
||||
for language, filename in six.iteritems(all_langs):
|
||||
for language, filename in all_langs.items():
|
||||
try:
|
||||
# for bumper videos, transcripts are stored in content store only
|
||||
if is_bumper:
|
||||
@@ -795,11 +792,11 @@ class VideoTranscriptsMixin(object):
|
||||
if self.transcript_language in other_lang:
|
||||
transcript_language = self.transcript_language
|
||||
elif sub:
|
||||
transcript_language = u'en'
|
||||
transcript_language = 'en'
|
||||
elif len(other_lang) > 0:
|
||||
transcript_language = sorted(other_lang)[0]
|
||||
else:
|
||||
transcript_language = u'en'
|
||||
transcript_language = 'en'
|
||||
return transcript_language
|
||||
|
||||
def get_transcripts_info(self, is_bumper=False):
|
||||
@@ -854,7 +851,7 @@ def get_transcript_from_val(edx_video_id, lang=None, output_format=Transcript.SR
|
||||
"""
|
||||
transcript = get_video_transcript_content(edx_video_id, lang)
|
||||
if not transcript:
|
||||
raise NotFoundError(u'Transcript not found for {}, lang: {}'.format(edx_video_id, lang))
|
||||
raise NotFoundError(f'Transcript not found for {edx_video_id}, lang: {lang}')
|
||||
|
||||
transcript_conversion_props = dict(transcript, output_format=output_format)
|
||||
transcript = convert_video_transcript(**transcript_conversion_props)
|
||||
@@ -916,7 +913,7 @@ def get_transcript_from_contentstore(video, language, output_format, transcripts
|
||||
"""
|
||||
input_format, base_name, transcript_content = None, None, None
|
||||
if output_format not in (Transcript.SRT, Transcript.SJSON, Transcript.TXT):
|
||||
raise NotFoundError('Invalid transcript format `{output_format}`'.format(output_format=output_format))
|
||||
raise NotFoundError(f'Invalid transcript format `{output_format}`')
|
||||
|
||||
sub, other_languages = transcripts_info['sub'], transcripts_info['transcripts']
|
||||
transcripts = dict(other_languages)
|
||||
@@ -925,7 +922,7 @@ def get_transcript_from_contentstore(video, language, output_format, transcripts
|
||||
possible_sub_ids = [youtube_id, sub, video.youtube_id_1_0] + get_html5_ids(video.html5_sources)
|
||||
for sub_id in possible_sub_ids:
|
||||
try:
|
||||
transcripts[u'en'] = sub_id
|
||||
transcripts['en'] = sub_id
|
||||
input_format, base_name, transcript_content = get_transcript_for_video(
|
||||
video.location,
|
||||
subs_id=sub_id,
|
||||
@@ -942,8 +939,8 @@ def get_transcript_from_contentstore(video, language, output_format, transcripts
|
||||
))
|
||||
|
||||
# add language prefix to transcript file only if language is not None
|
||||
language_prefix = '{}_'.format(language) if language else ''
|
||||
transcript_name = u'{}{}.{}'.format(language_prefix, base_name, output_format)
|
||||
language_prefix = f'{language}_' if language else ''
|
||||
transcript_name = f'{language_prefix}{base_name}.{output_format}'
|
||||
transcript_content = Transcript.convert(transcript_content, input_format=input_format, output_format=output_format)
|
||||
if not transcript_content.strip():
|
||||
raise NotFoundError('No transcript content')
|
||||
@@ -991,7 +988,7 @@ def get_transcript_from_blockstore(video_block, language, output_format, transcr
|
||||
tuple containing content, filename, mimetype
|
||||
"""
|
||||
if output_format not in (Transcript.SRT, Transcript.SJSON, Transcript.TXT):
|
||||
raise NotFoundError('Invalid transcript format `{output_format}`'.format(output_format=output_format))
|
||||
raise NotFoundError(f'Invalid transcript format `{output_format}`')
|
||||
transcripts = transcripts_info['transcripts']
|
||||
if language not in transcripts:
|
||||
raise NotFoundError("Video {} does not have a transcript file defined for the '{}' language in its OLX.".format(
|
||||
@@ -1017,7 +1014,7 @@ def get_transcript_from_blockstore(video_block, language, output_format, transcr
|
||||
))
|
||||
# Now convert the transcript data to the requested format:
|
||||
filename_no_extension = os.path.splitext(filename)[0]
|
||||
output_filename = '{}.{}'.format(filename_no_extension, output_format)
|
||||
output_filename = f'{filename_no_extension}.{output_format}'
|
||||
output_transcript = Transcript.convert(
|
||||
content_binary.decode('utf-8'),
|
||||
input_format=Transcript.SRT,
|
||||
|
||||
@@ -10,7 +10,6 @@ import json
|
||||
import logging
|
||||
import math
|
||||
|
||||
import six
|
||||
from django.core.files.base import ContentFile
|
||||
from django.utils.timezone import now
|
||||
from edxval.api import create_external_video, create_or_update_video_transcript, delete_video_transcript
|
||||
@@ -47,15 +46,15 @@ def to_boolean(value):
|
||||
"""
|
||||
Convert a value from a GET or POST request parameter to a bool
|
||||
"""
|
||||
if isinstance(value, six.binary_type):
|
||||
if isinstance(value, bytes):
|
||||
value = value.decode('ascii', errors='replace')
|
||||
if isinstance(value, six.text_type):
|
||||
if isinstance(value, str):
|
||||
return value.lower() == 'true'
|
||||
else:
|
||||
return bool(value)
|
||||
|
||||
|
||||
class VideoStudentViewHandlers(object):
|
||||
class VideoStudentViewHandlers:
|
||||
"""
|
||||
Handlers for video module instance.
|
||||
"""
|
||||
@@ -91,7 +90,7 @@ class VideoStudentViewHandlers(object):
|
||||
value = now()
|
||||
|
||||
if key == 'speed' and math.isnan(value):
|
||||
message = u"Invalid speed value {}, must be a float.".format(value)
|
||||
message = f"Invalid speed value {value}, must be a float."
|
||||
log.warning(message)
|
||||
return json.dumps({'success': False, 'error': message})
|
||||
|
||||
@@ -102,8 +101,8 @@ class VideoStudentViewHandlers(object):
|
||||
|
||||
return json.dumps({'success': True})
|
||||
|
||||
log.debug(u"GET {0}".format(data))
|
||||
log.debug(u"DISPATCH {0}".format(dispatch))
|
||||
log.debug(f"GET {data}")
|
||||
log.debug(f"DISPATCH {dispatch}")
|
||||
|
||||
raise NotFoundError('Unexpected dispatch type')
|
||||
|
||||
@@ -159,7 +158,7 @@ class VideoStudentViewHandlers(object):
|
||||
generate_sjson_for_all_speeds(
|
||||
self,
|
||||
other_lang[self.transcript_language],
|
||||
{speed: youtube_id for youtube_id, speed in six.iteritems(youtube_ids)},
|
||||
{speed: youtube_id for youtube_id, speed in youtube_ids.items()},
|
||||
self.transcript_language
|
||||
)
|
||||
sjson_transcript = Transcript.asset(self.location, youtube_id, self.transcript_language).data
|
||||
@@ -218,7 +217,7 @@ class VideoStudentViewHandlers(object):
|
||||
if asset_path:
|
||||
response = Response(
|
||||
status=307,
|
||||
location='/static/{0}/{1}'.format(
|
||||
location='/static/{}/{}'.format(
|
||||
asset_path,
|
||||
subs_filename(transcript_name, self.transcript_language)
|
||||
)
|
||||
@@ -240,14 +239,14 @@ class VideoStudentViewHandlers(object):
|
||||
"""
|
||||
completion_service = self.runtime.service(self, 'completion')
|
||||
if completion_service is None: # lint-amnesty, pylint: disable=no-else-raise
|
||||
raise JsonHandlerError(500, u"No completion service found")
|
||||
raise JsonHandlerError(500, "No completion service found")
|
||||
elif not completion_service.completion_tracking_enabled():
|
||||
raise JsonHandlerError(404, u"Completion tracking is not enabled and API calls are unexpected")
|
||||
raise JsonHandlerError(404, "Completion tracking is not enabled and API calls are unexpected")
|
||||
if not isinstance(data['completion'], (int, float)): # lint-amnesty, pylint: disable=no-else-raise
|
||||
message = u"Invalid completion value {}. Must be a float in range [0.0, 1.0]"
|
||||
message = "Invalid completion value {}. Must be a float in range [0.0, 1.0]"
|
||||
raise JsonHandlerError(400, message.format(data['completion']))
|
||||
elif not 0.0 <= data['completion'] <= 1.0:
|
||||
message = u"Invalid completion value {}. Must be in range [0.0, 1.0]"
|
||||
message = "Invalid completion value {}. Must be in range [0.0, 1.0]"
|
||||
raise JsonHandlerError(400, message.format(data['completion']))
|
||||
self.runtime.publish(self, "completion", data)
|
||||
return {"result": "ok"}
|
||||
@@ -272,7 +271,7 @@ class VideoStudentViewHandlers(object):
|
||||
headerlist.append(
|
||||
(
|
||||
'Content-Disposition',
|
||||
'attachment; filename="{}"'.format(filename.encode('utf-8') if six.PY2 else filename)
|
||||
f'attachment; filename="{filename}"'
|
||||
)
|
||||
)
|
||||
|
||||
@@ -419,7 +418,7 @@ class VideoStudentViewHandlers(object):
|
||||
return response
|
||||
|
||||
|
||||
class VideoStudioViewHandlers(object):
|
||||
class VideoStudioViewHandlers:
|
||||
"""
|
||||
Handlers for Studio view.
|
||||
"""
|
||||
@@ -443,15 +442,15 @@ class VideoStudioViewHandlers(object):
|
||||
available_translations = self.available_translations(transcripts, verify_assets=True)
|
||||
|
||||
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 available_translations
|
||||
):
|
||||
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 data:
|
||||
error = _(u'A transcript file is required.')
|
||||
error = _('A transcript file is required.')
|
||||
|
||||
return error
|
||||
|
||||
@@ -500,7 +499,7 @@ class VideoStudioViewHandlers(object):
|
||||
if not edx_video_id:
|
||||
# Back-populate the video ID for an external video.
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
self.edx_video_id = edx_video_id = create_external_video(display_name=u'external video')
|
||||
self.edx_video_id = edx_video_id = create_external_video(display_name='external video')
|
||||
|
||||
try:
|
||||
# Convert SRT transcript into an SJSON format
|
||||
@@ -528,7 +527,7 @@ class VideoStudioViewHandlers(object):
|
||||
response = Response(
|
||||
json={
|
||||
'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.'
|
||||
)
|
||||
},
|
||||
status=400
|
||||
@@ -545,7 +544,7 @@ class VideoStudioViewHandlers(object):
|
||||
if edx_video_id:
|
||||
delete_video_transcript(video_id=edx_video_id, language_code=language)
|
||||
|
||||
if language == u'en':
|
||||
if language == 'en':
|
||||
# remove any transcript file from content store for the video ids
|
||||
possible_sub_ids = [
|
||||
self.sub, # pylint: disable=access-member-before-definition
|
||||
@@ -567,7 +566,7 @@ class VideoStudioViewHandlers(object):
|
||||
elif request.method == 'GET':
|
||||
language = request.GET.get('language_code')
|
||||
if not language:
|
||||
return Response(json={'error': _(u'Language is required.')}, status=400)
|
||||
return Response(json={'error': _('Language is required.')}, status=400)
|
||||
|
||||
try:
|
||||
transcript_content, transcript_name, mime_type = get_transcript(
|
||||
@@ -576,9 +575,7 @@ class VideoStudioViewHandlers(object):
|
||||
response = Response(transcript_content, headerlist=[
|
||||
(
|
||||
'Content-Disposition',
|
||||
'attachment; filename="{}"'.format(
|
||||
transcript_name.encode('utf8') if six.PY2 else transcript_name
|
||||
)
|
||||
f'attachment; filename="{transcript_name}"'
|
||||
),
|
||||
('Content-Language', language),
|
||||
('Content-Type', mime_type)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Video is ungraded Xmodule for support video content.
|
||||
It's new improved video module, which support additional feature:
|
||||
- Can play non-YouTube video sources via in-browser HTML5 video player.
|
||||
@@ -20,7 +19,6 @@ import logging
|
||||
from collections import OrderedDict, defaultdict
|
||||
from operator import itemgetter
|
||||
|
||||
import six
|
||||
from django.conf import settings
|
||||
from edx_django_utils.cache import RequestCache
|
||||
from lxml import etree
|
||||
@@ -105,8 +103,8 @@ log = logging.getLogger(__name__)
|
||||
# `django.utils.translation.ugettext_noop` because Django cannot be imported in this file
|
||||
_ = lambda text: text
|
||||
|
||||
EXPORT_IMPORT_COURSE_DIR = u'course'
|
||||
EXPORT_IMPORT_STATIC_DIR = u'static'
|
||||
EXPORT_IMPORT_COURSE_DIR = 'course'
|
||||
EXPORT_IMPORT_STATIC_DIR = 'static'
|
||||
|
||||
|
||||
@XBlock.wants('settings', 'completion', 'i18n', 'request_cache')
|
||||
@@ -338,7 +336,7 @@ class VideoBlock(
|
||||
if getattr(self, 'video_speed_optimizations', True) and cdn_url:
|
||||
branding_info = BrandingInfoConfig.get_config().get(self.system.user_location)
|
||||
|
||||
if self.edx_video_id and edxval_api and video_status != u'external':
|
||||
if self.edx_video_id and edxval_api and video_status != 'external':
|
||||
for index, source_url in enumerate(sources):
|
||||
new_url = rewrite_video_url(cdn_url, source_url)
|
||||
if new_url:
|
||||
@@ -478,7 +476,7 @@ class VideoBlock(
|
||||
is the override of the general XBlock method, and it will also ask
|
||||
its superclass to validate.
|
||||
"""
|
||||
validation = super(VideoBlock, self).validate() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
validation = super().validate()
|
||||
if not isinstance(validation, StudioValidation):
|
||||
validation = StudioValidation.copy(validation)
|
||||
|
||||
@@ -564,7 +562,7 @@ class VideoBlock(
|
||||
|
||||
@property
|
||||
def editable_metadata_fields(self):
|
||||
editable_fields = super(VideoBlock, self).editable_metadata_fields # lint-amnesty, pylint: disable=super-with-arguments
|
||||
editable_fields = super().editable_metadata_fields
|
||||
|
||||
settings_service = self.runtime.service(self, 'settings')
|
||||
if settings_service:
|
||||
@@ -593,7 +591,7 @@ class VideoBlock(
|
||||
possible_sub_ids = [self.sub, self.youtube_id_1_0] + get_html5_ids(self.html5_sources)
|
||||
for sub_id in possible_sub_ids:
|
||||
try:
|
||||
_, sub_id, _ = get_transcript(self, lang=u'en', output_format=Transcript.TXT)
|
||||
_, sub_id, _ = get_transcript(self, lang='en', output_format=Transcript.TXT)
|
||||
transcripts_info['transcripts'] = dict(transcripts_info['transcripts'], en=sub_id)
|
||||
break
|
||||
except NotFoundError:
|
||||
@@ -675,7 +673,7 @@ class VideoBlock(
|
||||
# Mild workaround to ensure that tests pass -- if a field
|
||||
# is set to its default value, we don't need to write it out.
|
||||
if youtube_string and youtube_string != '1.00:3_yD_cEKoCk':
|
||||
xml.set('youtube', six.text_type(youtube_string))
|
||||
xml.set('youtube', str(youtube_string))
|
||||
xml.set('url_name', self.url_name)
|
||||
attrs = [
|
||||
('display_name', self.display_name),
|
||||
@@ -692,13 +690,13 @@ class VideoBlock(
|
||||
if value:
|
||||
if key in self.fields and self.fields[key].is_set_on(self): # lint-amnesty, pylint: disable=unsubscriptable-object, unsupported-membership-test
|
||||
try:
|
||||
xml.set(key, six.text_type(value))
|
||||
xml.set(key, str(value))
|
||||
except UnicodeDecodeError:
|
||||
exception_message = format_xml_exception_message(self.location, key, value)
|
||||
log.exception(exception_message)
|
||||
# If exception is UnicodeDecodeError set value using unicode 'utf-8' scheme.
|
||||
log.info("Setting xml value using 'utf-8' scheme.")
|
||||
xml.set(key, six.text_type(value, 'utf-8'))
|
||||
xml.set(key, str(value, 'utf-8'))
|
||||
except ValueError:
|
||||
exception_message = format_xml_exception_message(self.location, key, value)
|
||||
log.exception(exception_message)
|
||||
@@ -740,7 +738,7 @@ class VideoBlock(
|
||||
video_id=edx_video_id,
|
||||
resource_fs=resource_fs,
|
||||
static_dir=EXPORT_IMPORT_STATIC_DIR,
|
||||
course_id=six.text_type(self.runtime.course_id.for_branch(None))
|
||||
course_id=str(self.runtime.course_id.for_branch(None))
|
||||
)
|
||||
# Update xml with edxval metadata
|
||||
xml.append(exported_metadata['xml'])
|
||||
@@ -780,15 +778,15 @@ class VideoBlock(
|
||||
A full youtube url to the video whose ID is passed in
|
||||
"""
|
||||
if youtube_id:
|
||||
return u'https://www.youtube.com/watch?v={0}'.format(youtube_id)
|
||||
return f'https://www.youtube.com/watch?v={youtube_id}'
|
||||
else:
|
||||
return u''
|
||||
return ''
|
||||
|
||||
def get_context(self):
|
||||
"""
|
||||
Extend context by data for transcript basic tab.
|
||||
"""
|
||||
_context = super(VideoBlock, self).get_context() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
_context = super().get_context()
|
||||
|
||||
metadata_fields = copy.deepcopy(self.editable_metadata_fields)
|
||||
|
||||
@@ -891,7 +889,7 @@ class VideoBlock(
|
||||
Arguments:
|
||||
id_generator is used to generate course-specific urls and identifiers
|
||||
"""
|
||||
if isinstance(xml, six.string_types):
|
||||
if isinstance(xml, str):
|
||||
xml = etree.fromstring(xml)
|
||||
|
||||
field_data = {}
|
||||
@@ -937,7 +935,7 @@ class VideoBlock(
|
||||
normalized_speed = speed[:-1] if speed.endswith('0') else speed
|
||||
# If the user has specified html5 sources, make sure we don't use the default video
|
||||
if youtube_id != '' or 'html5_sources' in field_data:
|
||||
field_data['youtube_id_{0}'.format(normalized_speed.replace('.', '_'))] = youtube_id
|
||||
field_data['youtube_id_{}'.format(normalized_speed.replace('.', '_'))] = youtube_id
|
||||
elif attr in conversions:
|
||||
field_data[attr] = conversions[attr](value)
|
||||
elif attr not in cls.fields: # lint-amnesty, pylint: disable=unsupported-membership-test
|
||||
@@ -1016,7 +1014,7 @@ class VideoBlock(
|
||||
return edx_video_id
|
||||
|
||||
def index_dictionary(self):
|
||||
xblock_body = super(VideoBlock, self).index_dictionary() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
xblock_body = super().index_dictionary()
|
||||
video_body = {
|
||||
"display_name": self.display_name,
|
||||
}
|
||||
@@ -1061,7 +1059,7 @@ class VideoBlock(
|
||||
"""
|
||||
Returns the VAL data for the requested video profiles for the given course.
|
||||
"""
|
||||
return edxval_api.get_video_info_for_course_and_profiles(six.text_type(course_id), video_profile_names)
|
||||
return edxval_api.get_video_info_for_course_and_profiles(str(course_id), video_profile_names)
|
||||
|
||||
def student_view_data(self, context=None):
|
||||
"""
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Module contains utils specific for video_module but not for transcripts.
|
||||
"""
|
||||
@@ -6,13 +5,11 @@ Module contains utils specific for video_module but not for transcripts.
|
||||
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
from urllib.parse import parse_qs, urlencode, urlparse, urlsplit, urlunsplit
|
||||
|
||||
import six
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import URLValidator
|
||||
from six.moves import zip
|
||||
from six.moves.urllib.parse import parse_qs, urlencode, urlparse, urlsplit, urlunsplit
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -106,7 +103,7 @@ def format_xml_exception_message(location, key, value):
|
||||
when setting xml attributes.
|
||||
"""
|
||||
exception_message = "Block-location:{location}, Key:{key}, Value:{value}".format(
|
||||
location=six.text_type(location),
|
||||
location=str(location),
|
||||
key=key,
|
||||
value=value
|
||||
)
|
||||
|
||||
@@ -14,7 +14,7 @@ from xmodule.fields import RelativeTime
|
||||
_ = lambda text: text
|
||||
|
||||
|
||||
class VideoFields(object):
|
||||
class VideoFields:
|
||||
"""Fields for `VideoBlock`."""
|
||||
display_name = String(
|
||||
help=_("The display name for this component."),
|
||||
|
||||
@@ -12,8 +12,6 @@ import logging
|
||||
|
||||
from pkg_resources import resource_string
|
||||
|
||||
import six
|
||||
from six.moves import map
|
||||
from web_fragments.fragment import Fragment
|
||||
from xblock.fields import Boolean, Dict, Integer, List, Scope, String
|
||||
from xmodule.editing_module import EditingMixin
|
||||
@@ -141,7 +139,7 @@ class WordCloudBlock( # pylint: disable=abstract-method
|
||||
def get_state(self):
|
||||
"""Return success json answer for client."""
|
||||
if self.submitted:
|
||||
total_count = sum(six.itervalues(self.all_words))
|
||||
total_count = sum(self.all_words.values())
|
||||
return json.dumps({
|
||||
'status': 'success',
|
||||
'submitted': True,
|
||||
@@ -318,7 +316,7 @@ class WordCloudBlock( # pylint: disable=abstract-method
|
||||
# values may be numeric / string or dict
|
||||
# default implementation is an empty dict
|
||||
|
||||
xblock_body = super(WordCloudBlock, self).index_dictionary() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
xblock_body = super().index_dictionary()
|
||||
|
||||
index_body = {
|
||||
"display_name": self.display_name,
|
||||
|
||||
@@ -7,7 +7,6 @@ import time
|
||||
from collections import namedtuple
|
||||
from functools import partial
|
||||
|
||||
import six
|
||||
import yaml
|
||||
from contracts import contract, new_contract
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
@@ -16,8 +15,6 @@ from lxml import etree
|
||||
from opaque_keys.edx.asides import AsideDefinitionKeyV2, AsideUsageKeyV2
|
||||
from opaque_keys.edx.keys import UsageKey
|
||||
from pkg_resources import resource_exists, resource_isdir, resource_listdir, resource_string
|
||||
from six import text_type
|
||||
from six.moves import map
|
||||
from web_fragments.fragment import Fragment
|
||||
from webob import Response
|
||||
from webob.multidict import MultiDict
|
||||
@@ -78,8 +75,8 @@ STUDIO_VIEW = 'studio_view'
|
||||
PREVIEW_VIEWS = [STUDENT_VIEW, PUBLIC_VIEW, AUTHOR_VIEW]
|
||||
|
||||
DEFAULT_PUBLIC_VIEW_MESSAGE = (
|
||||
u'This content is only accessible to enrolled learners. '
|
||||
u'Sign in or register, and enroll in this course to view it.'
|
||||
'This content is only accessible to enrolled learners. '
|
||||
'Sign in or register, and enroll in this course to view it.'
|
||||
)
|
||||
|
||||
# Make '_' a no-op so we can scrape strings. Using lambda instead of
|
||||
@@ -203,7 +200,7 @@ def dummy_track(_event_type, _event):
|
||||
pass
|
||||
|
||||
|
||||
class HTMLSnippet(object):
|
||||
class HTMLSnippet:
|
||||
"""
|
||||
A base class defining an interface for an object that is able to present an
|
||||
html snippet, along with associated javascript and css
|
||||
@@ -300,7 +297,7 @@ class HTMLSnippet(object):
|
||||
Return the html used to display this snippet
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"get_html() must be provided by specific modules - not present in {0}"
|
||||
"get_html() must be provided by specific modules - not present in {}"
|
||||
.format(self.__class__))
|
||||
|
||||
|
||||
@@ -319,7 +316,7 @@ def shim_xmodule_js(fragment, js_module_name):
|
||||
add_webpack_to_fragment(fragment, 'XModuleShim')
|
||||
|
||||
|
||||
class XModuleFields(object):
|
||||
class XModuleFields:
|
||||
"""
|
||||
Common fields for XModules.
|
||||
"""
|
||||
@@ -369,7 +366,7 @@ class XModuleMixin(XModuleFields, XBlock):
|
||||
self.xmodule_runtime = None
|
||||
self._asides = []
|
||||
|
||||
super(XModuleMixin, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def runtime(self):
|
||||
@@ -484,8 +481,8 @@ class XModuleMixin(XModuleFields, XBlock):
|
||||
result[field.name] = field.read_json(self)
|
||||
except TypeError as exception:
|
||||
exception_message = "{message}, Block-location:{location}, Field-name:{field_name}".format(
|
||||
message=text_type(exception),
|
||||
location=text_type(self.location),
|
||||
message=str(exception),
|
||||
location=str(self.location),
|
||||
field_name=field.name
|
||||
)
|
||||
raise TypeError(exception_message) # lint-amnesty, pylint: disable=raise-missing-from
|
||||
@@ -551,7 +548,7 @@ class XModuleMixin(XModuleFields, XBlock):
|
||||
return [
|
||||
child
|
||||
for child
|
||||
in super(XModuleMixin, self).get_children(usage_id_filter) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
in super().get_children(usage_id_filter)
|
||||
if child is not None
|
||||
]
|
||||
|
||||
@@ -561,9 +558,9 @@ class XModuleMixin(XModuleFields, XBlock):
|
||||
is an error while retrieving the block.
|
||||
"""
|
||||
try:
|
||||
child = super(XModuleMixin, self).get_child(usage_id) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
child = super().get_child(usage_id)
|
||||
except ItemNotFoundError:
|
||||
log.warning(u'Unable to load item %s, skipping', usage_id)
|
||||
log.warning('Unable to load item %s, skipping', usage_id)
|
||||
return None
|
||||
|
||||
if child is None:
|
||||
@@ -810,14 +807,14 @@ class XModuleMixin(XModuleFields, XBlock):
|
||||
Default message for blocks that don't implement public_view
|
||||
"""
|
||||
alert_html = HTML(
|
||||
u'<div class="page-banner"><div class="alert alert-warning">'
|
||||
u'<span class="icon icon-alert fa fa fa-warning" aria-hidden="true"></span>'
|
||||
u'<div class="message-content">{}</div></div></div>'
|
||||
'<div class="page-banner"><div class="alert alert-warning">'
|
||||
'<span class="icon icon-alert fa fa fa-warning" aria-hidden="true"></span>'
|
||||
'<div class="message-content">{}</div></div></div>'
|
||||
)
|
||||
|
||||
if self.display_name:
|
||||
display_text = _(
|
||||
u'{display_name} is only accessible to enrolled learners. '
|
||||
'{display_name} is only accessible to enrolled learners. '
|
||||
'Sign in or register, and enroll in this course to view it.'
|
||||
).format(
|
||||
display_name=self.display_name
|
||||
@@ -828,7 +825,7 @@ class XModuleMixin(XModuleFields, XBlock):
|
||||
return Fragment(alert_html.format(display_text))
|
||||
|
||||
|
||||
class ProxyAttribute(object):
|
||||
class ProxyAttribute:
|
||||
"""
|
||||
A (python) descriptor that proxies attribute access.
|
||||
|
||||
@@ -876,7 +873,7 @@ descriptor_attr = partial(ProxyAttribute, 'descriptor') # pylint: disable=inval
|
||||
module_runtime_attr = partial(ProxyAttribute, 'xmodule_runtime') # pylint: disable=invalid-name
|
||||
|
||||
|
||||
class XModuleToXBlockMixin(object):
|
||||
class XModuleToXBlockMixin:
|
||||
"""
|
||||
Common code needed by XModule and XBlocks converted from XModules.
|
||||
"""
|
||||
@@ -892,7 +889,7 @@ class XModuleToXBlockMixin(object):
|
||||
"""
|
||||
XBlock handler that wraps `handle_ajax`
|
||||
"""
|
||||
class FileObjForWebobFiles(object):
|
||||
class FileObjForWebobFiles:
|
||||
"""
|
||||
Turn Webob cgi.FieldStorage uploaded files into pure file objects.
|
||||
|
||||
@@ -912,7 +909,7 @@ class XModuleToXBlockMixin(object):
|
||||
# WebOb requests have multiple entries for uploaded files. handle_ajax
|
||||
# expects a single entry as a list.
|
||||
request_post = MultiDict(request.POST)
|
||||
for key in set(six.iterkeys(request.POST)):
|
||||
for key in set(request.POST.keys()):
|
||||
if hasattr(request.POST[key], "file"):
|
||||
request_post[key] = list(map(FileObjForWebobFiles, request.POST.getall(key)))
|
||||
|
||||
@@ -955,7 +952,7 @@ class XModule(XModuleToXBlockMixin, HTMLSnippet, XModuleMixin): # lint-amnesty,
|
||||
# Set the descriptor first so that we can proxy to it
|
||||
self.descriptor = descriptor
|
||||
self._runtime = None
|
||||
super(XModule, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(*args, **kwargs)
|
||||
self.runtime.xmodule_instance = self
|
||||
|
||||
@property
|
||||
@@ -968,12 +965,12 @@ class XModule(XModuleToXBlockMixin, HTMLSnippet, XModuleMixin): # lint-amnesty,
|
||||
|
||||
def __str__(self):
|
||||
# xss-lint: disable=python-wrap-html
|
||||
return u'<x_module(id={0})>'.format(self.id) # lint-amnesty, pylint: disable=no-member
|
||||
return f'<x_module(id={self.id})>' # lint-amnesty, pylint: disable=no-member
|
||||
|
||||
def handle_ajax(self, _dispatch, _data):
|
||||
""" dispatch is last part of the URL.
|
||||
data is a dictionary-like object with the content of the request"""
|
||||
return u""
|
||||
return ""
|
||||
|
||||
def get_child(self, usage_id):
|
||||
if usage_id in self._child_cache:
|
||||
@@ -1028,13 +1025,13 @@ def policy_key(location):
|
||||
Get the key for a location in a policy file. (Since the policy file is
|
||||
specific to a course, it doesn't need the full location url).
|
||||
"""
|
||||
return u'{cat}/{name}'.format(cat=location.block_type, name=location.block_id)
|
||||
return f'{location.block_type}/{location.block_id}'
|
||||
|
||||
|
||||
Template = namedtuple("Template", "metadata data children")
|
||||
|
||||
|
||||
class ResourceTemplates(object):
|
||||
class ResourceTemplates:
|
||||
"""
|
||||
Gets the templates associated w/ a containing cls. The cls must have a 'template_dir_name' attribute.
|
||||
It finds the templates as directly in this directory under 'templates'.
|
||||
@@ -1071,7 +1068,7 @@ class ResourceTemplates(object):
|
||||
if getattr(cls, 'template_dir_name', None):
|
||||
dirname = os.path.join('templates', cls.template_dir_name) # lint-amnesty, pylint: disable=no-member
|
||||
if not resource_isdir(__name__, dirname):
|
||||
log.warning(u"No resource directory {dir} found when loading {cls_name} templates".format(
|
||||
log.warning("No resource directory {dir} found when loading {cls_name} templates".format(
|
||||
dir=dirname,
|
||||
cls_name=cls.__name__,
|
||||
))
|
||||
@@ -1099,7 +1096,7 @@ class ResourceTemplates(object):
|
||||
return template
|
||||
|
||||
|
||||
class XModuleDescriptorToXBlockMixin(object):
|
||||
class XModuleDescriptorToXBlockMixin:
|
||||
"""
|
||||
Common code needed by XModuleDescriptor and XBlocks converted from XModules.
|
||||
"""
|
||||
@@ -1133,9 +1130,9 @@ class XModuleDescriptorToXBlockMixin(object):
|
||||
legacy XModule code. Use the "normal" XBlock parsing code.
|
||||
"""
|
||||
try:
|
||||
return super(XModuleDescriptorToXBlockMixin, cls).parse_xml_new_runtime(node, runtime, keys)
|
||||
return super().parse_xml_new_runtime(node, runtime, keys)
|
||||
except AttributeError:
|
||||
return super(XModuleDescriptorToXBlockMixin, cls).parse_xml(node, runtime, keys, id_generator=None)
|
||||
return super().parse_xml(node, runtime, keys, id_generator=None)
|
||||
|
||||
@classmethod
|
||||
def from_xml(cls, xml_data, system, id_generator):
|
||||
@@ -1226,7 +1223,7 @@ class XModuleDescriptor(XModuleDescriptorToXBlockMixin, HTMLSnippet, ResourceTem
|
||||
|
||||
XModuleDescriptor.__init__ takes the same arguments as xblock.core:XBlock.__init__
|
||||
"""
|
||||
super(XModuleDescriptor, self).__init__(*args, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(*args, **kwargs)
|
||||
# update_version is the version which last updated this xblock v prev being the penultimate updater
|
||||
# leaving off original_version since it complicates creation w/o any obv value yet and is computable
|
||||
# by following previous until None
|
||||
@@ -1271,7 +1268,7 @@ class XModuleDescriptor(XModuleDescriptorToXBlockMixin, HTMLSnippet, ResourceTem
|
||||
performance if we have to rely on lists and equality rather than sets,
|
||||
dictionaries, and identity-based hash functions.
|
||||
"""
|
||||
return super(XModuleDescriptor, self).__hash__() # lint-amnesty, pylint: disable=super-with-arguments
|
||||
return super().__hash__()
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
@@ -1345,7 +1342,7 @@ class XModuleDescriptor(XModuleDescriptorToXBlockMixin, HTMLSnippet, ResourceTem
|
||||
return Fragment(self.get_html())
|
||||
|
||||
|
||||
class ConfigurableFragmentWrapper(object):
|
||||
class ConfigurableFragmentWrapper:
|
||||
"""
|
||||
Runtime mixin that allows for composition of many `wrap_xblock` wrappers
|
||||
"""
|
||||
@@ -1357,7 +1354,7 @@ class ConfigurableFragmentWrapper(object):
|
||||
...
|
||||
return wrapped_frag
|
||||
"""
|
||||
super(ConfigurableFragmentWrapper, self).__init__(**kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(**kwargs)
|
||||
if wrappers is not None:
|
||||
self.wrappers = wrappers
|
||||
else:
|
||||
@@ -1411,7 +1408,7 @@ def descriptor_global_local_resource_url(block, uri):
|
||||
raise NotImplementedError("Applications must monkey-patch this function before using local_resource_url for studio_view") # lint-amnesty, pylint: disable=line-too-long
|
||||
|
||||
|
||||
class MetricsMixin(object):
|
||||
class MetricsMixin:
|
||||
"""
|
||||
Mixin for adding metric logging for render and handle methods in the DescriptorSystem and ModuleSystem.
|
||||
"""
|
||||
@@ -1420,7 +1417,7 @@ class MetricsMixin(object):
|
||||
start_time = time.time()
|
||||
try:
|
||||
status = "success"
|
||||
return super(MetricsMixin, self).render(block, view_name, context=context) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
return super().render(block, view_name, context=context)
|
||||
|
||||
except:
|
||||
status = "failure"
|
||||
@@ -1431,12 +1428,12 @@ class MetricsMixin(object):
|
||||
duration = end_time - start_time
|
||||
course_id = getattr(self, 'course_id', '')
|
||||
tags = [ # lint-amnesty, pylint: disable=unused-variable
|
||||
u'view_name:{}'.format(view_name),
|
||||
u'action:render',
|
||||
u'action_status:{}'.format(status),
|
||||
u'course_id:{}'.format(course_id),
|
||||
u'block_type:{}'.format(block.scope_ids.block_type),
|
||||
u'block_family:{}'.format(block.entry_point),
|
||||
f'view_name:{view_name}',
|
||||
'action:render',
|
||||
f'action_status:{status}',
|
||||
f'course_id:{course_id}',
|
||||
f'block_type:{block.scope_ids.block_type}',
|
||||
f'block_family:{block.entry_point}',
|
||||
]
|
||||
log.debug(
|
||||
"%.3fs - render %s.%s (%s)",
|
||||
@@ -1450,7 +1447,7 @@ class MetricsMixin(object):
|
||||
start_time = time.time()
|
||||
try:
|
||||
status = "success"
|
||||
return super(MetricsMixin, self).handle(block, handler_name, request, suffix=suffix) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
return super().handle(block, handler_name, request, suffix=suffix)
|
||||
|
||||
except:
|
||||
status = "failure"
|
||||
@@ -1461,12 +1458,12 @@ class MetricsMixin(object):
|
||||
duration = end_time - start_time
|
||||
course_id = getattr(self, 'course_id', '')
|
||||
tags = [ # lint-amnesty, pylint: disable=unused-variable
|
||||
u'handler_name:{}'.format(handler_name),
|
||||
u'action:handle',
|
||||
u'action_status:{}'.format(status),
|
||||
u'course_id:{}'.format(course_id),
|
||||
u'block_type:{}'.format(block.scope_ids.block_type),
|
||||
u'block_family:{}'.format(block.entry_point),
|
||||
f'handler_name:{handler_name}',
|
||||
'action:handle',
|
||||
f'action_status:{status}',
|
||||
f'course_id:{course_id}',
|
||||
f'block_type:{block.scope_ids.block_type}',
|
||||
f'block_family:{block.entry_point}',
|
||||
]
|
||||
log.debug(
|
||||
"%.3fs - handle %s.%s (%s)",
|
||||
@@ -1504,7 +1501,7 @@ class DescriptorSystem(MetricsMixin, ConfigurableFragmentWrapper, Runtime):
|
||||
"""
|
||||
kwargs.setdefault('id_reader', OpaqueKeyReader())
|
||||
kwargs.setdefault('id_generator', AsideKeyGenerator())
|
||||
super(DescriptorSystem, self).__init__(**kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(**kwargs)
|
||||
|
||||
# This is used by XModules to write out separate files during xml export
|
||||
self.export_fs = None
|
||||
@@ -1529,7 +1526,7 @@ class DescriptorSystem(MetricsMixin, ConfigurableFragmentWrapper, Runtime):
|
||||
"""
|
||||
if block_type in self.disabled_xblock_types():
|
||||
return self.default_class
|
||||
return super(DescriptorSystem, self).load_block_type(block_type) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
return super().load_block_type(block_type)
|
||||
|
||||
def get_field_provenance(self, xblock, field):
|
||||
"""
|
||||
@@ -1576,7 +1573,7 @@ class DescriptorSystem(MetricsMixin, ConfigurableFragmentWrapper, Runtime):
|
||||
"""
|
||||
See :meth:`xblock.runtime.Runtime:applicable_aside_types` for documentation.
|
||||
"""
|
||||
potential_set = set(super(DescriptorSystem, self).applicable_aside_types(block)) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
potential_set = set(super().applicable_aside_types(block))
|
||||
if getattr(block, 'xmodule_runtime', None) is not None:
|
||||
if hasattr(block.xmodule_runtime, 'applicable_aside_types'):
|
||||
application_set = set(block.xmodule_runtime.applicable_aside_types(block))
|
||||
@@ -1613,7 +1610,7 @@ class DescriptorSystem(MetricsMixin, ConfigurableFragmentWrapper, Runtime):
|
||||
An object implementing the requested service, or None.
|
||||
"""
|
||||
# getting the service from parent module. making sure of block service declarations.
|
||||
service = super(DescriptorSystem, self).service(block=block, service_name=service_name) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
service = super().service(block=block, service_name=service_name)
|
||||
# Passing the block to service if it is callable e.g. ModuleI18nService. It is the responsibility of calling
|
||||
# service to handle the passing argument.
|
||||
if callable(service):
|
||||
@@ -1631,7 +1628,7 @@ class XMLParsingSystem(DescriptorSystem): # lint-amnesty, pylint: disable=abstr
|
||||
created from that xml
|
||||
"""
|
||||
|
||||
super(XMLParsingSystem, self).__init__(**kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(**kwargs)
|
||||
self.process_xml = process_xml
|
||||
|
||||
def _usage_id_from_node(self, node, parent_id, id_generator=None):
|
||||
@@ -1724,7 +1721,7 @@ class XMLParsingSystem(DescriptorSystem): # lint-amnesty, pylint: disable=abstr
|
||||
"""
|
||||
course_key = xblock.scope_ids.usage_id.course_key
|
||||
|
||||
for field in six.itervalues(xblock.fields):
|
||||
for field in xblock.fields.values():
|
||||
if field.is_set_on(xblock):
|
||||
field_value = getattr(xblock, field.name)
|
||||
if field_value is None:
|
||||
@@ -1734,8 +1731,8 @@ class XMLParsingSystem(DescriptorSystem): # lint-amnesty, pylint: disable=abstr
|
||||
elif isinstance(field, ReferenceList):
|
||||
setattr(xblock, field.name, [self._make_usage_key(course_key, ele) for ele in field_value])
|
||||
elif isinstance(field, ReferenceValueDict):
|
||||
for key, subvalue in six.iteritems(field_value):
|
||||
assert isinstance(subvalue, six.string_types)
|
||||
for key, subvalue in field_value.items():
|
||||
assert isinstance(subvalue, str)
|
||||
field_value[key] = self._make_usage_key(course_key, subvalue)
|
||||
setattr(xblock, field.name, field_value)
|
||||
|
||||
@@ -1833,7 +1830,7 @@ class ModuleSystem(MetricsMixin, ConfigurableFragmentWrapper, Runtime):
|
||||
# explicit field_data during construct_xblock.
|
||||
kwargs.setdefault('id_reader', getattr(descriptor_runtime, 'id_reader', OpaqueKeyReader()))
|
||||
kwargs.setdefault('id_generator', getattr(descriptor_runtime, 'id_generator', AsideKeyGenerator()))
|
||||
super(ModuleSystem, self).__init__(field_data=field_data, **kwargs) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().__init__(field_data=field_data, **kwargs)
|
||||
|
||||
self.STATIC_URL = static_url
|
||||
self.xqueue = xqueue
|
||||
@@ -1885,7 +1882,7 @@ class ModuleSystem(MetricsMixin, ConfigurableFragmentWrapper, Runtime):
|
||||
# Remove value set transiently by XBlock
|
||||
kwargs.pop('_view_name')
|
||||
|
||||
return "{}{}".format(self.__class__.__name__, kwargs)
|
||||
return f"{self.__class__.__name__}{kwargs}"
|
||||
|
||||
@property
|
||||
def ajax_url(self):
|
||||
@@ -1919,7 +1916,7 @@ class ModuleSystem(MetricsMixin, ConfigurableFragmentWrapper, Runtime):
|
||||
An object implementing the requested service, or None.
|
||||
"""
|
||||
# getting the service from parent module. making sure of block service declarations.
|
||||
service = super(ModuleSystem, self).service(block=block, service_name=service_name) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
service = super().service(block=block, service_name=service_name)
|
||||
# Passing the block to service if it is callable e.g. ModuleI18nService. It is the responsibility of calling
|
||||
# service to handle the passing argument.
|
||||
if callable(service):
|
||||
@@ -1927,7 +1924,7 @@ class ModuleSystem(MetricsMixin, ConfigurableFragmentWrapper, Runtime):
|
||||
return service
|
||||
|
||||
|
||||
class CombinedSystem(object):
|
||||
class CombinedSystem:
|
||||
"""
|
||||
This class is a shim to allow both pure XBlocks and XModuleDescriptors
|
||||
that have been bound as XModules to access both the attributes of ModuleSystem
|
||||
@@ -2029,7 +2026,7 @@ class CombinedSystem(object):
|
||||
Always set the attr on the DescriptorSystem.
|
||||
"""
|
||||
if name in self.__slots__:
|
||||
return super(CombinedSystem, self).__setattr__(name, value) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
return super().__setattr__(name, value)
|
||||
|
||||
if self._module_system:
|
||||
setattr(self._module_system, name, value)
|
||||
@@ -2045,10 +2042,10 @@ class CombinedSystem(object):
|
||||
delattr(self._descriptor_system, name)
|
||||
|
||||
def __repr__(self):
|
||||
return "CombinedSystem({!r}, {!r})".format(self._module_system, self._descriptor_system)
|
||||
return f"CombinedSystem({self._module_system!r}, {self._descriptor_system!r})"
|
||||
|
||||
|
||||
class DoNothingCache(object):
|
||||
class DoNothingCache:
|
||||
"""A duck-compatible object to use in ModuleSystem when there's no cache."""
|
||||
def get(self, _key):
|
||||
return None
|
||||
|
||||
@@ -45,9 +45,9 @@ def is_pointer_tag(xml_obj):
|
||||
Returns a bool.
|
||||
"""
|
||||
if xml_obj.tag != "course":
|
||||
expected_attr = set(['url_name'])
|
||||
expected_attr = {'url_name'}
|
||||
else:
|
||||
expected_attr = set(['url_name', 'course', 'org'])
|
||||
expected_attr = {'url_name', 'course', 'org'}
|
||||
|
||||
actual_attr = set(xml_obj.attrib.keys())
|
||||
|
||||
@@ -63,7 +63,7 @@ def serialize_field(value):
|
||||
If the value is a string, then we simply return what was passed in.
|
||||
Otherwise, we return json.dumps on the input value.
|
||||
"""
|
||||
if isinstance(value, six.string_types):
|
||||
if isinstance(value, str):
|
||||
return value
|
||||
|
||||
return json.dumps(value, cls=EdxJSONEncoder)
|
||||
@@ -101,7 +101,7 @@ def deserialize_field(field, value):
|
||||
return value
|
||||
|
||||
|
||||
class XmlParserMixin(object):
|
||||
class XmlParserMixin:
|
||||
"""
|
||||
Class containing XML parsing functionality shared between XBlock and XModuleDescriptor.
|
||||
"""
|
||||
@@ -208,7 +208,7 @@ class XmlParserMixin(object):
|
||||
return cls.file_to_xml(xml_file)
|
||||
except Exception as err: # lint-amnesty, pylint: disable=broad-except
|
||||
# Add info about where we are, but keep the traceback
|
||||
msg = 'Unable to load file contents at path %s for item %s: %s ' % (
|
||||
msg = 'Unable to load file contents at path {} for item {}: {} '.format(
|
||||
filepath, def_id, err)
|
||||
six.reraise(Exception, msg, sys.exc_info()[2])
|
||||
|
||||
@@ -275,7 +275,7 @@ class XmlParserMixin(object):
|
||||
Returns a dictionary {key: value}.
|
||||
"""
|
||||
metadata = {'xml_attributes': {}}
|
||||
for attr, val in six.iteritems(xml_object.attrib):
|
||||
for attr, val in xml_object.attrib.items():
|
||||
# VS[compat]. Remove after all key translations done
|
||||
attr = cls._translate(attr)
|
||||
|
||||
@@ -295,7 +295,7 @@ class XmlParserMixin(object):
|
||||
Add the keys in policy to metadata, after processing them
|
||||
through the attrmap. Updates the metadata dict in place.
|
||||
"""
|
||||
for attr, value in six.iteritems(policy):
|
||||
for attr, value in policy.items():
|
||||
attr = cls._translate(attr)
|
||||
if attr not in cls.fields:
|
||||
# Store unknown attributes coming from policy.json
|
||||
@@ -400,9 +400,9 @@ class XmlParserMixin(object):
|
||||
legacy XModule code. Use the "normal" XBlock parsing code.
|
||||
"""
|
||||
try:
|
||||
return super(XmlParserMixin, cls).parse_xml_new_runtime(node, runtime, keys)
|
||||
return super().parse_xml_new_runtime(node, runtime, keys)
|
||||
except AttributeError:
|
||||
return super(XmlParserMixin, cls).parse_xml(node, runtime, keys, id_generator=None)
|
||||
return super().parse_xml(node, runtime, keys, id_generator=None)
|
||||
|
||||
@classmethod
|
||||
def _get_url_name(cls, node):
|
||||
@@ -423,9 +423,7 @@ class XmlParserMixin(object):
|
||||
|
||||
@classmethod
|
||||
def _format_filepath(cls, category, name):
|
||||
return u'{category}/{name}.{ext}'.format(category=category,
|
||||
name=name,
|
||||
ext=cls.filename_extension)
|
||||
return f'{category}/{name}.{cls.filename_extension}'
|
||||
|
||||
def export_to_file(self):
|
||||
"""If this returns True, write the definition of this descriptor to a separate
|
||||
@@ -472,7 +470,7 @@ class XmlParserMixin(object):
|
||||
xml_object.set(attr, val)
|
||||
except Exception: # lint-amnesty, pylint: disable=broad-except
|
||||
logging.exception(
|
||||
u'Failed to serialize metadata attribute %s with value %s in module %s. This could mean data loss!!!', # lint-amnesty, pylint: disable=line-too-long
|
||||
'Failed to serialize metadata attribute %s with value %s in module %s. This could mean data loss!!!', # lint-amnesty, pylint: disable=line-too-long
|
||||
attr, val, self.url_name
|
||||
)
|
||||
|
||||
@@ -520,7 +518,7 @@ class XmlParserMixin(object):
|
||||
"""
|
||||
Return a list of all metadata fields that cannot be edited.
|
||||
"""
|
||||
non_editable_fields = super(XmlParserMixin, self).non_editable_metadata_fields # lint-amnesty, pylint: disable=super-with-arguments
|
||||
non_editable_fields = super().non_editable_metadata_fields
|
||||
non_editable_fields.append(XmlParserMixin.xml_attributes)
|
||||
return non_editable_fields
|
||||
|
||||
@@ -551,7 +549,7 @@ class XmlMixin(XmlParserMixin): # lint-amnesty, pylint: disable=abstract-method
|
||||
# This only exists to satisfy subclasses that both:
|
||||
# a) define from_xml themselves
|
||||
# b) call super(..).from_xml(..)
|
||||
return super(XmlMixin, cls).parse_xml(
|
||||
return super().parse_xml(
|
||||
etree.fromstring(xml_data),
|
||||
system,
|
||||
None, # This is ignored by XmlParserMixin
|
||||
@@ -568,7 +566,7 @@ class XmlMixin(XmlParserMixin): # lint-amnesty, pylint: disable=abstract-method
|
||||
# from XModuleDescriptor, which actually calls `from_xml`.
|
||||
return super(XmlParserMixin, cls).parse_xml(node, runtime, keys, id_generator) # pylint: disable=bad-super-call
|
||||
else:
|
||||
return super(XmlMixin, cls).parse_xml(node, runtime, keys, id_generator)
|
||||
return super().parse_xml(node, runtime, keys, id_generator)
|
||||
|
||||
@classmethod
|
||||
def parse_xml_new_runtime(cls, node, runtime, keys):
|
||||
@@ -577,9 +575,9 @@ class XmlMixin(XmlParserMixin): # lint-amnesty, pylint: disable=abstract-method
|
||||
legacy XModule code. Use the "normal" XBlock parsing code.
|
||||
"""
|
||||
try:
|
||||
return super(XmlMixin, cls).parse_xml_new_runtime(node, runtime, keys)
|
||||
return super().parse_xml_new_runtime(node, runtime, keys)
|
||||
except AttributeError:
|
||||
return super(XmlMixin, cls).parse_xml(node, runtime, keys, id_generator=None)
|
||||
return super().parse_xml(node, runtime, keys, id_generator=None)
|
||||
|
||||
def export_to_xml(self, resource_fs): # lint-amnesty, pylint: disable=unused-argument
|
||||
"""
|
||||
@@ -599,7 +597,7 @@ class XmlMixin(XmlParserMixin): # lint-amnesty, pylint: disable=abstract-method
|
||||
# a) define export_to_xml themselves
|
||||
# b) call super(..).export_to_xml(..)
|
||||
node = Element(self.category)
|
||||
super(XmlMixin, self).add_xml_to_node(node) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().add_xml_to_node(node)
|
||||
return etree.tostring(node)
|
||||
|
||||
def add_xml_to_node(self, node):
|
||||
@@ -612,7 +610,7 @@ class XmlMixin(XmlParserMixin): # lint-amnesty, pylint: disable=abstract-method
|
||||
# from XModuleDescriptor, which actually calls `export_to_xml`.
|
||||
super(XmlParserMixin, self).add_xml_to_node(node) # pylint: disable=bad-super-call
|
||||
else:
|
||||
super(XmlMixin, self).add_xml_to_node(node) # lint-amnesty, pylint: disable=super-with-arguments
|
||||
super().add_xml_to_node(node)
|
||||
|
||||
|
||||
class XmlDescriptor(XmlMixin, XModuleDescriptor): # lint-amnesty, pylint: disable=abstract-method
|
||||
|
||||
Reference in New Issue
Block a user