Commit 6893bb35 by Jeremy Bowman

PLAT-1861 Upgrade to pyfilesystem2

parent e063a8bd
...@@ -23,6 +23,7 @@ from opaque_keys import InvalidKeyError ...@@ -23,6 +23,7 @@ from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey, UsageKey from opaque_keys.edx.keys import CourseKey, UsageKey
from opaque_keys.edx.locations import AssetLocation, CourseLocator from opaque_keys.edx.locations import AssetLocation, CourseLocator
from path import Path as path from path import Path as path
from six import text_type
from waffle.testutils import override_switch from waffle.testutils import override_switch
from contentstore.tests.utils import AjaxEnabledTestClient, CourseTestCase, get_url, parse_json from contentstore.tests.utils import AjaxEnabledTestClient, CourseTestCase, get_url, parse_json
...@@ -117,7 +118,7 @@ class ImportRequiredTestCases(ContentStoreTestCase): ...@@ -117,7 +118,7 @@ class ImportRequiredTestCases(ContentStoreTestCase):
Asset name in XML: "/invalid\\displayname/subs-esLhHcdKGWvKs.srt" Asset name in XML: "/invalid\\displayname/subs-esLhHcdKGWvKs.srt"
""" """
content_store = contentstore() content_store = contentstore()
expected_displayname = '_invalid_displayname_subs-esLhHcdKGWvKs.srt' expected_displayname = u'_invalid_displayname_subs-esLhHcdKGWvKs.srt'
import_course_from_xml( import_course_from_xml(
self.store, self.store,
...@@ -156,10 +157,10 @@ class ImportRequiredTestCases(ContentStoreTestCase): ...@@ -156,10 +157,10 @@ class ImportRequiredTestCases(ContentStoreTestCase):
# Test course export does not fail # Test course export does not fail
root_dir = path(mkdtemp_clean()) root_dir = path(mkdtemp_clean())
print 'Exporting to tempdir = {0}'.format(root_dir) print 'Exporting to tempdir = {0}'.format(root_dir)
export_course_to_xml(self.store, content_store, course.id, root_dir, 'test_export') export_course_to_xml(self.store, content_store, course.id, root_dir, u'test_export')
filesystem = OSFS(root_dir / 'test_export/static') filesystem = OSFS(text_type(root_dir / 'test_export/static'))
exported_static_files = filesystem.listdir() exported_static_files = filesystem.listdir(u'/')
# Verify that asset have been overwritten during export. # Verify that asset have been overwritten during export.
self.assertEqual(len(exported_static_files), 1) self.assertEqual(len(exported_static_files), 1)
...@@ -236,18 +237,18 @@ class ImportRequiredTestCases(ContentStoreTestCase): ...@@ -236,18 +237,18 @@ class ImportRequiredTestCases(ContentStoreTestCase):
self.assertIsNotNone(course_updates) self.assertIsNotNone(course_updates)
# check that course which is imported has files 'updates.html' and 'updates.items.json' # check that course which is imported has files 'updates.html' and 'updates.items.json'
filesystem = OSFS(data_dir + '/course_info_updates/info') filesystem = OSFS(text_type(data_dir + '/course_info_updates/info'))
self.assertTrue(filesystem.exists('updates.html')) self.assertTrue(filesystem.exists(u'updates.html'))
self.assertTrue(filesystem.exists('updates.items.json')) self.assertTrue(filesystem.exists(u'updates.items.json'))
# verify that course info update module has same data content as in data file from which it is imported # verify that course info update module has same data content as in data file from which it is imported
# check 'data' field content # check 'data' field content
with filesystem.open('updates.html', 'r') as course_policy: with filesystem.open(u'updates.html', 'r') as course_policy:
on_disk = course_policy.read() on_disk = course_policy.read()
self.assertEqual(course_updates.data, on_disk) self.assertEqual(course_updates.data, on_disk)
# check 'items' field content # check 'items' field content
with filesystem.open('updates.items.json', 'r') as course_policy: with filesystem.open(u'updates.items.json', 'r') as course_policy:
on_disk = loads(course_policy.read()) on_disk = loads(course_policy.read())
self.assertEqual(course_updates.items, on_disk) self.assertEqual(course_updates.items, on_disk)
...@@ -255,19 +256,19 @@ class ImportRequiredTestCases(ContentStoreTestCase): ...@@ -255,19 +256,19 @@ class ImportRequiredTestCases(ContentStoreTestCase):
# with same content as in course 'info' directory # with same content as in course 'info' directory
root_dir = path(mkdtemp_clean()) root_dir = path(mkdtemp_clean())
print 'Exporting to tempdir = {0}'.format(root_dir) print 'Exporting to tempdir = {0}'.format(root_dir)
export_course_to_xml(self.store, content_store, course.id, root_dir, 'test_export') export_course_to_xml(self.store, content_store, course.id, root_dir, u'test_export')
# check that exported course has files 'updates.html' and 'updates.items.json' # check that exported course has files 'updates.html' and 'updates.items.json'
filesystem = OSFS(root_dir / 'test_export/info') filesystem = OSFS(text_type(root_dir / 'test_export/info'))
self.assertTrue(filesystem.exists('updates.html')) self.assertTrue(filesystem.exists(u'updates.html'))
self.assertTrue(filesystem.exists('updates.items.json')) self.assertTrue(filesystem.exists(u'updates.items.json'))
# verify that exported course has same data content as in course_info_update module # verify that exported course has same data content as in course_info_update module
with filesystem.open('updates.html', 'r') as grading_policy: with filesystem.open(u'updates.html', 'r') as grading_policy:
on_disk = grading_policy.read() on_disk = grading_policy.read()
self.assertEqual(on_disk, course_updates.data) self.assertEqual(on_disk, course_updates.data)
with filesystem.open('updates.items.json', 'r') as grading_policy: with filesystem.open(u'updates.items.json', 'r') as grading_policy:
on_disk = loads(grading_policy.read()) on_disk = loads(grading_policy.read())
self.assertEqual(on_disk, course_updates.items) self.assertEqual(on_disk, course_updates.items)
...@@ -315,39 +316,39 @@ class ImportRequiredTestCases(ContentStoreTestCase): ...@@ -315,39 +316,39 @@ class ImportRequiredTestCases(ContentStoreTestCase):
print 'Exporting to tempdir = {0}'.format(root_dir) print 'Exporting to tempdir = {0}'.format(root_dir)
# export out to a tempdir # export out to a tempdir
export_course_to_xml(self.store, content_store, course_id, root_dir, 'test_export') export_course_to_xml(self.store, content_store, course_id, root_dir, u'test_export')
# check for static tabs # check for static tabs
self.verify_content_existence(self.store, root_dir, course_id, 'tabs', 'static_tab', '.html') self.verify_content_existence(self.store, root_dir, course_id, u'tabs', 'static_tab', '.html')
# check for about content # check for about content
self.verify_content_existence(self.store, root_dir, course_id, 'about', 'about', '.html') self.verify_content_existence(self.store, root_dir, course_id, u'about', 'about', '.html')
# assert that there is an html and video directory in drafts: # assert that there is an html and video directory in drafts:
draft_dir = OSFS(root_dir / 'test_export/drafts') draft_dir = OSFS(root_dir / 'test_export/drafts')
self.assertTrue(draft_dir.exists('html')) self.assertTrue(draft_dir.exists(u'html'))
self.assertTrue(draft_dir.exists('video')) self.assertTrue(draft_dir.exists(u'video'))
# and assert that they contain the created modules # and assert that they contain the created modules
self.assertIn(self.DRAFT_HTML + ".xml", draft_dir.listdir('html')) self.assertIn(self.DRAFT_HTML + ".xml", draft_dir.listdir(u'html'))
self.assertIn(self.DRAFT_VIDEO + ".xml", draft_dir.listdir('video')) self.assertIn(self.DRAFT_VIDEO + ".xml", draft_dir.listdir(u'video'))
# and assert the child of the orphaned draft wasn't exported # and assert the child of the orphaned draft wasn't exported
self.assertNotIn(self.ORPHAN_DRAFT_HTML + ".xml", draft_dir.listdir('html')) self.assertNotIn(self.ORPHAN_DRAFT_HTML + ".xml", draft_dir.listdir(u'html'))
# check for grading_policy.json # check for grading_policy.json
filesystem = OSFS(root_dir / 'test_export/policies/2012_Fall') filesystem = OSFS(root_dir / 'test_export/policies/2012_Fall')
self.assertTrue(filesystem.exists('grading_policy.json')) self.assertTrue(filesystem.exists(u'grading_policy.json'))
course = self.store.get_course(course_id) course = self.store.get_course(course_id)
# compare what's on disk compared to what we have in our course # compare what's on disk compared to what we have in our course
with filesystem.open('grading_policy.json', 'r') as grading_policy: with filesystem.open(u'grading_policy.json', 'r') as grading_policy:
on_disk = loads(grading_policy.read()) on_disk = loads(grading_policy.read())
self.assertEqual(on_disk, course.grading_policy) self.assertEqual(on_disk, course.grading_policy)
# check for policy.json # check for policy.json
self.assertTrue(filesystem.exists('policy.json')) self.assertTrue(filesystem.exists(u'policy.json'))
# compare what's on disk to what we have in the course module # compare what's on disk to what we have in the course module
with filesystem.open('policy.json', 'r') as course_policy: with filesystem.open(u'policy.json', 'r') as course_policy:
on_disk = loads(course_policy.read()) on_disk = loads(course_policy.read())
self.assertIn('course/2012_Fall', on_disk) self.assertIn('course/2012_Fall', on_disk)
self.assertEqual(on_disk['course/2012_Fall'], own_metadata(course)) self.assertEqual(on_disk['course/2012_Fall'], own_metadata(course))
...@@ -417,7 +418,7 @@ class ImportRequiredTestCases(ContentStoreTestCase): ...@@ -417,7 +418,7 @@ class ImportRequiredTestCases(ContentStoreTestCase):
print 'Exporting to tempdir = {0}'.format(root_dir) print 'Exporting to tempdir = {0}'.format(root_dir)
# export out to a tempdir # export out to a tempdir
export_course_to_xml(self.store, content_store, course_id, root_dir, 'test_export') export_course_to_xml(self.store, content_store, course_id, root_dir, u'test_export')
shutil.rmtree(root_dir) shutil.rmtree(root_dir)
...@@ -443,7 +444,7 @@ class ImportRequiredTestCases(ContentStoreTestCase): ...@@ -443,7 +444,7 @@ class ImportRequiredTestCases(ContentStoreTestCase):
print 'Exporting to tempdir = {0}'.format(root_dir) print 'Exporting to tempdir = {0}'.format(root_dir)
# export out to a tempdir # export out to a tempdir
export_course_to_xml(self.store, content_store, course_id, root_dir, 'test_export') export_course_to_xml(self.store, content_store, course_id, root_dir, u'test_export')
shutil.rmtree(root_dir) shutil.rmtree(root_dir)
...@@ -496,7 +497,7 @@ class ImportRequiredTestCases(ContentStoreTestCase): ...@@ -496,7 +497,7 @@ class ImportRequiredTestCases(ContentStoreTestCase):
# Export the course # Export the course
root_dir = path(mkdtemp_clean()) root_dir = path(mkdtemp_clean())
export_course_to_xml(self.store, content_store, course_id, root_dir, 'test_roundtrip') export_course_to_xml(self.store, content_store, course_id, root_dir, u'test_roundtrip')
# Reimport and get the video back # Reimport and get the video back
import_course_from_xml(self.store, self.user.id, root_dir) import_course_from_xml(self.store, self.user.id, root_dir)
...@@ -517,7 +518,7 @@ class ImportRequiredTestCases(ContentStoreTestCase): ...@@ -517,7 +518,7 @@ class ImportRequiredTestCases(ContentStoreTestCase):
# Export the course # Export the course
root_dir = path(mkdtemp_clean()) root_dir = path(mkdtemp_clean())
export_course_to_xml(self.store, content_store, course_id, root_dir, 'test_roundtrip') export_course_to_xml(self.store, content_store, course_id, root_dir, u'test_roundtrip')
# Reimport and get the video back # Reimport and get the video back
import_course_from_xml(self.store, self.user.id, root_dir, create_if_not_present=True) import_course_from_xml(self.store, self.user.id, root_dir, create_if_not_present=True)
...@@ -541,7 +542,7 @@ class ImportRequiredTestCases(ContentStoreTestCase): ...@@ -541,7 +542,7 @@ class ImportRequiredTestCases(ContentStoreTestCase):
root_dir = path(mkdtemp_clean()) root_dir = path(mkdtemp_clean())
print 'Exporting to tempdir = {0}'.format(root_dir) print 'Exporting to tempdir = {0}'.format(root_dir)
export_course_to_xml(self.store, None, course_id, root_dir, 'test_export_no_content_store') export_course_to_xml(self.store, None, course_id, root_dir, u'test_export_no_content_store')
# Delete the course from module store and reimport it # Delete the course from module store and reimport it
...@@ -596,7 +597,7 @@ class ImportRequiredTestCases(ContentStoreTestCase): ...@@ -596,7 +597,7 @@ class ImportRequiredTestCases(ContentStoreTestCase):
content_store, content_store,
course_id, course_id,
root_dir, root_dir,
'test_no_xml_attributes' u'test_no_xml_attributes'
) )
...@@ -695,7 +696,7 @@ class MiscCourseTests(ContentStoreTestCase): ...@@ -695,7 +696,7 @@ class MiscCourseTests(ContentStoreTestCase):
def test_export_on_invalid_displayname(self, invalid_displayname): def test_export_on_invalid_displayname(self, invalid_displayname):
""" Tests that assets with invalid 'displayname' does not cause export to fail """ """ Tests that assets with invalid 'displayname' does not cause export to fail """
content_store = contentstore() content_store = contentstore()
exported_asset_name = '_Fake_asset_displayname' exported_asset_name = u'_Fake_asset_displayname'
# Create an asset with slash `invalid_displayname` ' # Create an asset with slash `invalid_displayname` '
asset_key = self.course.id.make_asset_key('asset', "fake_asset.txt") asset_key = self.course.id.make_asset_key('asset', "fake_asset.txt")
...@@ -713,10 +714,10 @@ class MiscCourseTests(ContentStoreTestCase): ...@@ -713,10 +714,10 @@ class MiscCourseTests(ContentStoreTestCase):
# Now export the course to a tempdir and test that it contains assets. The export should pass # Now export the course to a tempdir and test that it contains assets. The export should pass
root_dir = path(mkdtemp_clean()) root_dir = path(mkdtemp_clean())
print 'Exporting to tempdir = {0}'.format(root_dir) print 'Exporting to tempdir = {0}'.format(root_dir)
export_course_to_xml(self.store, content_store, self.course.id, root_dir, 'test_export') export_course_to_xml(self.store, content_store, self.course.id, root_dir, u'test_export')
filesystem = OSFS(root_dir / 'test_export/static') filesystem = OSFS(root_dir / 'test_export/static')
exported_static_files = filesystem.listdir() exported_static_files = filesystem.listdir(u'/')
# Verify that only single asset has been exported with the expected asset name. # Verify that only single asset has been exported with the expected asset name.
self.assertTrue(filesystem.exists(exported_asset_name)) self.assertTrue(filesystem.exists(exported_asset_name))
...@@ -737,13 +738,13 @@ class MiscCourseTests(ContentStoreTestCase): ...@@ -737,13 +738,13 @@ class MiscCourseTests(ContentStoreTestCase):
# Make an existing unit a draft # Make an existing unit a draft
self.store.convert_to_draft(self.problem.location, self.user.id) self.store.convert_to_draft(self.problem.location, self.user.id)
root_dir = path(mkdtemp_clean()) root_dir = path(mkdtemp_clean())
export_course_to_xml(self.store, None, self.course.id, root_dir, 'test_export') export_course_to_xml(self.store, None, self.course.id, root_dir, u'test_export')
# Verify that problem is exported in the drafts. This is expected because we are # Verify that problem is exported in the drafts. This is expected because we are
# mocking get_item to for drafts. Expect no draft is exported. # mocking get_item to for drafts. Expect no draft is exported.
# Specifically get_item is used in `xmodule.modulestore.xml_exporter._export_drafts` # Specifically get_item is used in `xmodule.modulestore.xml_exporter._export_drafts`
export_draft_dir = OSFS(root_dir / 'test_export/drafts') export_draft_dir = OSFS(root_dir / 'test_export/drafts')
self.assertEqual(len(export_draft_dir.listdir()), 0) self.assertEqual(len(export_draft_dir.listdir(u'/')), 0)
# Remove tempdir # Remove tempdir
shutil.rmtree(root_dir) shutil.rmtree(root_dir)
...@@ -751,7 +752,7 @@ class MiscCourseTests(ContentStoreTestCase): ...@@ -751,7 +752,7 @@ class MiscCourseTests(ContentStoreTestCase):
def test_assets_overwrite(self): def test_assets_overwrite(self):
""" Tests that assets will similar 'displayname' will be overwritten during export """ """ Tests that assets will similar 'displayname' will be overwritten during export """
content_store = contentstore() content_store = contentstore()
asset_displayname = 'Fake_asset.txt' asset_displayname = u'Fake_asset.txt'
# Create two assets with similar 'displayname' # Create two assets with similar 'displayname'
for i in range(2): for i in range(2):
...@@ -773,11 +774,11 @@ class MiscCourseTests(ContentStoreTestCase): ...@@ -773,11 +774,11 @@ class MiscCourseTests(ContentStoreTestCase):
# Now export the course to a tempdir and test that it contains assets. # Now export the course to a tempdir and test that it contains assets.
root_dir = path(mkdtemp_clean()) root_dir = path(mkdtemp_clean())
print 'Exporting to tempdir = {0}'.format(root_dir) print 'Exporting to tempdir = {0}'.format(root_dir)
export_course_to_xml(self.store, content_store, self.course.id, root_dir, 'test_export') export_course_to_xml(self.store, content_store, self.course.id, root_dir, u'test_export')
# Verify that asset have been overwritten during export. # Verify that asset have been overwritten during export.
filesystem = OSFS(root_dir / 'test_export/static') filesystem = OSFS(root_dir / 'test_export/static')
exported_static_files = filesystem.listdir() exported_static_files = filesystem.listdir(u'/')
self.assertTrue(filesystem.exists(asset_displayname)) self.assertTrue(filesystem.exists(asset_displayname))
self.assertEqual(len(exported_static_files), 1) self.assertEqual(len(exported_static_files), 1)
...@@ -2109,7 +2110,7 @@ class ContentLicenseTest(ContentStoreTestCase): ...@@ -2109,7 +2110,7 @@ class ContentLicenseTest(ContentStoreTestCase):
root_dir = path(mkdtemp_clean()) root_dir = path(mkdtemp_clean())
self.course.license = "creative-commons: BY SA" self.course.license = "creative-commons: BY SA"
self.store.update_item(self.course, None) self.store.update_item(self.course, None)
export_course_to_xml(self.store, content_store, self.course.id, root_dir, 'test_license') export_course_to_xml(self.store, content_store, self.course.id, root_dir, u'test_license')
fname = "{block}.xml".format(block=self.course.scope_ids.usage_id.block_id) fname = "{block}.xml".format(block=self.course.scope_ids.usage_id.block_id)
run_file_path = root_dir / "test_license" / "course" / fname run_file_path = root_dir / "test_license" / "course" / fname
run_xml = etree.parse(run_file_path.open()) run_xml = etree.parse(run_file_path.open())
...@@ -2122,7 +2123,7 @@ class ContentLicenseTest(ContentStoreTestCase): ...@@ -2122,7 +2123,7 @@ class ContentLicenseTest(ContentStoreTestCase):
parent_location=self.course.location, category='video', parent_location=self.course.location, category='video',
license="all-rights-reserved" license="all-rights-reserved"
) )
export_course_to_xml(self.store, content_store, self.course.id, root_dir, 'test_license') export_course_to_xml(self.store, content_store, self.course.id, root_dir, u'test_license')
fname = "{block}.xml".format(block=video_descriptor.scope_ids.usage_id.block_id) fname = "{block}.xml".format(block=video_descriptor.scope_ids.usage_id.block_id)
video_file_path = root_dir / "test_license" / "video" / fname video_file_path = root_dir / "test_license" / "video" / fname
video_xml = etree.parse(video_file_path.open()) video_xml = etree.parse(video_file_path.open())
......
...@@ -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