This function was removed by python 3.11 so update to the alternate call that is the current recommended replacement. https://docs.python.org/3.11/library/inspect.html#inspect.getfullargspec
169 lines
4.9 KiB
Python
169 lines
4.9 KiB
Python
"""
|
|
Factories for generating edXML for testing XModule import
|
|
"""
|
|
|
|
|
|
import inspect
|
|
from tempfile import mkdtemp
|
|
|
|
from factory import Factory, Sequence, lazy_attribute, post_generation
|
|
from fs.osfs import OSFS
|
|
from lxml import etree
|
|
|
|
from xmodule.modulestore.inheritance import InheritanceMixin
|
|
from xmodule.x_module import XModuleMixin
|
|
|
|
|
|
class XmlImportData:
|
|
"""
|
|
Class to capture all of the data needed to actually run an XML import,
|
|
so that the Factories have something to generate
|
|
"""
|
|
def __init__(self, xml_node, xml=None, course_id=None,
|
|
default_class=None, policy=None,
|
|
filesystem=None, parent=None,
|
|
xblock_mixins=(), xblock_select=None):
|
|
|
|
self._xml_node = xml_node
|
|
self._xml_string = xml
|
|
self.course_id = course_id
|
|
self.default_class = default_class
|
|
self.filesystem = filesystem
|
|
self.xblock_mixins = xblock_mixins
|
|
self.xblock_select = xblock_select
|
|
self.parent = parent
|
|
|
|
if policy is None:
|
|
self.policy = {}
|
|
else:
|
|
self.policy = policy
|
|
|
|
@property
|
|
def xml_string(self):
|
|
"""Return the stringified version of the generated xml"""
|
|
if self._xml_string is not None:
|
|
return self._xml_string
|
|
|
|
return etree.tostring(self._xml_node)
|
|
|
|
def __repr__(self):
|
|
return "XmlImportData{!r}".format((
|
|
self._xml_node, self._xml_string, self.course_id,
|
|
self.default_class, self.policy,
|
|
self.filesystem, self.parent, self.xblock_mixins,
|
|
self.xblock_select,
|
|
))
|
|
|
|
|
|
# Extract all argument names used to construct XmlImportData objects,
|
|
# so that the factory doesn't treat them as XML attributes
|
|
XML_IMPORT_ARGS = inspect.getfullargspec(XmlImportData.__init__).args
|
|
|
|
|
|
class XmlImportFactory(Factory):
|
|
"""
|
|
Factory for generating XmlImportData's, which can hold all the data needed
|
|
to run an XModule XML import
|
|
"""
|
|
class Meta:
|
|
model = XmlImportData
|
|
|
|
filesystem = OSFS(mkdtemp())
|
|
xblock_mixins = (InheritanceMixin, XModuleMixin)
|
|
url_name = Sequence(str)
|
|
attribs = {}
|
|
policy = {}
|
|
inline_xml = True
|
|
tag = 'unknown'
|
|
course_id = 'edX/xml_test_course/101'
|
|
|
|
@classmethod
|
|
def _adjust_kwargs(cls, **kwargs):
|
|
"""
|
|
Adjust the kwargs to be passed to the generated class.
|
|
|
|
Any kwargs that match :fun:`XmlImportData.__init__` will be passed
|
|
through. Any other unknown `kwargs` will be treated as XML attributes
|
|
|
|
:param tag: xml tag for the generated :class:`Element` node
|
|
:param text: (Optional) specifies the text of the generated :class:`Element`.
|
|
:param policy: (Optional) specifies data for the policy json file for this node
|
|
:type policy: dict
|
|
:param attribs: (Optional) specify attributes for the XML node
|
|
:type attribs: dict
|
|
"""
|
|
tag = kwargs.pop('tag', 'unknown')
|
|
kwargs['policy'] = {'{tag}/{url_name}'.format(tag=tag, url_name=kwargs['url_name']): kwargs['policy']}
|
|
|
|
kwargs['xml_node'].text = kwargs.pop('text', None)
|
|
|
|
kwargs['xml_node'].attrib.update(kwargs.pop('attribs', {}))
|
|
|
|
# Make sure that the xml_module doesn't try and open a file to find the contents
|
|
# of this node.
|
|
inline_xml = kwargs.pop('inline_xml')
|
|
|
|
if inline_xml:
|
|
kwargs['xml_node'].set('not_a_pointer', 'true')
|
|
|
|
for key in list(kwargs.keys()):
|
|
if key not in XML_IMPORT_ARGS:
|
|
kwargs['xml_node'].set(key, kwargs.pop(key))
|
|
|
|
if not inline_xml:
|
|
kwargs['xml_node'].write(
|
|
kwargs['filesystem'].open(
|
|
'{}/{}.xml'.format(kwargs['tag'], kwargs['url_name'])
|
|
),
|
|
encoding='utf-8'
|
|
)
|
|
|
|
return kwargs
|
|
|
|
@lazy_attribute
|
|
def xml_node(self):
|
|
"""An :class:`xml.etree.Element`"""
|
|
return etree.Element(self.tag)
|
|
|
|
@post_generation
|
|
def parent(self, _create, extracted, **_):
|
|
"""Hook to merge this xml into a parent xml node"""
|
|
if extracted is None:
|
|
return
|
|
|
|
extracted._xml_node.append(self._xml_node) # pylint: disable=no-member, protected-access
|
|
extracted.policy.update(self.policy)
|
|
|
|
|
|
class CourseFactory(XmlImportFactory):
|
|
"""Factory for <course> nodes"""
|
|
tag = 'course'
|
|
name = '101'
|
|
static_asset_path = 'xml_test_course'
|
|
|
|
|
|
class ChapterFactory(XmlImportFactory):
|
|
"""Factory for <chapter> nodes"""
|
|
tag = 'chapter'
|
|
|
|
|
|
class SequenceFactory(XmlImportFactory):
|
|
"""Factory for <sequential> nodes"""
|
|
tag = 'sequential'
|
|
|
|
|
|
class VerticalFactory(XmlImportFactory):
|
|
"""Factory for <vertical> nodes"""
|
|
tag = 'vertical'
|
|
|
|
|
|
class ProblemFactory(XmlImportFactory):
|
|
"""Factory for <problem> nodes"""
|
|
tag = 'problem'
|
|
text = '<h1>Empty Problem!</h1>'
|
|
|
|
|
|
class HtmlFactory(XmlImportFactory):
|
|
"""Factory for <html> nodes"""
|
|
tag = 'html'
|