Commit 6893bb35 by Jeremy Bowman

PLAT-1861 Upgrade to pyfilesystem2

parent e063a8bd
...@@ -43,7 +43,7 @@ class CapaHtmlRenderTest(unittest.TestCase): ...@@ -43,7 +43,7 @@ class CapaHtmlRenderTest(unittest.TestCase):
def test_include_html(self): def test_include_html(self):
# Create a test file to include # Create a test file to include
self._create_test_file( self._create_test_file(
'test_include.xml', u'test_include.xml',
'<test>Test include</test>' '<test>Test include</test>'
) )
......
...@@ -45,10 +45,10 @@ class AssetMetadata(object): ...@@ -45,10 +45,10 @@ class AssetMetadata(object):
ASSET_XML_TAG = 'asset' ASSET_XML_TAG = 'asset'
# Top-level directory name in exported course XML which holds asset metadata. # Top-level directory name in exported course XML which holds asset metadata.
EXPORTED_ASSET_DIR = 'assets' EXPORTED_ASSET_DIR = u'assets'
# Filename of all asset metadata exported as XML. # Filename of all asset metadata exported as XML.
EXPORTED_ASSET_FILENAME = 'assets.xml' EXPORTED_ASSET_FILENAME = u'assets.xml'
@contract(asset_id='AssetKey', @contract(asset_id='AssetKey',
pathname='basestring|None', internal_name='basestring|None', pathname='basestring|None', internal_name='basestring|None',
......
...@@ -1002,12 +1002,12 @@ class CourseDescriptor(CourseFields, SequenceDescriptor, LicenseMixin): ...@@ -1002,12 +1002,12 @@ class CourseDescriptor(CourseFields, SequenceDescriptor, LicenseMixin):
policy_dir = None policy_dir = None
url_name = xml_obj.get('url_name', xml_obj.get('slug')) url_name = xml_obj.get('url_name', xml_obj.get('slug'))
if url_name: if url_name:
policy_dir = 'policies/' + url_name policy_dir = u'policies/' + url_name
# Try to load grading policy # Try to load grading policy
paths = ['grading_policy.json'] paths = [u'grading_policy.json']
if policy_dir: if policy_dir:
paths = [policy_dir + '/grading_policy.json'] + paths paths = [policy_dir + u'/grading_policy.json'] + paths
try: try:
policy = json.loads(cls.read_grading_policy(paths, system)) policy = json.loads(cls.read_grading_policy(paths, system))
......
...@@ -7,7 +7,7 @@ import textwrap ...@@ -7,7 +7,7 @@ import textwrap
from datetime import datetime from datetime import datetime
from django.conf import settings from django.conf import settings
from fs.errors import ResourceNotFoundError from fs.errors import ResourceNotFound
from lxml import etree from lxml import etree
from path import Path as path from path import Path as path
from pkg_resources import resource_string from pkg_resources import resource_string
...@@ -236,7 +236,7 @@ class HtmlDescriptor(HtmlBlock, XmlDescriptor, EditingDescriptor): # pylint: di ...@@ -236,7 +236,7 @@ class HtmlDescriptor(HtmlBlock, XmlDescriptor, EditingDescriptor): # pylint: di
) )
base = path(pointer_path).dirname() base = path(pointer_path).dirname()
# log.debug("base = {0}, base.dirname={1}, filename={2}".format(base, base.dirname(), filename)) # log.debug("base = {0}, base.dirname={1}, filename={2}".format(base, base.dirname(), filename))
filepath = "{base}/{name}.html".format(base=base, name=filename) filepath = u"{base}/{name}.html".format(base=base, name=filename)
# log.debug("looking for html file for {0} at {1}".format(location, filepath)) # log.debug("looking for html file for {0} at {1}".format(location, filepath))
# VS[compat] # VS[compat]
...@@ -259,8 +259,8 @@ class HtmlDescriptor(HtmlBlock, XmlDescriptor, EditingDescriptor): # pylint: di ...@@ -259,8 +259,8 @@ class HtmlDescriptor(HtmlBlock, XmlDescriptor, EditingDescriptor): # pylint: di
break break
try: try:
with system.resources_fs.open(filepath) as infile: with system.resources_fs.open(filepath, encoding='utf-8') as infile:
html = infile.read().decode('utf-8') html = infile.read()
# Log a warning if we can't parse the file, but don't error # Log a warning if we can't parse the file, but don't error
if not check_html(html) and len(html) > 0: if not check_html(html) and len(html) > 0:
msg = "Couldn't parse html in {0}, content = {1}".format(filepath, html) msg = "Couldn't parse html in {0}, content = {1}".format(filepath, html)
...@@ -275,7 +275,7 @@ class HtmlDescriptor(HtmlBlock, XmlDescriptor, EditingDescriptor): # pylint: di ...@@ -275,7 +275,7 @@ class HtmlDescriptor(HtmlBlock, XmlDescriptor, EditingDescriptor): # pylint: di
return definition, [] return definition, []
except (ResourceNotFoundError) as err: except ResourceNotFound as err:
msg = 'Unable to load file contents at path {0}: {1} '.format( msg = 'Unable to load file contents at path {0}: {1} '.format(
filepath, err) filepath, err)
# add more info and re-raise # add more info and re-raise
...@@ -295,8 +295,8 @@ class HtmlDescriptor(HtmlBlock, XmlDescriptor, EditingDescriptor): # pylint: di ...@@ -295,8 +295,8 @@ class HtmlDescriptor(HtmlBlock, XmlDescriptor, EditingDescriptor): # pylint: di
pathname=pathname pathname=pathname
) )
resource_fs.makedir(os.path.dirname(filepath), recursive=True, allow_recreate=True) resource_fs.makedirs(os.path.dirname(filepath), recreate=True)
with resource_fs.open(filepath, 'w') as filestream: with resource_fs.open(filepath, 'wb') as filestream:
html_data = self.data.encode('utf-8') html_data = self.data.encode('utf-8')
filestream.write(html_data) filestream.write(html_data)
......
...@@ -34,8 +34,8 @@ new_contract('AssetKey', AssetKey) ...@@ -34,8 +34,8 @@ new_contract('AssetKey', AssetKey)
new_contract('AssetMetadata', AssetMetadata) new_contract('AssetMetadata', AssetMetadata)
new_contract('XBlock', XBlock) new_contract('XBlock', XBlock)
LIBRARY_ROOT = 'library.xml' LIBRARY_ROOT = u'library.xml'
COURSE_ROOT = 'course.xml' COURSE_ROOT = u'course.xml'
# List of names of computed fields on xmodules that are of type usage keys. # List of names of computed fields on xmodules that are of type usage keys.
# This list can be used to determine which fields need to be stripped of # This list can be used to determine which fields need to be stripped of
......
...@@ -39,7 +39,7 @@ COURSE_DATA_NAMES = ( ...@@ -39,7 +39,7 @@ COURSE_DATA_NAMES = (
'split_test_module_draft', 'split_test_module_draft',
) )
EXPORTED_COURSE_DIR_NAME = 'exported_source_course' EXPORTED_COURSE_DIR_NAME = u'exported_source_course'
@ddt.ddt @ddt.ddt
......
...@@ -572,7 +572,7 @@ class TestMongoModuleStore(TestMongoModuleStoreBase): ...@@ -572,7 +572,7 @@ class TestMongoModuleStore(TestMongoModuleStoreBase):
root_dir = path(mkdtemp()) root_dir = path(mkdtemp())
self.addCleanup(shutil.rmtree, root_dir) self.addCleanup(shutil.rmtree, root_dir)
export_course_to_xml(self.draft_store, self.content_store, course_key, root_dir, 'test_export') export_course_to_xml(self.draft_store, self.content_store, course_key, root_dir, u'test_export')
self.assertTrue(path(root_dir / 'test_export/static/images/course_image.jpg').isfile()) self.assertTrue(path(root_dir / 'test_export/static/images/course_image.jpg').isfile())
self.assertTrue(path(root_dir / 'test_export/static/images_course_image.jpg').isfile()) self.assertTrue(path(root_dir / 'test_export/static/images_course_image.jpg').isfile())
...@@ -588,7 +588,7 @@ class TestMongoModuleStore(TestMongoModuleStoreBase): ...@@ -588,7 +588,7 @@ class TestMongoModuleStore(TestMongoModuleStoreBase):
root_dir = path(mkdtemp()) root_dir = path(mkdtemp())
self.addCleanup(shutil.rmtree, root_dir) self.addCleanup(shutil.rmtree, root_dir)
export_course_to_xml(self.draft_store, self.content_store, course.id, root_dir, 'test_export') export_course_to_xml(self.draft_store, self.content_store, course.id, root_dir, u'test_export')
self.assertTrue(path(root_dir / 'test_export/static/just_a_test.jpg').isfile()) self.assertTrue(path(root_dir / 'test_export/static/just_a_test.jpg').isfile())
self.assertFalse(path(root_dir / 'test_export/static/images/course_image.jpg').isfile()) self.assertFalse(path(root_dir / 'test_export/static/images/course_image.jpg').isfile())
...@@ -601,7 +601,7 @@ class TestMongoModuleStore(TestMongoModuleStoreBase): ...@@ -601,7 +601,7 @@ class TestMongoModuleStore(TestMongoModuleStoreBase):
course = self.draft_store.get_course(CourseKey.from_string('edX/simple_with_draft/2012_Fall')) course = self.draft_store.get_course(CourseKey.from_string('edX/simple_with_draft/2012_Fall'))
root_dir = path(mkdtemp()) root_dir = path(mkdtemp())
self.addCleanup(shutil.rmtree, root_dir) self.addCleanup(shutil.rmtree, root_dir)
export_course_to_xml(self.draft_store, self.content_store, course.id, root_dir, 'test_export') export_course_to_xml(self.draft_store, self.content_store, course.id, root_dir, u'test_export')
self.assertFalse(path(root_dir / 'test_export/static/images/course_image.jpg').isfile()) self.assertFalse(path(root_dir / 'test_export/static/images/course_image.jpg').isfile())
self.assertFalse(path(root_dir / 'test_export/static/images_course_image.jpg').isfile()) self.assertFalse(path(root_dir / 'test_export/static/images_course_image.jpg').isfile())
......
...@@ -497,8 +497,8 @@ class DraftPublishedOpBaseTestSetup(OLXFormatChecker, DraftPublishedOpTestCourse ...@@ -497,8 +497,8 @@ class DraftPublishedOpBaseTestSetup(OLXFormatChecker, DraftPublishedOpTestCourse
Setup base class for draft/published/OLX tests. Setup base class for draft/published/OLX tests.
""" """
EXPORTED_COURSE_BEFORE_DIR_NAME = 'exported_course_before' EXPORTED_COURSE_BEFORE_DIR_NAME = u'exported_course_before'
EXPORTED_COURSE_AFTER_DIR_NAME = 'exported_course_after_{}' EXPORTED_COURSE_AFTER_DIR_NAME = u'exported_course_after_{}'
def setUp(self): def setUp(self):
super(DraftPublishedOpBaseTestSetup, self).setUp() super(DraftPublishedOpBaseTestSetup, self).setUp()
......
...@@ -4,6 +4,7 @@ Methods for exporting course data to XML ...@@ -4,6 +4,7 @@ Methods for exporting course data to XML
import logging import logging
from abc import abstractmethod from abc import abstractmethod
from six import text_type
import lxml.etree import lxml.etree
from xblock.fields import Scope, Reference, ReferenceList, ReferenceValueDict from xblock.fields import Scope, Reference, ReferenceList, ReferenceValueDict
from xmodule.contentstore.content import StaticContent from xmodule.contentstore.content import StaticContent
...@@ -42,7 +43,7 @@ def _export_drafts(modulestore, course_key, export_fs, xml_centric_course_key): ...@@ -42,7 +43,7 @@ def _export_drafts(modulestore, course_key, export_fs, xml_centric_course_key):
# Only modules with changes will be exported into the /drafts directory. # Only modules with changes will be exported into the /drafts directory.
draft_modules = [module for module in draft_modules if modulestore.has_changes(module)] draft_modules = [module for module in draft_modules if modulestore.has_changes(module)]
if draft_modules: if draft_modules:
draft_course_dir = export_fs.makeopendir(DRAFT_DIR) draft_course_dir = export_fs.makedir(DRAFT_DIR, recreate=True)
# accumulate tuples of draft_modules and their parents in # accumulate tuples of draft_modules and their parents in
# this list: # this list:
...@@ -118,7 +119,7 @@ class ExportManager(object): ...@@ -118,7 +119,7 @@ class ExportManager(object):
self.contentstore = contentstore self.contentstore = contentstore
self.courselike_key = courselike_key self.courselike_key = courselike_key
self.root_dir = root_dir self.root_dir = root_dir
self.target_dir = target_dir self.target_dir = text_type(target_dir)
@abstractmethod @abstractmethod
def get_key(self): def get_key(self):
...@@ -160,7 +161,7 @@ class ExportManager(object): ...@@ -160,7 +161,7 @@ class ExportManager(object):
# export only the published content # export only the published content
with self.modulestore.branch_setting(ModuleStoreEnum.Branch.published_only, self.courselike_key): with self.modulestore.branch_setting(ModuleStoreEnum.Branch.published_only, self.courselike_key):
courselike = self.get_courselike() courselike = self.get_courselike()
export_fs = courselike.runtime.export_fs = fsm.makeopendir(self.target_dir) export_fs = courselike.runtime.export_fs = fsm.makedir(self.target_dir, recreate=True)
# change all of the references inside the course to use the xml expected key type w/o version & branch # change all of the references inside the course to use the xml expected key type w/o version & branch
xml_centric_courselike_key = self.get_key() xml_centric_courselike_key = self.get_key()
...@@ -196,8 +197,8 @@ class CourseExportManager(ExportManager): ...@@ -196,8 +197,8 @@ class CourseExportManager(ExportManager):
return self.modulestore.get_course(self.courselike_key, depth=None, lazy=False) return self.modulestore.get_course(self.courselike_key, depth=None, lazy=False)
def process_root(self, root, export_fs): def process_root(self, root, export_fs):
with export_fs.open('course.xml', 'w') as course_xml: with export_fs.open(u'course.xml', 'wb') as course_xml:
lxml.etree.ElementTree(root).write(course_xml) lxml.etree.ElementTree(root).write(course_xml, encoding='utf-8')
def process_extra(self, root, courselike, root_courselike_dir, xml_centric_courselike_key, export_fs): def process_extra(self, root, courselike, root_courselike_dir, xml_centric_courselike_key, export_fs):
# Export the modulestore's asset metadata. # Export the modulestore's asset metadata.
...@@ -210,11 +211,11 @@ class CourseExportManager(ExportManager): ...@@ -210,11 +211,11 @@ class CourseExportManager(ExportManager):
# All asset types are exported using the "asset" tag - but their asset type is specified in each asset key. # All asset types are exported using the "asset" tag - but their asset type is specified in each asset key.
asset = lxml.etree.SubElement(asset_root, AssetMetadata.ASSET_XML_TAG) asset = lxml.etree.SubElement(asset_root, AssetMetadata.ASSET_XML_TAG)
asset_md.to_xml(asset) asset_md.to_xml(asset)
with OSFS(asset_dir).open(AssetMetadata.EXPORTED_ASSET_FILENAME, 'w') as asset_xml_file: with OSFS(asset_dir).open(AssetMetadata.EXPORTED_ASSET_FILENAME, 'wb') as asset_xml_file:
lxml.etree.ElementTree(asset_root).write(asset_xml_file) lxml.etree.ElementTree(asset_root).write(asset_xml_file, encoding='utf-8')
# export the static assets # export the static assets
policies_dir = export_fs.makeopendir('policies') policies_dir = export_fs.makedir('policies', recreate=True)
if self.contentstore: if self.contentstore:
self.contentstore.export_all_for_course( self.contentstore.export_all_for_course(
self.courselike_key, self.courselike_key,
...@@ -238,7 +239,7 @@ class CourseExportManager(ExportManager): ...@@ -238,7 +239,7 @@ class CourseExportManager(ExportManager):
output_dir = root_courselike_dir + '/static/images/' output_dir = root_courselike_dir + '/static/images/'
if not os.path.isdir(output_dir): if not os.path.isdir(output_dir):
os.makedirs(output_dir) os.makedirs(output_dir)
with OSFS(output_dir).open('course_image.jpg', 'wb') as course_image_file: with OSFS(output_dir).open(u'course_image.jpg', 'wb') as course_image_file:
course_image_file.write(course_image.data) course_image_file.write(course_image.data)
# export the static tabs # export the static tabs
...@@ -270,16 +271,17 @@ class CourseExportManager(ExportManager): ...@@ -270,16 +271,17 @@ class CourseExportManager(ExportManager):
# Use url_name for split mongo because course_run is not used when loading policies. # Use url_name for split mongo because course_run is not used when loading policies.
course_policy_dir_name = courselike.url_name course_policy_dir_name = courselike.url_name
course_run_policy_dir = policies_dir.makeopendir(course_policy_dir_name) course_run_policy_dir = policies_dir.makedir(course_policy_dir_name, recreate=True)
# export the grading policy # export the grading policy
with course_run_policy_dir.open('grading_policy.json', 'w') as grading_policy: with course_run_policy_dir.open(u'grading_policy.json', 'wb') as grading_policy:
grading_policy.write(dumps(courselike.grading_policy, cls=EdxJSONEncoder, sort_keys=True, indent=4)) grading_policy.write(dumps(courselike.grading_policy, cls=EdxJSONEncoder,
sort_keys=True, indent=4).encode('utf-8'))
# export all of the course metadata in policy.json # export all of the course metadata in policy.json
with course_run_policy_dir.open('policy.json', 'w') as course_policy: with course_run_policy_dir.open(u'policy.json', 'wb') as course_policy:
policy = {'course/' + courselike.location.name: own_metadata(courselike)} policy = {'course/' + courselike.location.name: own_metadata(courselike)}
course_policy.write(dumps(policy, cls=EdxJSONEncoder, sort_keys=True, indent=4)) course_policy.write(dumps(policy, cls=EdxJSONEncoder, sort_keys=True, indent=4).encode('utf-8'))
_export_drafts(self.modulestore, self.courselike_key, export_fs, xml_centric_courselike_key) _export_drafts(self.modulestore, self.courselike_key, export_fs, xml_centric_courselike_key)
...@@ -315,7 +317,7 @@ class LibraryExportManager(ExportManager): ...@@ -315,7 +317,7 @@ class LibraryExportManager(ExportManager):
to ease in duck typing during import. This may be expanded as a useful feature eventually. to ease in duck typing during import. This may be expanded as a useful feature eventually.
""" """
# export the static assets # export the static assets
export_fs.makeopendir('policies') export_fs.makedir('policies', recreate=True)
if self.contentstore: if self.contentstore:
self.contentstore.export_all_for_course( self.contentstore.export_all_for_course(
...@@ -332,7 +334,7 @@ class LibraryExportManager(ExportManager): ...@@ -332,7 +334,7 @@ class LibraryExportManager(ExportManager):
called library.xml. called library.xml.
""" """
# Create the Library.xml file, which acts as the index of all library contents. # Create the Library.xml file, which acts as the index of all library contents.
xml_file = export_fs.open(LIBRARY_ROOT, 'w') xml_file = export_fs.open(LIBRARY_ROOT, 'wb')
xml_file.write(lxml.etree.tostring(root, pretty_print=True, encoding='utf-8')) xml_file.write(lxml.etree.tostring(root, pretty_print=True, encoding='utf-8'))
xml_file.close() xml_file.close()
...@@ -387,19 +389,20 @@ def _export_field_content(xblock_item, item_dir): ...@@ -387,19 +389,20 @@ def _export_field_content(xblock_item, item_dir):
for field_name in module_data: for field_name in module_data:
if field_name not in DEFAULT_CONTENT_FIELDS: if field_name not in DEFAULT_CONTENT_FIELDS:
# filename format: {dirname}.{field_name}.json # filename format: {dirname}.{field_name}.json
with item_dir.open('{0}.{1}.{2}'.format(xblock_item.location.name, field_name, 'json'), with item_dir.open(u'{0}.{1}.{2}'.format(xblock_item.location.name, field_name, 'json'),
'w') as field_content_file: 'wb') as field_content_file:
field_content_file.write(dumps(module_data.get(field_name, {}), cls=EdxJSONEncoder, sort_keys=True, indent=4)) field_content_file.write(dumps(module_data.get(field_name, {}), cls=EdxJSONEncoder,
sort_keys=True, indent=4).encode('utf-8'))
def export_extra_content(export_fs, modulestore, source_course_key, dest_course_key, category_type, dirname, file_suffix=''): def export_extra_content(export_fs, modulestore, source_course_key, dest_course_key, category_type, dirname, file_suffix=''):
items = modulestore.get_items(source_course_key, qualifiers={'category': category_type}) items = modulestore.get_items(source_course_key, qualifiers={'category': category_type})
if len(items) > 0: if len(items) > 0:
item_dir = export_fs.makeopendir(dirname) item_dir = export_fs.makedir(dirname, recreate=True)
for item in items: for item in items:
adapt_references(item, dest_course_key, export_fs) adapt_references(item, dest_course_key, export_fs)
with item_dir.open(item.location.name + file_suffix, 'w') as item_file: with item_dir.open(item.location.name + file_suffix, 'wb') as item_file:
item_file.write(item.data.encode('utf8')) item_file.write(item.data.encode('utf8'))
# export content fields other then metadata and data in json format in current directory # export content fields other then metadata and data in json format in current directory
......
...@@ -71,14 +71,14 @@ class RoundTripTestCase(unittest.TestCase): ...@@ -71,14 +71,14 @@ class RoundTripTestCase(unittest.TestCase):
@mock.patch('xmodule.video_module.video_module.edxval_api', None) @mock.patch('xmodule.video_module.video_module.edxval_api', None)
@mock.patch('xmodule.course_module.requests.get') @mock.patch('xmodule.course_module.requests.get')
@ddt.data( @ddt.data(
"toy", u"toy",
"simple", u"simple",
"conditional_and_poll", u"conditional_and_poll",
"conditional", u"conditional",
"self_assessment", u"self_assessment",
"test_exam_registration", u"test_exam_registration",
"word_cloud", u"word_cloud",
"pure_xblock", u"pure_xblock",
) )
@XBlock.register_temp_plugin(PureXBlock, 'pure') @XBlock.register_temp_plugin(PureXBlock, 'pure')
def test_export_roundtrip(self, course_dir, mock_get): def test_export_roundtrip(self, course_dir, mock_get):
...@@ -107,12 +107,12 @@ class RoundTripTestCase(unittest.TestCase): ...@@ -107,12 +107,12 @@ class RoundTripTestCase(unittest.TestCase):
# will still be there. # will still be there.
print "Starting export" print "Starting export"
file_system = OSFS(root_dir) file_system = OSFS(root_dir)
initial_course.runtime.export_fs = file_system.makeopendir(course_dir) initial_course.runtime.export_fs = file_system.makedir(course_dir, recreate=True)
root = lxml.etree.Element('root') root = lxml.etree.Element('root')
initial_course.add_xml_to_node(root) initial_course.add_xml_to_node(root)
with initial_course.runtime.export_fs.open('course.xml', 'w') as course_xml: with initial_course.runtime.export_fs.open('course.xml', 'wb') as course_xml:
lxml.etree.ElementTree(root).write(course_xml) lxml.etree.ElementTree(root).write(course_xml, encoding='utf-8')
print "Starting second import" print "Starting second import"
second_import = XMLModuleStore(root_dir, source_dirs=[course_dir], xblock_mixins=(XModuleMixin,)) second_import = XMLModuleStore(root_dir, source_dirs=[course_dir], xblock_mixins=(XModuleMixin,))
......
...@@ -219,7 +219,7 @@ class ImportTestCase(BaseCourseTestCase): ...@@ -219,7 +219,7 @@ class ImportTestCase(BaseCourseTestCase):
self.assertEqual(node.attrib['org'], ORG) self.assertEqual(node.attrib['org'], ORG)
# Does the course still have unicorns? # Does the course still have unicorns?
with descriptor.runtime.export_fs.open('course/{url_name}.xml'.format(url_name=url_name)) as f: with descriptor.runtime.export_fs.open(u'course/{url_name}.xml'.format(url_name=url_name)) as f:
course_xml = etree.fromstring(f.read()) course_xml = etree.fromstring(f.read())
self.assertEqual(course_xml.attrib['unicorn'], unicorn_color) self.assertEqual(course_xml.attrib['unicorn'], unicorn_color)
...@@ -233,7 +233,7 @@ class ImportTestCase(BaseCourseTestCase): ...@@ -233,7 +233,7 @@ class ImportTestCase(BaseCourseTestCase):
# Does the chapter tag now have a due attribute? # Does the chapter tag now have a due attribute?
# hardcoded path to child # hardcoded path to child
with descriptor.runtime.export_fs.open('chapter/ch.xml') as f: with descriptor.runtime.export_fs.open(u'chapter/ch.xml') as f:
chapter_xml = etree.fromstring(f.read()) chapter_xml = etree.fromstring(f.read())
self.assertEqual(chapter_xml.tag, 'chapter') self.assertEqual(chapter_xml.tag, 'chapter')
self.assertNotIn('due', chapter_xml.attrib) self.assertNotIn('due', chapter_xml.attrib)
......
...@@ -475,8 +475,8 @@ class XmlParserMixin(object): ...@@ -475,8 +475,8 @@ class XmlParserMixin(object):
# Write the definition to a file # Write the definition to a file
url_path = name_to_pathname(self.url_name) url_path = name_to_pathname(self.url_name)
filepath = self._format_filepath(self.category, url_path) filepath = self._format_filepath(self.category, url_path)
self.runtime.export_fs.makedir(os.path.dirname(filepath), recursive=True, allow_recreate=True) self.runtime.export_fs.makedirs(os.path.dirname(filepath), recreate=True)
with self.runtime.export_fs.open(filepath, 'w') as fileobj: with self.runtime.export_fs.open(filepath, 'wb') as fileobj:
ElementTree(xml_object).write(fileobj, pretty_print=True, encoding='utf-8') ElementTree(xml_object).write(fileobj, pretty_print=True, encoding='utf-8')
else: else:
# Write all attributes from xml_object onto node # Write all attributes from xml_object onto node
......
...@@ -25,7 +25,7 @@ from django.core.urlresolvers import reverse ...@@ -25,7 +25,7 @@ from django.core.urlresolvers import reverse
from django.http import Http404, QueryDict from django.http import Http404, QueryDict
from enrollment.api import get_course_enrollment_details from enrollment.api import get_course_enrollment_details
from edxmako.shortcuts import render_to_string from edxmako.shortcuts import render_to_string
from fs.errors import ResourceNotFoundError from fs.errors import ResourceNotFound
from lms.djangoapps.courseware.courseware_access_exception import CoursewareAccessException from lms.djangoapps.courseware.courseware_access_exception import CoursewareAccessException
from lms.djangoapps.courseware.exceptions import CourseAccessRedirect from lms.djangoapps.courseware.exceptions import CourseAccessRedirect
from opaque_keys.edx.keys import UsageKey from opaque_keys.edx.keys import UsageKey
...@@ -207,13 +207,13 @@ def find_file(filesystem, dirs, filename): ...@@ -207,13 +207,13 @@ def find_file(filesystem, dirs, filename):
dirs: a list of path objects dirs: a list of path objects
filename: a string filename: a string
Returns d / filename if found in dir d, else raises ResourceNotFoundError. Returns d / filename if found in dir d, else raises ResourceNotFound.
""" """
for directory in dirs: for directory in dirs:
filepath = path(directory) / filename filepath = path(directory) / filename
if filesystem.exists(filepath): if filesystem.exists(filepath):
return filepath return filepath
raise ResourceNotFoundError(u"Could not find {0}".format(filename)) raise ResourceNotFound(u"Could not find {0}".format(filename))
def get_course_about_section(request, course, section_key): def get_course_about_section(request, course, section_key):
...@@ -419,7 +419,7 @@ def get_course_syllabus_section(course, section_key): ...@@ -419,7 +419,7 @@ def get_course_syllabus_section(course, section_key):
course_id=course.id, course_id=course.id,
static_asset_path=course.static_asset_path, static_asset_path=course.static_asset_path,
) )
except ResourceNotFoundError: except ResourceNotFound:
log.exception( log.exception(
u"Missing syllabus section %s in course %s", u"Missing syllabus section %s in course %s",
section_key, course.location.to_deprecated_string() section_key, course.location.to_deprecated_string()
......
...@@ -4,12 +4,15 @@ ...@@ -4,12 +4,15 @@
# * @edx/ospr - to check licensing # * @edx/ospr - to check licensing
# * @edx/devops - to check system requirements # * @edx/devops - to check system requirements
appdirs==1.4.3
attrs==17.2.0 attrs==17.2.0
beautifulsoup4==4.1.3 beautifulsoup4==4.1.3
beautifulsoup==3.2.1 beautifulsoup==3.2.1
bleach==1.4 bleach==1.4
html5lib==0.999 html5lib==0.999
boto==2.39.0 boto==2.39.0
boto3==1.4.8
botocore==1.8.17
celery==3.1.18 celery==3.1.18
cryptography==1.9 cryptography==1.9
cssselect==0.9.1 cssselect==0.9.1
...@@ -25,7 +28,7 @@ django-model-utils==3.0.0 ...@@ -25,7 +28,7 @@ django-model-utils==3.0.0
django-mptt>=0.8.6,<0.9 django-mptt>=0.8.6,<0.9
django-oauth-toolkit==0.12.0 django-oauth-toolkit==0.12.0
django-pipeline-forgiving==1.0.0 django-pipeline-forgiving==1.0.0
django-pyfs==1.0.7 #django-pyfs==1.0.7
django-sekizai>=0.10 django-sekizai>=0.10
django-ses==0.8.4 django-ses==0.8.4
django-simple-history==1.9.0 django-simple-history==1.9.0
...@@ -35,6 +38,7 @@ django-method-override==0.1.0 ...@@ -35,6 +38,7 @@ django-method-override==0.1.0
django-user-tasks==0.1.5 django-user-tasks==0.1.5
django-waffle==0.12.0 django-waffle==0.12.0
djangorestframework-jwt==1.11.0 djangorestframework-jwt==1.11.0
docutils==0.14
enum34==1.1.6 enum34==1.1.6
edx-ace==0.1.6 edx-ace==0.1.6
edx-ccx-keys==0.2.1 edx-ccx-keys==0.2.1
...@@ -59,11 +63,15 @@ edxval==0.1.6 ...@@ -59,11 +63,15 @@ edxval==0.1.6
event-tracking==0.2.4 event-tracking==0.2.4
feedparser==5.1.3 feedparser==5.1.3
firebase-token-generator==1.3.2 firebase-token-generator==1.3.2
fs==2.0.17
fs-s3fs==0.1.5
futures==3.2.0 ; python_version == "2.7"
GitPython==0.3.2.RC1 GitPython==0.3.2.RC1
glob2==0.3 glob2==0.3
gunicorn==0.17.4 gunicorn==0.17.4
help-tokens==1.0.3 help-tokens==1.0.3
httpretty==0.8.3 httpretty==0.8.3
jmespath==0.9.3
lazy==1.1 lazy==1.1
mako==1.0.2 mako==1.0.2
Markdown>=2.6,<2.7 Markdown>=2.6,<2.7
...@@ -95,6 +103,7 @@ pysrt==0.4.7 ...@@ -95,6 +103,7 @@ pysrt==0.4.7
PyYAML==3.12 PyYAML==3.12
requests-oauthlib==0.4.1 requests-oauthlib==0.4.1
rules==1.1.1 rules==1.1.1
s3transfer==0.1.12
scipy==0.14.0 scipy==0.14.0
Shapely==1.2.16 Shapely==1.2.16
singledispatch==3.4.0.2 singledispatch==3.4.0.2
......
...@@ -57,10 +57,8 @@ git+https://github.com/edx/nltk.git@2.0.6#egg=nltk==2.0.6 ...@@ -57,10 +57,8 @@ git+https://github.com/edx/nltk.git@2.0.6#egg=nltk==2.0.6
-e git+https://github.com/jazkarta/edx-jsme.git@690dbf75441fa91c7c4899df0b83d77f7deb5458#egg=edx-jsme -e git+https://github.com/jazkarta/edx-jsme.git@690dbf75441fa91c7c4899df0b83d77f7deb5458#egg=edx-jsme
git+https://github.com/mitodl/django-cas.git@afac57bc523f145ae826f4ed3d4fa8b2c86c5364#egg=django-cas==2.1.1 git+https://github.com/mitodl/django-cas.git@afac57bc523f145ae826f4ed3d4fa8b2c86c5364#egg=django-cas==2.1.1
-e git+https://github.com/dgrtwo/ParsePy.git@7949b9f754d1445eff8e8f20d0e967b9a6420639#egg=parse_rest -e git+https://github.com/dgrtwo/ParsePy.git@7949b9f754d1445eff8e8f20d0e967b9a6420639#egg=parse_rest
# Master pyfs has a bug working with VPC auth. This is a fix. We should switch # This will be replaced with an actual release requirement before this PR is merged
# back to master when and if this fix is merged back. git+https://github.com/edx/django-pyfs.git@5296382609a8233272d13f94813cb436f35d1408#egg=django-pyfs==2.0
# fs==0.4.0
git+https://github.com/edx/pyfs.git@96e1922348bfe6d99201b9512a9ed946c87b7e0b#egg=fs==0.4.0
# The officially released version of django-debug-toolbar-mongo doesn't support DJDT 1.x. This commit does. # The officially released version of django-debug-toolbar-mongo doesn't support DJDT 1.x. This commit does.
git+https://github.com/hmarr/django-debug-toolbar-mongo.git@b0686a76f1ce3532088c4aee6e76b9abe61cc808#egg=django-debug-toolbar-mongo==0.1.10 git+https://github.com/hmarr/django-debug-toolbar-mongo.git@b0686a76f1ce3532088c4aee6e76b9abe61cc808#egg=django-debug-toolbar-mongo==0.1.10
...@@ -91,7 +89,7 @@ git+https://github.com/edx/django-celery.git@756cb57aad765cb2b0d37372c1855b8f5f3 ...@@ -91,7 +89,7 @@ git+https://github.com/edx/django-celery.git@756cb57aad765cb2b0d37372c1855b8f5f3
-e git+https://github.com/edx/django-splash.git@v0.2#egg=django-splash==0.2 -e git+https://github.com/edx/django-splash.git@v0.2#egg=django-splash==0.2
-e git+https://github.com/edx/acid-block.git@e46f9cda8a03e121a00c7e347084d142d22ebfb7#egg=acid-xblock -e git+https://github.com/edx/acid-block.git@e46f9cda8a03e121a00c7e347084d142d22ebfb7#egg=acid-xblock
git+https://github.com/edx/edx-ora2.git@2.1.8#egg=ora2==2.1.8 git+https://github.com/edx/edx-ora2.git@2.1.8#egg=ora2==2.1.8
git+https://github.com/edx/RecommenderXBlock.git@0e744b393cf1f8b886fe77bc697e7d9d78d65cd6#egg=recommender-xblock==1.2 git+https://github.com/edx/RecommenderXBlock.git@e0f777c3d2295c28a6f21652b9f1773a1910a8f3#egg=recommender-xblock==1.3
git+https://github.com/solashirai/crowdsourcehinter.git@518605f0a95190949fe77bd39158450639e2e1dc#egg=crowdsourcehinter-xblock==0.1 git+https://github.com/solashirai/crowdsourcehinter.git@518605f0a95190949fe77bd39158450639e2e1dc#egg=crowdsourcehinter-xblock==0.1
-e git+https://github.com/edx/RateXBlock.git@367e19c0f6eac8a5f002fd0f1559555f8e74bfff#egg=rate-xblock -e git+https://github.com/edx/RateXBlock.git@367e19c0f6eac8a5f002fd0f1559555f8e74bfff#egg=rate-xblock
-e git+https://github.com/edx/DoneXBlock.git@01a14f3bd80ae47dd08cdbbe2f88f3eb88d00fba#egg=done-xblock -e git+https://github.com/edx/DoneXBlock.git@01a14f3bd80ae47dd08cdbbe2f88f3eb88d00fba#egg=done-xblock
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment