From da29d88d04782ec3966607a3bcf6c328d12ea7db Mon Sep 17 00:00:00 2001 From: Victor Shnayder Date: Thu, 26 Jul 2012 16:17:17 -0400 Subject: [PATCH] Add import-export round-trip check. * add fix for the file export code--remove tag tail and text, not just the children. --- common/lib/xmodule/xmodule/xml_module.py | 5 ++ .../management/commands/clean_xml.py | 82 +++++++++++++------ 2 files changed, 60 insertions(+), 27 deletions(-) diff --git a/common/lib/xmodule/xmodule/xml_module.py b/common/lib/xmodule/xmodule/xml_module.py index ee0b541de5..2c3fa62f6e 100644 --- a/common/lib/xmodule/xmodule/xml_module.py +++ b/common/lib/xmodule/xmodule/xml_module.py @@ -257,6 +257,11 @@ class XmlDescriptor(XModuleDescriptor): # ...and remove all of its children here for child in xml_object: xml_object.remove(child) + # also need to remove the text of this object. + xml_object.text = '' + # and the tail for good measure... + xml_object.tail = '' + xml_object.set('filename', self.name) diff --git a/lms/djangoapps/courseware/management/commands/clean_xml.py b/lms/djangoapps/courseware/management/commands/clean_xml.py index 1803553ba3..bd0c2b21bb 100644 --- a/lms/djangoapps/courseware/management/commands/clean_xml.py +++ b/lms/djangoapps/courseware/management/commands/clean_xml.py @@ -1,6 +1,8 @@ +import os import sys import traceback +from filecmp import dircmp from fs.osfs import OSFS from path import path from lxml import etree @@ -10,28 +12,6 @@ from django.core.management.base import BaseCommand from xmodule.modulestore.xml import XMLModuleStore -def export(course, export_dir): - """Export the specified course to course_dir. Creates dir if it doesn't exist. - Overwrites files, does not clean out dir beforehand. - """ - fs = OSFS(export_dir, create=True) - if not fs.isdirempty('.'): - print ('WARNING: Directory {dir} not-empty.' - ' May clobber/confuse things'.format(dir=export_dir)) - - try: - xml = course.export_to_xml(fs) - with fs.open('course.xml', mode='w') as f: - f.write(xml) - - return True - except: - print 'Export failed!' - traceback.print_exc() - - return False - - def traverse_tree(course): '''Load every descriptor in course. Return bool success value.''' queue = [course] @@ -61,7 +41,30 @@ def make_logging_error_handler(): return (error_handler, errors) -def clean_xml(course_dir, export_dir, verbose=True): + +def export(course, export_dir): + """Export the specified course to course_dir. Creates dir if it doesn't exist. + Overwrites files, does not clean out dir beforehand. + """ + fs = OSFS(export_dir, create=True) + if not fs.isdirempty('.'): + print ('WARNING: Directory {dir} not-empty.' + ' May clobber/confuse things'.format(dir=export_dir)) + + try: + xml = course.export_to_xml(fs) + with fs.open('course.xml', mode='w') as f: + f.write(xml) + + return True + except: + print 'Export failed!' + traceback.print_exc() + + return False + + +def import_with_checks(course_dir, verbose=True): all_ok = True print "Attempting to load '{0}'".format(course_dir) @@ -105,7 +108,7 @@ def clean_xml(course_dir, export_dir, verbose=True): course = courses[0] - print course + #print course validators = ( traverse_tree, ) @@ -120,10 +123,36 @@ def clean_xml(course_dir, export_dir, verbose=True): if all_ok: print 'Course passes all checks!' - export(course, export_dir) - else: print "Course fails some checks. See above for errors." + return all_ok, course + + +def check_roundtrip(course_dir): + '''Check that import->export leaves the course the same''' + + print "====== Roundtrip import =======" + (ok, course) = import_with_checks(course_dir) + if not ok: + raise Exception("Roundtrip import failed!") + + print "====== Roundtrip export =======" + export_dir = course_dir + ".rt" + export(course, export_dir) + + # dircmp doesn't do recursive diffs. + # diff = dircmp(course_dir, export_dir, ignore=[], hide=[]) + print "======== Roundtrip diff: =========" + os.system("diff -r {0} {1}".format(course_dir, export_dir)) + print "======== ideally there is no diff above this =======" + + +def clean_xml(course_dir, export_dir): + (ok, course) = import_with_checks(course_dir) + if ok: + export(course, export_dir) + check_roundtrip(export_dir) + else: print "Did NOT export" @@ -134,7 +163,6 @@ class Command(BaseCommand): Usage: clean_xml PATH-TO-COURSE-DIR PATH-TO-OUTPUT-DIR """ - def handle(self, *args, **options): if len(args) != 2: print Command.help