forked from openedx/edx-platform
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Cleaned up imports, reverted export_olx to original state, crea…
…ted new export_imscc file
- Loading branch information
Showing
3 changed files
with
144 additions
and
67 deletions.
There are no files selected for viewing
121 changes: 121 additions & 0 deletions
121
cms/djangoapps/contentstore/management/commands/export_imscc.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
""" | ||
A Django command that exports a course to a tar.gz file using IMSCC protocol | ||
At present, it differs from Studio exports in several ways: | ||
* It does not include static content. | ||
* It only supports the export of courses. It does not export libraries. | ||
""" | ||
|
||
import os | ||
import re | ||
import shutil | ||
import tarfile | ||
from tempfile import mkdtemp, mktemp | ||
from textwrap import dedent | ||
|
||
from django.core.management.base import BaseCommand, CommandError | ||
from opaque_keys import InvalidKeyError | ||
from opaque_keys.edx.keys import CourseKey | ||
from path import Path as path | ||
|
||
from xmodule.modulestore.django import modulestore | ||
from xmodule.modulestore.imscc_exporter import export_course_to_imscc | ||
|
||
class Command(BaseCommand): | ||
""" | ||
Export a course to IMSCC. The output is compressed as a tar.gz file. | ||
""" | ||
help = dedent(__doc__).strip() | ||
|
||
def add_arguments(self, parser): | ||
parser.add_argument('course_id', nargs="+") #nargs = "+" allows parsing of unlimited course ids | ||
parser.add_argument('--output') | ||
parser.add_argument('--external-tool-only', action = 'store_true', help = 'Export Common Cartridge file using only external tools and no assignment types') | ||
|
||
def handle(self, *args, **options): | ||
external_tool_only = options.get('external_tool_only', False) | ||
course_ids = options['course_id'] | ||
|
||
# stores all the different course keys based on the inputted course ids | ||
course_keys = [] | ||
for course_id in course_ids: | ||
try: | ||
course_keys.append(CourseKey.from_string(course_id)) | ||
except InvalidKeyError: | ||
raise CommandError("Unparsable course_id") # lint-amnesty, pylint: disable=raise-missing-from | ||
except IndexError: | ||
raise CommandError("Insufficient arguments") # lint-amnesty, pylint: disable=raise-missing-from | ||
|
||
filename = options['output'] | ||
pipe_results = False | ||
|
||
if filename is None: | ||
filename = mktemp() | ||
pipe_results = True | ||
|
||
export_course_to_tarfile(course_keys, filename, external_tool_only) | ||
|
||
results = self._get_results(filename) if pipe_results else b'' | ||
|
||
# results is of type bytes, so we must write the underlying buffer directly. | ||
self.stdout.buffer.write(results) | ||
|
||
def _get_results(self, filename): | ||
""" | ||
Load results from file. | ||
Returns: | ||
bytes: bytestring of file contents. | ||
""" | ||
with open(filename, 'rb') as f: | ||
results = f.read() | ||
os.remove(filename) | ||
return results | ||
|
||
|
||
def export_course_to_tarfile(course_keys, filename, external_tool_only): | ||
"""Exports a course into a tar.gz file""" | ||
tmp_dir = mkdtemp() | ||
try: | ||
course_dir = export_course_to_directory(course_keys, tmp_dir, external_tool_only) | ||
compress_directory(course_dir, filename) | ||
finally: | ||
shutil.rmtree(tmp_dir, ignore_errors=True) | ||
|
||
|
||
def export_course_to_directory(course_keys, root_dir, external_tool_only): | ||
"""Export course into a directory""" | ||
# attempt to get all the courses based on the course_keys | ||
store = modulestore() | ||
courses = [] | ||
for course_key in course_keys: | ||
course = store.get_course(course_key) | ||
if course is None: | ||
raise CommandError("Invalid course_id") | ||
courses.append(course) | ||
|
||
course_ids = [] | ||
for course in courses: | ||
course_ids.append(course.id) | ||
# The safest characters are A-Z, a-z, 0-9, <underscore>, <period> and <hyphen>. | ||
# We represent the first four with \w. | ||
# TODO: Once we support courses with unicode characters, we will need to revisit this. | ||
replacement_char = '-' | ||
course_dir = replacement_char.join([courses[0].id.org, courses[0].id.course, courses[0].id.run]) | ||
course_dir = re.sub(r'[^\w\.\-]', replacement_char, course_dir) | ||
|
||
if len(courses) > 1: | ||
course_dir = "MULTI-COURSE-EXPORT" | ||
export_course_to_imscc(store, None, course_ids, root_dir, course_dir, external_tool_only) | ||
|
||
export_dir = path(root_dir) / course_dir | ||
return export_dir | ||
|
||
|
||
def compress_directory(directory, filename): | ||
"""Compress a directory 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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters