Commit 7852b107 by Carlos Andrés Rocha

Add command to dump a course to stdout as a tar.gz file

parent 8cc86082
"""
A Django command that exports a course to a tar.gz file.
"""
import shutil
import tarfile
from tempfile import mkdtemp
from textwrap import dedent
from path import path
from django.core.management.base import BaseCommand, CommandError
from xmodule.modulestore.django import modulestore
from xmodule.contentstore.django import contentstore
from xmodule.modulestore.xml_exporter import export_to_xml
class Command(BaseCommand):
"""
Export a course to XML. The output is compressed as a tar.gz file
"""
args = "<course_id> <output_filename>"
help = dedent(__doc__).strip()
def handle(self, *args, **options):
try:
course_id = args[0]
filename = args[1]
except IndexError:
raise CommandError("Insufficient arguments")
export_course_to_tarfile(course_id, filename)
def export_course_to_tarfile(course_id, filename):
"""Exports a course into a tar.gz file"""
tmp_dir = mkdtemp()
try:
course_dir = export_course_to_directory(course_id, tmp_dir)
compress_directory(course_dir, filename)
finally:
shutil.rmtree(tmp_dir)
def export_course_to_directory(course_id, root_dir):
"""Export course into a directory"""
store = modulestore()
course = store.get_course(course_id)
if course is None:
raise CommandError("Invalid course_id")
course_name = course.location.course_id.replace('/', '-')
export_to_xml(store, None, course.location, root_dir, course_name)
course_dir = path(root_dir) / course_name
return course_dir
def compress_directory(directory, filename):
"""Compress a directrory into a tar.gz file"""
mode = 'w:gz'
name = path(directory).name
with tarfile.open(filename, mode) as tar_file:
tar_file.add(directory, arcname=name)
"""Tests for Django management commands"""
import json
import shutil
from StringIO import StringIO
import tarfile
from tempfile import mkdtemp
from path import path
from django.core.management import call_command
from django.test.utils import override_settings
......@@ -46,7 +51,10 @@ class CommandsTestCase(CommandTestCase):
self.assertEqual(self.loaded_courses, dumped_courses)
def test_dump_course_structure(self):
dumped_courses = self.call_command('dump_course_ids').split('\n')
self.assertEqual(self.loaded_courses, dumped_courses)
def test_dump_course_structure(self):
args = ['edX/simple/2012_Fall']
kwargs = {'modulestore': 'default'}
output = self.call_command('dump_course_structure', *args, **kwargs)
......@@ -71,3 +79,34 @@ class CommandsTestCase(CommandTestCase):
# Check if there is the right number of elements
self.assertEqual(len(dump), 16)
def test_export_course(self):
tmp_dir = path(mkdtemp())
filename = tmp_dir / 'test.tar.gz'
try:
self.run_export_course(filename)
with tarfile.open(filename) as tar_file:
self.check_export_file(tar_file)
finally:
shutil.rmtree(tmp_dir)
def run_export_course(self, filename): # pylint: disable=missing-docstring
args = ['edX/simple/2012_Fall', filename]
kwargs = {'modulestore': 'default'}
self.call_command('export_course', *args, **kwargs)
def check_export_file(self, tar_file): # pylint: disable=missing-docstring
names = tar_file.getnames()
# Check if some of the files are present.
# The rest is of the code should be covered by the tests for
# xmodule.modulestore.xml_exporter, used by the dump_course command
assert_in = self.assertIn
assert_in('edX-simple-2012_Fall', names)
assert_in('edX-simple-2012_Fall/policies/2012_Fall/policy.json', names)
assert_in('edX-simple-2012_Fall/html/toylab.html', names)
assert_in('edX-simple-2012_Fall/videosequence/A_simple_sequence.xml', names)
assert_in('edX-simple-2012_Fall/sequential/Lecture_2.xml', names)
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