INCR-416 python3 compatibility
This commit is contained in:
@@ -7,6 +7,8 @@ Run like this:
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import inspect
|
||||
import json
|
||||
import os
|
||||
@@ -14,17 +16,18 @@ import pprint
|
||||
import sys
|
||||
import traceback
|
||||
import unittest
|
||||
|
||||
from contextlib import contextmanager, nested
|
||||
from django.test import TestCase
|
||||
from functools import wraps
|
||||
|
||||
import six
|
||||
from django.test import TestCase
|
||||
from mock import Mock
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from path import Path as path
|
||||
from six import text_type
|
||||
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from xblock.field_data import DictFieldData
|
||||
from xblock.fields import ScopeIds, Reference, ReferenceList, ReferenceValueDict
|
||||
from xblock.fields import Reference, ReferenceList, ReferenceValueDict, ScopeIds
|
||||
|
||||
from xmodule.assetstore import AssetMetadata
|
||||
from xmodule.error_module import ErrorDescriptor
|
||||
from xmodule.mako_module import MakoDescriptorSystem
|
||||
@@ -53,7 +56,7 @@ class TestModuleSystem(ModuleSystem): # pylint: disable=abstract-method
|
||||
|
||||
def handler_url(self, block, handler, suffix='', query='', thirdparty=False):
|
||||
return '{usage_id}/{handler}{suffix}?{query}'.format(
|
||||
usage_id=unicode(block.scope_ids.usage_id),
|
||||
usage_id=six.text_type(block.scope_ids.usage_id),
|
||||
handler=handler,
|
||||
suffix=suffix,
|
||||
query=query,
|
||||
@@ -61,7 +64,7 @@ class TestModuleSystem(ModuleSystem): # pylint: disable=abstract-method
|
||||
|
||||
def local_resource_url(self, block, uri):
|
||||
return 'resource/{usage_id}/{uri}'.format(
|
||||
usage_id=unicode(block.scope_ids.usage_id),
|
||||
usage_id=six.text_type(block.scope_ids.usage_id),
|
||||
uri=uri,
|
||||
)
|
||||
|
||||
@@ -218,7 +221,7 @@ def map_references(value, field, actual_course_key):
|
||||
if isinstance(field, ReferenceList):
|
||||
return [sub.map_into_course(actual_course_key) for sub in value]
|
||||
if isinstance(field, ReferenceValueDict):
|
||||
return {key: ele.map_into_course(actual_course_key) for key, ele in value.iteritems()}
|
||||
return {key: ele.map_into_course(actual_course_key) for key, ele in six.iteritems(value)}
|
||||
return value
|
||||
|
||||
|
||||
@@ -388,13 +391,13 @@ class LazyFormat(object):
|
||||
return self._message
|
||||
|
||||
def __repr__(self):
|
||||
return unicode(self)
|
||||
return six.text_type(self)
|
||||
|
||||
def __len__(self):
|
||||
return len(unicode(self))
|
||||
return len(six.text_type(self))
|
||||
|
||||
def __getitem__(self, index):
|
||||
return unicode(self)[index]
|
||||
return six.text_type(self)[index]
|
||||
|
||||
|
||||
class CourseComparisonTest(BulkAssertionTest):
|
||||
@@ -446,8 +449,8 @@ class CourseComparisonTest(BulkAssertionTest):
|
||||
expected = [extract_key(key) for key in expected]
|
||||
actual = [extract_key(key) for key in actual]
|
||||
elif isinstance(reference_field, ReferenceValueDict):
|
||||
expected = {key: extract_key(val) for (key, val) in expected.iteritems()}
|
||||
actual = {key: extract_key(val) for (key, val) in actual.iteritems()}
|
||||
expected = {key: extract_key(val) for (key, val) in six.iteritems(expected)}
|
||||
actual = {key: extract_key(val) for (key, val) in six.iteritems(actual)}
|
||||
self.assertEqual(
|
||||
expected,
|
||||
actual,
|
||||
@@ -560,7 +563,7 @@ class CourseComparisonTest(BulkAssertionTest):
|
||||
actual_item = actual_item_map.get(map_key(actual_item_location))
|
||||
|
||||
# Formatting the message slows down tests of large courses significantly, so only do it if it would be used
|
||||
self.assertIn(map_key(actual_item_location), actual_item_map.keys())
|
||||
self.assertIn(map_key(actual_item_location), list(actual_item_map.keys()))
|
||||
|
||||
if actual_item is None:
|
||||
continue
|
||||
@@ -568,7 +571,7 @@ class CourseComparisonTest(BulkAssertionTest):
|
||||
# compare fields
|
||||
self.assertEqual(expected_item.fields, actual_item.fields)
|
||||
|
||||
for field_name, field in expected_item.fields.iteritems():
|
||||
for field_name, field in six.iteritems(expected_item.fields):
|
||||
if (expected_item.scope_ids.usage_id, field_name) in self.field_exclusions:
|
||||
continue
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
"""
|
||||
This test will run for annotator_token.py
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import unittest
|
||||
|
||||
from xmodule.annotator_token import retrieve_token
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
from __future__ import print_function
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import json
|
||||
import unittest
|
||||
|
||||
from fs.memoryfs import MemoryFS
|
||||
from lxml import etree
|
||||
from mock import Mock, patch
|
||||
from six import text_type
|
||||
|
||||
from xblock.field_data import DictFieldData
|
||||
from xblock.fields import ScopeIds
|
||||
from xmodule.error_module import NonStaffErrorDescriptor
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
|
||||
from xmodule.modulestore.xml import ImportSystem, XMLModuleStore, CourseLocationManager
|
||||
from xmodule.conditional_module import ConditionalDescriptor
|
||||
from xmodule.tests import DATA_DIR, get_test_system, get_test_descriptor_system
|
||||
from xmodule.tests.xml import factories as xml, XModuleXmlImportTest
|
||||
from xmodule.validation import StudioValidationMessage
|
||||
from xmodule.x_module import STUDENT_VIEW, AUTHOR_VIEW
|
||||
from six import text_type
|
||||
from web_fragments.fragment import Fragment
|
||||
from xblock.field_data import DictFieldData
|
||||
from xblock.fields import ScopeIds
|
||||
|
||||
from xmodule.conditional_module import ConditionalDescriptor
|
||||
from xmodule.error_module import NonStaffErrorDescriptor
|
||||
from xmodule.modulestore.xml import CourseLocationManager, ImportSystem, XMLModuleStore
|
||||
from xmodule.tests import DATA_DIR, get_test_descriptor_system, get_test_system
|
||||
from xmodule.tests.xml import XModuleXmlImportTest
|
||||
from xmodule.tests.xml import factories as xml
|
||||
from xmodule.validation import StudioValidationMessage
|
||||
from xmodule.x_module import AUTHOR_VIEW, STUDENT_VIEW
|
||||
|
||||
ORG = 'test_org'
|
||||
COURSE = 'conditional' # name of directory with course data
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
"""
|
||||
Tests for ErrorModule and NonStaffErrorModule
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import unittest
|
||||
from xmodule.tests import get_test_system
|
||||
from xmodule.error_module import ErrorDescriptor, ErrorModule, NonStaffErrorDescriptor
|
||||
from xmodule.modulestore.xml import CourseLocationManager
|
||||
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
|
||||
from xmodule.x_module import XModuleDescriptor, XModule, STUDENT_VIEW
|
||||
|
||||
from mock import MagicMock, Mock, patch
|
||||
from xblock.runtime import Runtime, IdReader
|
||||
from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator
|
||||
from xblock.field_data import DictFieldData
|
||||
from xblock.fields import ScopeIds
|
||||
from xblock.runtime import IdReader, Runtime
|
||||
from xblock.test.tools import unabc
|
||||
|
||||
from xmodule.error_module import ErrorDescriptor, ErrorModule, NonStaffErrorDescriptor
|
||||
from xmodule.modulestore.xml import CourseLocationManager
|
||||
from xmodule.tests import get_test_system
|
||||
from xmodule.x_module import STUDENT_VIEW, XModule, XModuleDescriptor
|
||||
|
||||
|
||||
class SetupTestErrorModules(unittest.TestCase):
|
||||
"""Common setUp for use in ErrorModule tests."""
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
"""
|
||||
Tests that check that we ignore the appropriate files when importing courses.
|
||||
"""
|
||||
import unittest
|
||||
from mock import Mock
|
||||
from __future__ import absolute_import
|
||||
|
||||
from xmodule.modulestore.xml_importer import StaticContentImporter
|
||||
import unittest
|
||||
|
||||
from mock import Mock
|
||||
from opaque_keys.edx.locator import CourseLocator
|
||||
from xmodule.tests import DATA_DIR
|
||||
|
||||
from xmodule.modulestore.tests.utils import (
|
||||
add_temp_files_from_dict, remove_temp_files_from_list, DOT_FILES_DICT, TILDA_FILES_DICT
|
||||
DOT_FILES_DICT,
|
||||
TILDA_FILES_DICT,
|
||||
add_temp_files_from_dict,
|
||||
remove_temp_files_from_list
|
||||
)
|
||||
from xmodule.modulestore.xml_importer import StaticContentImporter
|
||||
from xmodule.tests import DATA_DIR
|
||||
|
||||
|
||||
class IgnoredFilesTestCase(unittest.TestCase):
|
||||
@@ -20,7 +26,7 @@ class IgnoredFilesTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
super(IgnoredFilesTestCase, self).setUp()
|
||||
for dictionary in self.dict_list:
|
||||
self.addCleanup(remove_temp_files_from_list, dictionary.keys(), self.course_dir / "static")
|
||||
self.addCleanup(remove_temp_files_from_list, list(dictionary.keys()), self.course_dir / "static")
|
||||
add_temp_files_from_dict(dictionary, self.course_dir / "static")
|
||||
|
||||
def test_sample_static_files(self):
|
||||
|
||||
@@ -4,21 +4,24 @@ Basic unit tests for LibraryContentModule
|
||||
|
||||
Higher-level tests are in `cms/djangoapps/contentstore/tests/test_libraries.py`.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
import six
|
||||
from bson.objectid import ObjectId
|
||||
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
|
||||
|
||||
from xmodule.library_content_module import ANY_CAPA_TYPE_VALUE, LibraryContentDescriptor
|
||||
from xmodule.library_tools import LibraryToolsService
|
||||
from xmodule.modulestore import ModuleStoreEnum
|
||||
from xmodule.modulestore.tests.factories import LibraryFactory, CourseFactory
|
||||
from xmodule.modulestore.tests.factories import CourseFactory, LibraryFactory
|
||||
from xmodule.modulestore.tests.utils import MixedSplitTestCase
|
||||
from xmodule.tests import get_test_system
|
||||
from xmodule.validation import StudioValidationMessage
|
||||
from xmodule.x_module import AUTHOR_VIEW
|
||||
from search.search_engine_base import SearchEngine
|
||||
|
||||
dummy_render = lambda block, _: Fragment(block.data) # pylint: disable=invalid-name
|
||||
|
||||
@@ -44,7 +47,7 @@ class LibraryContentTest(MixedSplitTestCase):
|
||||
"library_content",
|
||||
self.vertical,
|
||||
max_count=1,
|
||||
source_library_id=unicode(self.library.location.library_key)
|
||||
source_library_id=six.text_type(self.library.location.library_key)
|
||||
)
|
||||
|
||||
def _bind_course_module(self, module):
|
||||
@@ -149,7 +152,7 @@ class LibraryContentModuleTestMixin(object):
|
||||
self.assertIn("invalid", 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 = unicode(self.library.location.library_key)
|
||||
self.lc_block.source_library_id = six.text_type(self.library.location.library_key)
|
||||
result = self.lc_block.validate()
|
||||
self.assertFalse(result) # Validation fails due to at least one warning/message
|
||||
self.assertTrue(result.summary)
|
||||
@@ -365,7 +368,7 @@ class TestLibraryContentAnalytics(LibraryContentTest):
|
||||
self.assertTrue(len(self.publisher.call_args[0]), 3)
|
||||
_, event_name, event_data = self.publisher.call_args[0]
|
||||
self.assertEqual(event_name, "edx.librarycontentblock.content.{}".format(event_type))
|
||||
self.assertEqual(event_data["location"], unicode(self.lc_block.location))
|
||||
self.assertEqual(event_data["location"], six.text_type(self.lc_block.location))
|
||||
return event_data
|
||||
|
||||
def test_assigned_event(self):
|
||||
@@ -378,13 +381,13 @@ class TestLibraryContentAnalytics(LibraryContentTest):
|
||||
self.assertIsInstance(child_lib_version, ObjectId)
|
||||
event_data = self._assert_event_was_published("assigned")
|
||||
block_info = {
|
||||
"usage_key": unicode(child.location),
|
||||
"original_usage_key": unicode(child_lib_location),
|
||||
"original_usage_version": unicode(child_lib_version),
|
||||
"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),
|
||||
"descendants": [],
|
||||
}
|
||||
self.assertEqual(event_data, {
|
||||
"location": unicode(self.lc_block.location),
|
||||
"location": six.text_type(self.lc_block.location),
|
||||
"added": [block_info],
|
||||
"result": [block_info],
|
||||
"previous_count": 0,
|
||||
@@ -400,7 +403,7 @@ class TestLibraryContentAnalytics(LibraryContentTest):
|
||||
self.assertEqual(len(children), 2)
|
||||
child, new_child = children if children[0].location == child.location else reversed(children)
|
||||
event_data = self._assert_event_was_published("assigned")
|
||||
self.assertEqual(event_data["added"][0]["usage_key"], unicode(new_child.location))
|
||||
self.assertEqual(event_data["added"][0]["usage_key"], six.text_type(new_child.location))
|
||||
self.assertEqual(len(event_data["result"]), 2)
|
||||
self.assertEqual(event_data["previous_count"], 1)
|
||||
self.assertEqual(event_data["max_count"], 2)
|
||||
@@ -447,7 +450,7 @@ class TestLibraryContentAnalytics(LibraryContentTest):
|
||||
|
||||
for block_list in (event_data["added"], event_data["result"]):
|
||||
self.assertEqual(len(block_list), 1) # main_vertical is the only root block added, and is the only result.
|
||||
self.assertEqual(block_list[0]["usage_key"], unicode(course_usage_main_vertical))
|
||||
self.assertEqual(block_list[0]["usage_key"], six.text_type(course_usage_main_vertical))
|
||||
|
||||
# Check that "descendants" is a flat, unordered list of all of main_vertical's descendants:
|
||||
descendants_expected = (
|
||||
@@ -457,10 +460,10 @@ class TestLibraryContentAnalytics(LibraryContentTest):
|
||||
)
|
||||
descendant_data_expected = {}
|
||||
for lib_key, course_usage_key in descendants_expected:
|
||||
descendant_data_expected[unicode(course_usage_key)] = {
|
||||
"usage_key": unicode(course_usage_key),
|
||||
"original_usage_key": unicode(lib_key),
|
||||
"original_usage_version": unicode(self.store.get_block_original_usage(course_usage_key)[1]),
|
||||
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]),
|
||||
}
|
||||
self.assertEqual(len(block_list[0]["descendants"]), len(descendant_data_expected))
|
||||
for descendant in block_list[0]["descendants"]:
|
||||
@@ -517,16 +520,16 @@ class TestLibraryContentAnalytics(LibraryContentTest):
|
||||
self.assertEqual(len(children), 1)
|
||||
event_data = self._assert_event_was_published("removed")
|
||||
self.assertEqual(event_data["removed"], [{
|
||||
"usage_key": unicode(deleted_block_key),
|
||||
"usage_key": six.text_type(deleted_block_key),
|
||||
"original_usage_key": None, # Note: original_usage_key info is sadly unavailable because the block has been
|
||||
# deleted so that info can no longer be retrieved
|
||||
"original_usage_version": None,
|
||||
"descendants": [],
|
||||
}])
|
||||
self.assertEqual(event_data["result"], [{
|
||||
"usage_key": unicode(keep_block_key),
|
||||
"original_usage_key": unicode(keep_block_lib_usage_key),
|
||||
"original_usage_version": unicode(keep_block_lib_version),
|
||||
"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": [],
|
||||
}])
|
||||
self.assertEqual(event_data["reason"], "invalid")
|
||||
|
||||
@@ -2,17 +2,23 @@
|
||||
Tests for sequence module.
|
||||
"""
|
||||
# pylint: disable=no-member
|
||||
import json
|
||||
from __future__ import absolute_import
|
||||
|
||||
import json
|
||||
from datetime import timedelta
|
||||
|
||||
import ddt
|
||||
import six
|
||||
from django.utils.timezone import now
|
||||
from freezegun import freeze_time
|
||||
from mock import Mock, patch
|
||||
from six.moves import range
|
||||
|
||||
from xmodule.seq_module import SequenceModule
|
||||
from xmodule.tests import get_test_system
|
||||
from xmodule.tests.helpers import StubUserService
|
||||
from xmodule.tests.xml import factories as xml, XModuleXmlImportTest
|
||||
from xmodule.tests.xml import XModuleXmlImportTest
|
||||
from xmodule.tests.xml import factories as xml
|
||||
from xmodule.x_module import PUBLIC_VIEW, STUDENT_VIEW
|
||||
|
||||
TODAY = now()
|
||||
@@ -136,7 +142,7 @@ class SequenceBlockTestCase(XModuleXmlImportTest):
|
||||
view=view
|
||||
)
|
||||
self._assert_view_at_position(html, expected_position=1)
|
||||
self.assertIn(unicode(self.sequence_3_1.location), html)
|
||||
self.assertIn(six.text_type(self.sequence_3_1.location), html)
|
||||
self.assertIn("'gated': False", html)
|
||||
self.assertIn("'next_url': 'NextSequential'", html)
|
||||
self.assertIn("'prev_url': 'PrevSequential'", html)
|
||||
@@ -222,7 +228,7 @@ class SequenceBlockTestCase(XModuleXmlImportTest):
|
||||
self.assertIn("'gated': True", html)
|
||||
self.assertIn("'prereq_url': 'PrereqUrl'", html)
|
||||
self.assertIn("'prereq_section_name': 'PrereqSectionName'", html)
|
||||
self.assertIn("'gated_section_name': u'{}'".format(unicode(sequence.display_name)), html)
|
||||
self.assertIn("'gated_section_name': u'{}'".format(six.text_type(sequence.display_name)), html)
|
||||
self.assertIn("'next_url': 'NextSequential'", html)
|
||||
self.assertIn("'prev_url': 'PrevSequential'", html)
|
||||
|
||||
@@ -237,7 +243,7 @@ class SequenceBlockTestCase(XModuleXmlImportTest):
|
||||
html
|
||||
)
|
||||
self.assertIn("'gated': False", html)
|
||||
self.assertIn(unicode(sequence.location), html)
|
||||
self.assertIn(six.text_type(sequence.location), html)
|
||||
self.assertIn("'prereq_url': None", html)
|
||||
self.assertIn("'prereq_section_name': None", html)
|
||||
self.assertIn("'next_url': 'NextSequential'", html)
|
||||
@@ -250,7 +256,7 @@ class SequenceBlockTestCase(XModuleXmlImportTest):
|
||||
self.assertIn("seq_module.html", html)
|
||||
self.assertIn("'banner_text': None", html)
|
||||
self.assertIn("'gated': False", html)
|
||||
self.assertIn(unicode(sequence.location), html)
|
||||
self.assertIn(six.text_type(sequence.location), html)
|
||||
self.assertIn("'prereq_url': None", html)
|
||||
self.assertIn("'prereq_section_name': None", html)
|
||||
self.assertIn("'next_url': 'NextSequential'", html)
|
||||
@@ -312,7 +318,7 @@ class SequenceBlockTestCase(XModuleXmlImportTest):
|
||||
targeted vertical through ajax call
|
||||
"""
|
||||
for child in self.sequence_3_1.get_children():
|
||||
usage_key = unicode(child.location)
|
||||
usage_key = six.text_type(child.location)
|
||||
completion_return = json.loads(self.sequence_3_1.handle_ajax(
|
||||
'get_completion',
|
||||
{'usage_key': usage_key}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
Tests for StudioEditableModule.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from xmodule.tests.test_vertical import BaseVerticalBlockTest
|
||||
from xmodule.x_module import AUTHOR_VIEW
|
||||
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
Test xblock/validation.py
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import unittest
|
||||
|
||||
import pytest
|
||||
|
||||
from xmodule.validation import StudioValidationMessage, StudioValidation
|
||||
from xblock.validation import Validation, ValidationMessage
|
||||
|
||||
from xmodule.validation import StudioValidation, StudioValidationMessage
|
||||
|
||||
|
||||
class StudioValidationMessageTest(unittest.TestCase):
|
||||
"""
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
# disable missing docstring
|
||||
# pylint: disable=missing-docstring
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import unittest
|
||||
|
||||
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 Scope, String, Dict, Boolean, Integer, Float, Any, List
|
||||
from xblock.runtime import KvsFieldData, DictKeyValueStore
|
||||
from xblock.fields import Any, Boolean, Dict, Float, Integer, List, Scope, String
|
||||
from xblock.runtime import DictKeyValueStore, KvsFieldData
|
||||
|
||||
from xmodule.fields import Date, Timedelta, RelativeTime
|
||||
from xmodule.course_module import CourseDescriptor
|
||||
from xmodule.fields import Date, RelativeTime, Timedelta
|
||||
from xmodule.modulestore.inheritance import InheritanceKeyValueStore, InheritanceMixin, InheritingFieldData
|
||||
from xmodule.modulestore.split_mongo.split_mongo_kvs import SplitMongoKVS
|
||||
from xmodule.xml_module import XmlDescriptor, serialize_field, deserialize_field
|
||||
from xmodule.course_module import CourseDescriptor
|
||||
from xmodule.seq_module import SequenceDescriptor
|
||||
from xmodule.x_module import XModuleMixin
|
||||
|
||||
from xmodule.tests import get_test_descriptor_system
|
||||
from xmodule.tests.xml import XModuleXmlImportTest
|
||||
from xmodule.tests.xml.factories import CourseFactory, SequenceFactory, ProblemFactory
|
||||
from xmodule.tests.xml.factories import CourseFactory, ProblemFactory, SequenceFactory
|
||||
from xmodule.x_module import XModuleMixin
|
||||
from xmodule.xml_module import XmlDescriptor, deserialize_field, serialize_field
|
||||
|
||||
|
||||
class CrazyJsonString(String):
|
||||
|
||||
Reference in New Issue
Block a user