Commit 5244a81f by Victor Shnayder

xml format cleanups

* rename slug->url_name
* rename name->display_name
* docstring cleanups
* comments :)
parent 565cc684
...@@ -50,24 +50,25 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem): ...@@ -50,24 +50,25 @@ class ImportSystem(XMLParsingSystem, MakoDescriptorSystem):
log.exception("Unable to parse xml: {xml}".format(xml=xml)) log.exception("Unable to parse xml: {xml}".format(xml=xml))
raise raise
# TODO (vshnayder): is the slug munging permanent, or also intended # VS[compat]. Take this out once course conversion is done
# to be taken out? if xml_data.get('slug') is None and xml_data.get('url_name') is None:
if xml_data.get('slug') is None:
if xml_data.get('name'): if xml_data.get('name'):
slug = Location.clean(xml_data.get('name')) slug = Location.clean(xml_data.get('name'))
elif xml_data.get('display_name'):
slug = Location.clean(xml_data.get('display_name'))
else: else:
self.unnamed_modules += 1 self.unnamed_modules += 1
slug = '{tag}_{count}'.format(tag=xml_data.tag, slug = '{tag}_{count}'.format(tag=xml_data.tag,
count=self.unnamed_modules) count=self.unnamed_modules)
if slug in self.used_slugs: while slug in self.used_slugs:
self.unnamed_modules += 1 self.unnamed_modules += 1
slug = '{slug}_{count}'.format(slug=slug, slug = '{slug}_{count}'.format(slug=slug,
count=self.unnamed_modules) count=self.unnamed_modules)
self.used_slugs.add(slug) self.used_slugs.add(slug)
# log.debug('-> slug=%s' % slug) # log.debug('-> slug=%s' % slug)
xml_data.set('slug', slug) xml_data.set('url_name', slug)
module = XModuleDescriptor.load_from_xml( module = XModuleDescriptor.load_from_xml(
etree.tostring(xml_data), self, org, etree.tostring(xml_data), self, org,
......
...@@ -9,7 +9,6 @@ import os ...@@ -9,7 +9,6 @@ import os
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
# TODO (cpennington): This was implemented in an attempt to improve performance, # TODO (cpennington): This was implemented in an attempt to improve performance,
# but the actual improvement wasn't measured (and it was implemented late at night). # but the actual improvement wasn't measured (and it was implemented late at night).
# We should check if it hurts, and whether there's a better way of doing lazy loading # We should check if it hurts, and whether there's a better way of doing lazy loading
...@@ -76,8 +75,13 @@ _AttrMapBase = namedtuple('_AttrMap', 'metadata_key to_metadata from_metadata') ...@@ -76,8 +75,13 @@ _AttrMapBase = namedtuple('_AttrMap', 'metadata_key to_metadata from_metadata')
class AttrMap(_AttrMapBase): class AttrMap(_AttrMapBase):
""" """
A class that specifies a metadata_key, a function to transform an xml A class that specifies a metadata_key, and two functions:
attribute to be placed in that key, and to transform that key value
to_metadata: convert value from the xml representation into
an internal python representation
from_metadata: convert the internal python representation into
the value to store in the xml.
""" """
def __new__(_cls, metadata_key, def __new__(_cls, metadata_key,
to_metadata=lambda x: x, to_metadata=lambda x: x,
...@@ -96,17 +100,35 @@ class XmlDescriptor(XModuleDescriptor): ...@@ -96,17 +100,35 @@ class XmlDescriptor(XModuleDescriptor):
# The attributes will be removed from the definition xml passed # The attributes will be removed from the definition xml passed
# to definition_from_xml, and from the xml returned by definition_to_xml # to definition_from_xml, and from the xml returned by definition_to_xml
metadata_attributes = ('format', 'graceperiod', 'showanswer', 'rerandomize', metadata_attributes = ('format', 'graceperiod', 'showanswer', 'rerandomize',
'start', 'due', 'graded', 'name', 'slug', 'hide_from_toc') 'start', 'due', 'graded', 'display_name', 'url_name', 'hide_from_toc',
# VS[compat] Remove once unused.
'name', 'slug')
# A dictionary mapping xml attribute names to functions of the value
# that return the metadata key and value # A dictionary mapping xml attribute names AttrMaps that describe how
# to import and export them
xml_attribute_map = { xml_attribute_map = {
# type conversion: want True/False in python, "true"/"false" in xml
'graded': AttrMap('graded', 'graded': AttrMap('graded',
lambda val: val == 'true', lambda val: val == 'true',
lambda val: str(val).lower()), lambda val: str(val).lower()),
'name': AttrMap('display_name'),
} }
# VS[compat]. Backwards compatibility code that can go away after
# importing 2012 courses.
# A set of metadata key conversions that we want to make
metadata_translations = {
'slug' : 'url_name',
'name' : 'display_name',
}
@classmethod
def _translate(cls, key):
'VS[compat]'
return cls.metadata_translations.get(key, key)
@classmethod @classmethod
def definition_from_xml(cls, xml_object, system): def definition_from_xml(cls, xml_object, system):
""" """
...@@ -157,9 +179,11 @@ class XmlDescriptor(XModuleDescriptor): ...@@ -157,9 +179,11 @@ class XmlDescriptor(XModuleDescriptor):
for attr in cls.metadata_attributes: for attr in cls.metadata_attributes:
val = xml_object.get(attr) val = xml_object.get(attr)
if val is not None: if val is not None:
# VS[compat]. Remove after all key translations done
attr = cls._translate(attr)
attr_map = cls.xml_attribute_map.get(attr, AttrMap(attr)) attr_map = cls.xml_attribute_map.get(attr, AttrMap(attr))
metadata[attr_map.metadata_key] = attr_map.to_metadata(val) metadata[attr_map.metadata_key] = attr_map.to_metadata(val)
return metadata return metadata
def definition_loader(): def definition_loader():
...@@ -182,7 +206,6 @@ class XmlDescriptor(XModuleDescriptor): ...@@ -182,7 +206,6 @@ class XmlDescriptor(XModuleDescriptor):
filepath = candidate filepath = candidate
break break
log.debug('filepath=%s, resources_fs=%s' % (filepath, system.resources_fs))
try: try:
with system.resources_fs.open(filepath) as file: with system.resources_fs.open(filepath) as file:
definition_xml = cls.file_to_xml(file) definition_xml = cls.file_to_xml(file)
...@@ -190,12 +213,14 @@ class XmlDescriptor(XModuleDescriptor): ...@@ -190,12 +213,14 @@ class XmlDescriptor(XModuleDescriptor):
msg = 'Unable to load file contents at path %s' % filepath msg = 'Unable to load file contents at path %s' % filepath
log.exception(msg) log.exception(msg)
system.error_handler(msg) system.error_handler(msg)
# if error_handler didn't reraise, work around it. # if error_handler didn't reraise, work around problem.
return {'data': 'Error loading file contents at path %s' % filepath} return {'data': 'Error loading file contents at path %s' % filepath}
cls.clean_metadata_from_xml(definition_xml) cls.clean_metadata_from_xml(definition_xml)
return cls.definition_from_xml(definition_xml, system) return cls.definition_from_xml(definition_xml, system)
# VS[compat] -- just have the url_name lookup once translation is done
slug = xml_object.get('url_name', xml_object.get('slug'))
return cls( return cls(
system, system,
LazyLoadingDict(definition_loader), LazyLoadingDict(definition_loader),
...@@ -203,7 +228,7 @@ class XmlDescriptor(XModuleDescriptor): ...@@ -203,7 +228,7 @@ class XmlDescriptor(XModuleDescriptor):
org, org,
course, course,
xml_object.tag, xml_object.tag,
xml_object.get('slug')], slug],
metadata=LazyLoadingDict(metadata_loader), metadata=LazyLoadingDict(metadata_loader),
) )
...@@ -242,12 +267,15 @@ class XmlDescriptor(XModuleDescriptor): ...@@ -242,12 +267,15 @@ class XmlDescriptor(XModuleDescriptor):
resource_fs is a pyfilesystem object (from the fs package) resource_fs is a pyfilesystem object (from the fs package)
""" """
# Get the definition
xml_object = self.definition_to_xml(resource_fs) xml_object = self.definition_to_xml(resource_fs)
self.__class__.clean_metadata_from_xml(xml_object) self.__class__.clean_metadata_from_xml(xml_object)
# Set the tag first, so it's right if writing to a file
xml_object.tag = self.category xml_object.tag = self.category
xml_object.set('slug', self.name)
# Write it to a file if necessary
if self.split_to_file(xml_object): if self.split_to_file(xml_object):
# Put this object in it's own file # Put this object in it's own file
filepath = self.__class__._format_filepath(self.category, self.name) filepath = self.__class__._format_filepath(self.category, self.name)
...@@ -265,6 +293,8 @@ class XmlDescriptor(XModuleDescriptor): ...@@ -265,6 +293,8 @@ class XmlDescriptor(XModuleDescriptor):
xml_object.set('filename', self.name) xml_object.set('filename', self.name)
# Add the metadata
xml_object.set('url_name', self.name)
for attr in self.metadata_attributes: for attr in self.metadata_attributes:
attr_map = self.xml_attribute_map.get(attr, AttrMap(attr)) attr_map = self.xml_attribute_map.get(attr, AttrMap(attr))
metadata_key = attr_map.metadata_key metadata_key = attr_map.metadata_key
...@@ -276,6 +306,7 @@ class XmlDescriptor(XModuleDescriptor): ...@@ -276,6 +306,7 @@ class XmlDescriptor(XModuleDescriptor):
val = attr_map.from_metadata(self.metadata[metadata_key]) val = attr_map.from_metadata(self.metadata[metadata_key])
xml_object.set(attr, val) xml_object.set(attr, val)
# Now we just have to make it beautiful
return etree.tostring(xml_object, pretty_print=True) return etree.tostring(xml_object, pretty_print=True)
def definition_to_xml(self, resource_fs): def definition_to_xml(self, resource_fs):
......
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