This commit updates lms/djangoapps/courseware. These keys are now objects with a limited interface, and the particular internal representation is managed by the data storage layer (the modulestore). For the LMS, there should be no outward-facing changes to the system. The keys are, for now, a change to internal representation only. For Studio, the new serialized form of the keys is used in urls, to allow for further migration in the future. Co-Author: Andy Armstrong <andya@edx.org> Co-Author: Christina Roberts <christina@edx.org> Co-Author: David Baumgold <db@edx.org> Co-Author: Diana Huang <dkh@edx.org> Co-Author: Don Mitchell <dmitchell@edx.org> Co-Author: Julia Hansbrough <julia@edx.org> Co-Author: Nimisha Asthagiri <nasthagiri@edx.org> Co-Author: Sarina Canelake <sarina@edx.org> [LMS-2370]
97 lines
2.7 KiB
Python
97 lines
2.7 KiB
Python
"""
|
|
A Django command that exports a course to a tar.gz file.
|
|
|
|
If <filename> is '-', it pipes the file to stdout
|
|
|
|
"""
|
|
|
|
import os
|
|
import shutil
|
|
import tarfile
|
|
from tempfile import mktemp, 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.modulestore.xml_exporter import export_to_xml
|
|
from opaque_keys import InvalidKeyError
|
|
from xmodule.modulestore.locations import SlashSeparatedCourseKey
|
|
|
|
|
|
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):
|
|
course_id, filename, pipe_results = self._parse_arguments(args)
|
|
|
|
export_course_to_tarfile(course_id, filename)
|
|
|
|
results = self._get_results(filename) if pipe_results else None
|
|
|
|
return results
|
|
|
|
def _parse_arguments(self, args):
|
|
"""Parse command line arguments"""
|
|
try:
|
|
course_id = SlashSeparatedCourseKey.from_deprecated_string(args[0])
|
|
filename = args[1]
|
|
except InvalidKeyError:
|
|
raise CommandError("Unparsable course_id")
|
|
except IndexError:
|
|
raise CommandError("Insufficient arguments")
|
|
|
|
# If filename is '-' save to a temp file
|
|
pipe_results = False
|
|
if filename == '-':
|
|
filename = mktemp()
|
|
pipe_results = True
|
|
|
|
return course_id, filename, pipe_results
|
|
|
|
def _get_results(self, filename):
|
|
"""Load results from file"""
|
|
with open(filename) as f:
|
|
results = f.read()
|
|
os.remove(filename)
|
|
return results
|
|
|
|
|
|
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.id.to_deprecated_string().replace('/', '-')
|
|
export_to_xml(store, None, course.id, 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)
|