def traverse_dom_for_pspictures(self, element):
     # <pspicture><code>
     if element.tag == "pspicture":
         if self.pspictureGeneratorVersion == "1.0":
             self.psPictureCount += 1
         src = self.pspicture_path(element)
         mediaNode = utils.create_node("media")
         mediaNode.append(utils.create_node("image"))
         mediaNode.attrib["alt"] = "Image"
         mediaNode[0].attrib["src"] = src
         mediaNode.tail = element.tail
         element.getparent().replace(element, mediaNode)
     elif element.tag == "tikzpicture":
         if self.pspictureGeneratorVersion == "1.0":
             self.tikzPictureCount += 1
         src = self.pspicture_path(element)
         mediaNode = utils.create_node("media")
         mediaNode.append(utils.create_node("image"))
         mediaNode.attrib["alt"] = "Image"
         mediaNode[0].attrib["src"] = src
         mediaNode.tail = element.tail
         element.getparent().replace(element, mediaNode)
     else:
         children = element.getchildren()
         for child in children:
             self.traverse_dom_for_pspictures(child)
Exemplo n.º 2
0
 def setUp(self):
     self.nodes = [
         create_node(
             name="one",
             input_type="in",
             output_type="one_out",
             space={"param": Choice(choices=[True, False])},
             with_execute=True,
         ),
         create_node(
             name="two",
             input_type="one_out",
             output_type="two_out",
             space={"param": Choice(choices=[True, False])},
             with_execute=True,
         ),
         create_node(
             name="three",
             input_type="two_out",
             output_type="out",
             space={"param": Choice(choices=[True, False])},
             with_execute=True,
         ),
     ]
     self.pipeline = Pipeline()
     for node in self.nodes:
         self.pipeline.add_node(node)
 def traverse_dom_for_pspictures(self, element):
     # <pspicture><code>
     if element.tag == 'pspicture':
         if self.pspictureGeneratorVersion == '1.0':
             self.psPictureCount += 1
         src = self.pspicture_path(element)
         mediaNode = utils.create_node('media')
         mediaNode.append(utils.create_node('image'))
         mediaNode.attrib['alt'] = 'Image'
         mediaNode[0].attrib['src'] = src
         mediaNode.tail = element.tail
         element.getparent().replace(element, mediaNode)
     elif element.tag == 'tikzpicture':
         if self.pspictureGeneratorVersion == '1.0':
             self.tikzPictureCount += 1
         src = self.pspicture_path(element)
         mediaNode = utils.create_node('media')
         mediaNode.append(utils.create_node('image'))
         mediaNode.attrib['alt'] = 'Image'
         mediaNode[0].attrib['src'] = src
         mediaNode.tail = element.tail
         element.getparent().replace(element, mediaNode)
     else:
         children = element.getchildren()
         for child in children:
             self.traverse_dom_for_pspictures(child)
Exemplo n.º 4
0
def test_equal_and_hash():
    name = "test"
    node1 = create_node(name=name)
    node2 = create_node(name=name)
    node3 = create_node(name="other")
    assert node1 == node2
    assert hash(node1) == hash(node2)
    assert node1 != node3
    assert hash(node1) != hash(node3)
    assert node1 != name
Exemplo n.º 5
0
 def test_add_not_matching_node(self):
     node = create_node(name="not_matching", input_type="not_matching")
     self.assertRaises(ValueError, self.pipeline.add_node, node)
Exemplo n.º 6
0
NODE_COUNT = 50

# Get the connection to Node4j
g = connect()
g.delete_all()

# Create NODE_COUNT compute nodes with 2048 DISK_GB, 4096 MEMORY_MB, and 8
# VCPU. Each will also have 2PFs, one public and one private, and each PF will
# provide 8 VFs. We will also make each successive compute node have fewer and
# fewer resources available.

for i in range(NODE_COUNT):
    tx = g.begin()
    ratio = i / NODE_COUNT
    c = create_node(tx, "ComputeNode", "cn", i)
    d = create_node(tx,
                    "DISK_GB",
                    "disk",
                    i,
                    total=2048,
                    used=int((2048 * ratio)))
    tx.create(Relationship(c, "PROVIDES", d))
    r = create_node(tx,
                    "MEMORY_MB",
                    "ram",
                    i,
                    total=4096,
                    used=int((4096 * ratio)))
    tx.create(Relationship(c, "PROVIDES", r))
    v = create_node(tx, "VCPU", "vcpu", i, total=8, used=int((8 * ratio)))
Exemplo n.º 7
0
        node_count += 1
        source.on_next(node_count)

        return jsonify(response)

    source.pipe(ops.filter(lambda n: n == settings.N)).subscribe(
        lambda x: do_broadcast_ring())

    while True:
        pass

# non-bootstrap nodes
else:
    if len(sys.argv) != 3:
        print('Bad usage')
        exit(1)

    def current_node():
        return node

    ip = sys.argv[1]
    port = sys.argv[2]

    utils.startThreadedServer(app, ip, port)
    node = utils.create_node(ip, port)

    print('created node with id: ', node.id)

    while True:
        pass
Exemplo n.º 8
0
def test_name():
    try:
        _ = create_node().name
        raise AssertionError("No exception raised")
    except NotImplementedError:
        pass
Exemplo n.º 9
0
def test_execute():
    try:
        _ = create_node().execute(None)
        raise AssertionError("No exception raised")
    except NotImplementedError:
        pass
Exemplo n.º 10
0
def test_parameter_space():
    try:
        _ = create_node().parameter_space()
        raise AssertionError("No exception raised")
    except NotImplementedError:
        pass
Exemplo n.º 11
0
def test_output_type():
    try:
        _ = create_node().output_type
        raise AssertionError("No exception raised")
    except NotImplementedError:
        pass
    def traverse_dom_for_cnxml(self, element):
        # traverse every element in tree, find matching environments, transform
        for child in element:
            self.traverse_dom_for_cnxml(child)

        childIndex = 0
        while childIndex < len(element):
            child = element[childIndex]

            if child.tag in ["video", "simulation", "presentation", "box"]:
                child.tag = "todo-" + child.tag
                childIndex += 1

            elif child.tag == "image":
                # <image> <arguments/> <src/> </image>
                mediaNode = utils.create_node("media")
                mediaNode.append(utils.create_node("image"))
                mediaNode.attrib["alt"] = "Image"
                urlNode = child.find("src")
                if urlNode is not None:
                    mediaNode[0].attrib["src"] = urlNode.text.strip()
                else:
                    mediaNode[0].attrib["src"] = ""
                mediaNode.tail = child.tail
                element[childIndex] = mediaNode
                childIndex += 1

            elif child.tag == "figure":
                typeNode = child.find("type")
                if typeNode is not None:
                    typ = typeNode.text.strip()
                    child.attrib["type"] = typ
                    typeNode.tag = "label"
                    typeNode.text = {"figure": "Figure", "table": "Table"}[typ]
                childIndex += 1

            elif child.tag == "caption":
                if (len(child) == 1) and (child[0].tag == "para"):
                    utils.etree_replace_with_node_list(child, child[0], child[0])
                childIndex += 1

            elif child.tag == "activity":
                # <activity type="activity"><title/> <section><title/>...</section> </activity>
                child.tag = "example"
                child.append(
                    utils.create_node(
                        "label",
                        text={
                            "g_experiment": "General experiment",
                            "f_experiment": "Formal experiment",
                            "i_experiment": "Informal experiment",
                            "activity": "Activity",
                            "Investigation": "Investigation",
                            "groupdiscussion": "Group discussion",
                            "casestudy": "Case study",
                            "project": "Project",
                        }[child.attrib["type"]],
                    )
                )
                pos = 1
                while pos < len(child):
                    if child[pos].tag == "section":
                        sectionNode = child[pos]
                        assert sectionNode[0].tag == "title"
                        del child[pos]
                        child.insert(pos, utils.create_node("para"))
                        child[pos].append(utils.create_node("emphasis", text=sectionNode[0].text.strip()))
                        child[pos][-1].attrib["effect"] = "bold"
                        pos += 1
                        sectionChildren = sectionNode.getchildren()
                        for i in range(1, len(sectionChildren)):
                            child.insert(pos, sectionChildren[i])
                            pos += 1
                    else:
                        pos += 1
                childIndex += 1

            elif child.tag == "worked_example":
                # <worked_example> <title/> <question/> <answer> ... <workstep> <title/> ... </workstep> </answer> </worked_example>
                child.tag = "example"
                newSubChildren = []
                newSubChildren.append(utils.create_node("label", text="Worked example"))
                pos = 1
                for subChild in child:
                    if subChild.tag == "title":
                        newSubChildren.append(subChild)
                    elif subChild.tag == "question":
                        newSubChildren.append(subChild)
                        subChild.tag = "section"
                        subChild.append(utils.create_node("title", text="Question"))
                    elif subChild.tag == "answer":
                        newSubChildren.append(subChild)
                        subChild.tag = "section"
                        subChild.append(utils.create_node("title", text="Answer"))
                        for x in subChild:
                            if x.tag == "workstep":
                                x.tag = "section"
                childIndex += 1

            elif child.tag == "note":
                child.insert(
                    0,
                    utils.create_node(
                        "label",
                        text={"warning": "Warning", "tip": "Tip", "note": "Note", "aside": "Interesting Fact"}.get(
                            child.attrib["type"], child.attrib["type"]
                        ),
                    ),
                )
                childIndex += 1

            elif child.tag == "math_identity":
                del element[childIndex]  # Remove math_identity from DOM, still available as child
                ruleNode = utils.create_node("rule")
                ruleNode.attrib["type"] = "Identity"
                child.tag = "statement"
                ruleNode.append(child)
                element.insert(childIndex, ruleNode)
                childIndex += 1

            elif child.tag == "nuclear_notation":
                namespace = "http://www.w3.org/1998/Math/MathML"
                mathNode = utils.create_node("math", namespace=namespace)
                mathNode.append(utils.create_node("msubsup", namespace=namespace))
                mathNode[-1].append(utils.create_node("mo", namespace=namespace, text=u"\u200b"))
                mathNode[-1].append(utils.create_node("mn", namespace=namespace, text=child.find("atomic_number").text))
                if child.find("mass_number") is not None:
                    massNumber = child.find("mass_number").text
                else:
                    massNumber = u"\u200b"
                mathNode[-1].append(utils.create_node("mn", namespace=namespace, text=massNumber))
                mathNode.append(utils.create_node("mtext", namespace=namespace, text=child.find("symbol").text))

                mathNode.tail = child.tail
                element[childIndex] = mathNode
                childIndex += 1

            elif child.tag == "math_extension":
                child.tag = "note"
                titleNode = child.find("title")
                if titleNode is not None:
                    titleNode.tag = "label"
                    titleNode.text = u"Extension \u2014 " + titleNode.text.strip()
                else:
                    child.insert(0, utils.create_node("label", text="Extension"))
                bodyNode = child.find("body")
                utils.etree_replace_with_node_list(child, bodyNode, bodyNode)
                childIndex += 1

            elif child.tag == "section":
                # Check that it is not an activity section
                if child.getparent().tag != "activity":
                    shortCodeNode = child.find("shortcode")
                    if shortCodeNode is None:
                        if (child.attrib.get("type") not in ["subsubsection", "subsubsubsection"]) and (
                            child.find("title").text.strip() != "Chapter summary"
                        ):
                            print 'WARNING: no shortcode for section "%s"' % child.find("title").text.strip()
                            shortcode = "SHORTCODE"
                        else:
                            shortcode = None
                    else:
                        if (child.attrib.get("type") in ["subsubsection", "subsubsubsection"]) or (
                            child.find("title").text.strip() == "Chapter summary"
                        ):
                            print 'WARNING: section "%s" should not have a shortcode' % child.find("title").text.strip()
                        shortcode = shortCodeNode.text.strip()
                        child.remove(shortCodeNode)
                    """ # Commented out so that shortcodes do not get displayed
                    if shortcode is not None:
                        titleNode = child.find('title')
                        if len(titleNode) == 0:
                            if titleNode.text is None:
                                titleNode.text = ''
                            titleNode.text += ' [' + shortcode + ']'
                        else:
                            if titleNode[-1].tail is None:
                                titleNode[-1].tail = ''
                            titleNode[-1].tail += ' [' + shortcode + ']'
                    """
                childIndex += 1

            elif child.tag == "latex":
                if child.attrib.get("display", "inline") == "block":
                    delimiters = "[]"
                else:
                    delimiters = "()"
                if child.text is None:
                    child.text = ""
                child.text = "\\" + delimiters[0] + child.text
                if len(child) > 0:
                    if child[-1].text is None:
                        child[-1].tail = ""
                    child[-1].tail += "\\" + delimiters[1]
                else:
                    child.text += "\\" + delimiters[1]
                utils.etree_replace_with_node_list(element, child, child)
                childIndex += len(child)

            elif child.tag in ["chem_compound", "spec_note"]:
                assert len(child) == 0, "<chem_compound> element not expected to have sub-elements."
                if child.text is None:
                    child.text = ""
                child.text = child.text.strip()
                assert child.text != "", "<chem_compound> element must contain text."

                compoundText = child.text
                pos = 0
                textOpen = False
                while pos < len(compoundText):
                    if "a" <= compoundText[pos].lower() <= "z":
                        if not textOpen:
                            compoundText = compoundText[:pos] + r"\text{" + compoundText[pos:]
                            textOpen = True
                            pos += len(r"\text{") + 1
                        else:
                            pos += 1
                    else:
                        if textOpen:
                            compoundText = compoundText[:pos] + "}" + compoundText[pos:]
                            textOpen = False
                            pos += 2
                        else:
                            pos += 1
                if textOpen:
                    compoundText += "}"
                compoundXml = utils.xmlify(r"\(" + compoundText + r"\)")

                compoundDom = etree.fromstring(compoundXml[compoundXml.find("<formula ") : compoundXml.rfind("\n</p>")])
                utils.etree_replace_with_node_list(element, child, compoundDom)
                childIndex += len(child)

            else:
                path = [child.tag]
                node = child
                while True:
                    node = node.getparent()
                    if node is None:
                        break
                    path.append(node.tag)
                path.reverse()

                namespaces = {"m": "http://www.w3.org/1998/Math/MathML"}
                valid = [
                    "emphasis",
                    "para",
                    "figure/type",
                    "exercise/problem",
                    "exercise/title",
                    "exercise/shortcodes/entry/number",
                    "exercise/shortcodes/entry/shortcode",
                    "exercise/shortcodes/entry/url",
                    "exercise/shortcodes/entry/todo-content",
                    "list/item/label",
                    "table/tgroup/tbody/row/entry",
                    "table/tgroup/colspec",
                    "definition/term",
                    "definition/meaning",
                    "sup",
                    "sub",
                    "m:mn",
                    "m:mo",
                    "m:mi",
                    "m:msup",
                    "m:mrow",
                    "m:math",
                    "m:mtable",
                    "m:mtr",
                    "m:mtd",
                    "m:msub",
                    "m:mfrac",
                    "m:msqrt",
                    "m:mspace",
                    "m:mstyle",
                    "m:mfenced",
                    "m:mtext",
                    "m:mroot",
                    "m:mref",
                    "m:msubsup",
                    "m:munderover",
                    "m:munder",
                    "m:mover",
                    "m:mphantom",
                    "equation",
                    "link",
                    "quote",
                    "rule/title",
                    "rule/statement",
                    "rule/proof",
                    "section/title",
                    "section/shortcode",
                    "image/arguments",
                    "image/src",
                    "number/coeff",
                    "number/exp",
                    "number/base",
                    "nuclear_notation/mass_number",
                    "nuclear_notation/atomic_number",
                    "nuclear_notation/symbol",
                    "pspicture/code",
                    "pspicture/usepackage",
                    "tikzpicture/code",
                    "video/title",
                    "video/shortcode",
                    "video/url",
                    "video/width",
                    "video/height",
                    "worked_example/answer/workstep/title",
                    "worked_example/question",
                    "worked_example/title",
                    "activity/title",
                    "math_extension/title",
                    "math_extension/body",
                    "math_identity",
                    "document/content/title",
                    "document/content/content",
                    "simulation/title",
                    "simulation/shortcode",
                    "simulation/url",
                    "simulation/width",
                    "simulation/height",
                    "simulation/embed",
                    "presentation/title",
                    "presentation/url",
                    "presentation/shortcode",
                    "presentation/embed",
                    "box",
                ]
                validSet = set([])
                for entry in valid:
                    entry = entry.split("/")
                    for i in range(len(entry)):
                        if ":" in entry[i]:
                            entry[i] = entry[i].split(":")
                            assert len(entry[i]) == 2
                            entry[i] = "{%s}%s" % (namespaces[entry[i][0]], entry[i][1])
                        validSet.add(tuple(entry[: i + 1]))
                valid = validSet

                passed = False
                for entry in valid:
                    if tuple(path[-len(entry) :]) == entry:
                        passed = True
                        break
                if not passed:
                    path = "/".join(path)
                    for key, url in namespaces.iteritems():
                        path = path.replace("{%s}" % url, key + ":")
                    LOGGER.info("Unhandled element: " + path)

                childIndex += 1
    def process(self, markup):
        # Strip comments
        pos = 0
        while True:
            start = markup.find("<!--", pos)
            if start == -1:
                break
            stop = markup.find("-->", start + 4)
            assert stop != -1
            stop += 3
            markup = markup[:start] + markup[stop:]
            pos = start

        # Convert XML to DOM
        dom = etree.fromstring(markup)

        # Get CNXML+ version number
        versionNodeList = dom.xpath("/document/metadata/cnxml-version")
        if len(versionNodeList) == 0:
            # Insert version number into DOM
            version = "0.0"
        else:
            if len(versionNodeList) != 1:
                raise ValueError, "More than one cnxml-version node found in metadata section."
            version = versionNodeList[0].text.strip()

        # Convert v0.1 down to v0.0, if necessary
        if version == "0.1":
            for oldExercisesNode in dom.xpath("//exercises"):
                newExerciseNode = etree.Element("exercise")
                titleNode = oldExercisesNode.find("title")
                if titleNode is None:
                    titleNode = etree.Element("title")
                newExerciseNode.append(titleNode)

                for oldEntryNode in oldExercisesNode.xpath("./entry"):
                    shortcodeNode = oldEntryNode.find("shortcode")
                    if shortcodeNode is None:
                        shortcodeNode = etree.Element("shortcode")
                    problemNode = oldEntryNode.find("problem")
                    assert problemNode is not None
                    solutionNode = oldEntryNode.find("solution")
                    assert solutionNode is not None
                    newExerciseNode.append(problemNode)
                    newExerciseNode.append(etree.Element("shortcodes"))
                    newExerciseNode[-1].append(etree.Element("entry"))
                    newExerciseNode[-1][-1].append(shortcodeNode)
                    if not ((solutionNode.text is None) and (len(solutionNode) == 0)):
                        newExerciseNode[-1][-1].append(solutionNode)
                        solutionNode.tag = "content"
                    if solutionNode.attrib.get("url") is not None:
                        newExerciseNode[-1][-1].append(etree.Element("url"))
                        newExerciseNode[-1][-1][-1].text = solutionNode.attrib["url"]
                        del solutionNode.attrib["url"]
                newExerciseNode.tail = oldExercisesNode.tail
                oldExercisesNode.getparent().replace(oldExercisesNode, newExerciseNode)
            contentNode = dom.find("content")
            # Check if it's an end of chapter exercise block
            if (len(contentNode) == 1) and (contentNode[0].tag == "exercise"):
                contentNode[0].tag = "section"
                contentNode[0].attrib["type"] = "chapter"
        elif version != "0.0":
            raise ValueError, "Don't know how to handle CNXML+ version " + version

        # Check for a pspicture generator version tag
        metadataNode = dom.find("metadata")
        self.pspictureGeneratorVersion = "1.0"
        if metadataNode is not None:
            generatorNode = metadataNode.find("pspicture-generator-version")
            if generatorNode is not None:
                self.pspictureGeneratorVersion = generatorNode.text.strip()
        if self.pspictureGeneratorVersion == "1.0":
            LOGGER.info("Deprecation warning: pspicture-generator-version 1.0 is deprecated, please upgrade to 1.1")

        # Convert down to CNXML

        # Strip out <section type="chapter">
        dom = dom.find("content")
        assert len(dom) == 1  # only a single chapter section
        assert (dom[0].tag == "section") and (dom[0].attrib["type"] == "chapter")
        chapterNode = dom[0]
        titleNode = chapterNode[0]
        assert titleNode.tag == "title"
        contentsNodes = chapterNode[1:]
        del dom[0]
        dom.append(titleNode)
        dom.append(utils.create_node("content"))
        for node in contentsNodes:
            dom[-1].append(node)

        # Build chapter hash from title: for pspictures directory
        if titleNode.text is None:
            chapterTitle = ""
        else:
            chapterTitle = titleNode.text
        if self.pspictureGeneratorVersion == "1.0":
            self.chapterHash = hashlib.md5(chapterTitle.encode("utf-8")).hexdigest()

        # Hack to replace shortcode content with todo-content.
        # traverse_dom_for_cnxml() needs to change eventually to use
        # xpath rather than a recursive function.
        for contentNode in dom.xpath("//shortcodes/entry/content"):
            contentNode.tag = "todo-content"

        # Transform using new-style (xpath-based) transform
        self.transform(dom)

        # TOFIX: Remaining elements using old-style transform
        # Transform all elements in document, except pspictures
        self.traverse_dom_for_cnxml(dom)

        # Transform pspictures
        if self.pspictureGeneratorVersion == "1.0":
            self.psPictureCount = 0
            self.tikzPictureCount = 0
        self.traverse_dom_for_pspictures(dom)

        markup = utils.declutter_latex_tags(etree.tostring(dom)).strip()
        assert markup[:8] == "<content"
        assert markup[-10:] == "</content>"
        markup = '<?xml version="1.0"?>\n<document xmlns="http://cnx.rice.edu/cnxml"' + markup[8:-10] + "</document>\n"
        return markup
    def traverse_dom_for_cnxml(self, element):
        # traverse every element in tree, find matching environments, transform
        for child in element:
            self.traverse_dom_for_cnxml(child)

        childIndex = 0
        while childIndex < len(element):
            child = element[childIndex]

            if child.tag in ['video', 'simulation', 'presentation', 'box']:
                child.tag = 'todo-' + child.tag
                childIndex += 1

            elif child.tag == 'image':
                # <image> <arguments/> <src/> </image>
                mediaNode = utils.create_node('media')
                mediaNode.append(utils.create_node('image'))
                mediaNode.attrib['alt'] = 'Image'
                urlNode = child.find('src')
                if urlNode is not None:
                    mediaNode[0].attrib['src'] = urlNode.text.strip()
                else:
                    mediaNode[0].attrib['src'] = ''
                mediaNode.tail = child.tail
                element[childIndex] = mediaNode
                childIndex += 1

            elif child.tag == 'figure':
                typeNode = child.find('type')
                if typeNode is not None:
                    typ = typeNode.text.strip()
                    child.attrib['type'] = typ
                    typeNode.tag = 'label'
                    typeNode.text = {'figure': 'Figure', 'table': 'Table'}[typ]
                childIndex += 1

            elif child.tag == 'caption':
                if (len(child) == 1) and (child[0].tag == 'para'):
                    utils.etree_replace_with_node_list(child, child[0], child[0])
                childIndex += 1

            elif child.tag == 'activity':
                # <activity type="activity"><title/> <section><title/>...</section> </activity>
                child.tag = 'example'
                child.append(utils.create_node('label', text={
                    'g_experiment': 'General experiment',
                    'f_experiment': 'Formal experiment',
                    'i_experiment': 'Informal experiment',
                    'activity': 'Activity',
                    'Investigation': 'Investigation',
                    'groupdiscussion': 'Group discussion',
                    'casestudy': 'Case study',
                    'project': 'Project'}[child.attrib['type']]))
                pos = 1
                while pos < len(child):
                    if child[pos].tag == 'section':
                        sectionNode = child[pos]
                        assert sectionNode[0].tag == 'title'
                        del child[pos]
                        child.insert(pos, utils.create_node('para'))
                        child[pos].append(utils.create_node('emphasis', text=sectionNode[0].text.strip()))
                        child[pos][-1].attrib['effect'] = 'bold'
                        pos += 1
                        sectionChildren = sectionNode.getchildren()
                        for i in range(1, len(sectionChildren)):
                            child.insert(pos, sectionChildren[i])
                            pos += 1
                    else:
                        pos += 1
                childIndex += 1

            elif child.tag == 'worked_example':
                # <worked_example> <title/> <question/> <answer> ... <workstep> <title/> ... </workstep> </answer> </worked_example>
                child.tag = 'example'
                newSubChildren = []
                newSubChildren.append(utils.create_node('label', text="Worked example"))
                pos = 1
                for subChild in child:
                    if subChild.tag == 'title':
                        newSubChildren.append(subChild)
                    elif subChild.tag == 'question':
                        newSubChildren.append(subChild)
                        subChild.tag = 'section'
                        subChild.append(utils.create_node('title', text='Question'))
                    elif subChild.tag == 'answer':
                        newSubChildren.append(subChild)
                        subChild.tag = 'section'
                        subChild.append(utils.create_node('title', text='Answer'))
                        for x in subChild:
                            if x.tag == 'workstep':
                                x.tag = 'section'
                childIndex += 1

            elif child.tag == 'note':
                child.insert(0, utils.create_node('label', text={
                    'warning': 'Warning',
                    'tip': 'Tip',
                    'note': 'Note',
                    'aside': 'Interesting Fact'}.get(child.attrib['type'], child.attrib['type'])))
                childIndex += 1

            elif child.tag == 'math_identity':
                del element[childIndex] # Remove math_identity from DOM, still available as child
                ruleNode = utils.create_node('rule')
                ruleNode.attrib['type'] = 'Identity'
                child.tag = 'statement'
                ruleNode.append(child)
                element.insert(childIndex, ruleNode)
                childIndex += 1

            elif child.tag == 'nuclear_notation':
                namespace = 'http://www.w3.org/1998/Math/MathML'
                mathNode = utils.create_node('math', namespace=namespace)
                mathNode.append(utils.create_node('msubsup', namespace=namespace))
                mathNode[-1].append(utils.create_node('mo', namespace=namespace, text=u'\u200b'))
                mathNode[-1].append(utils.create_node('mn', namespace=namespace, text=child.find('atomic_number').text))
                if child.find('mass_number') is not None:
                    massNumber = child.find('mass_number').text
                else:
                    massNumber = u'\u200b'
                mathNode[-1].append(utils.create_node('mn', namespace=namespace, text=massNumber))
                mathNode.append(utils.create_node('mtext', namespace=namespace, text=child.find('symbol').text))

                mathNode.tail = child.tail
                element[childIndex] = mathNode
                childIndex += 1

            elif child.tag == 'math_extension':
                child.tag = 'note'
                titleNode = child.find('title')
                if titleNode is not None:
                    titleNode.tag = 'label'
                    titleNode.text = u'Extension \u2014 ' + titleNode.text.strip()
                else:
                    child.insert(0, utils.create_node('label', text='Extension'))
                bodyNode = child.find('body')
                utils.etree_replace_with_node_list(child, bodyNode, bodyNode)
                childIndex += 1

            elif child.tag == 'section':
                # Check that it is not an activity section
                if child.getparent().tag != 'activity':
                    shortCodeNode = child.find('shortcode')
                    if shortCodeNode is None:
                        if (child.attrib.get('type') not in ['subsubsection', 'subsubsubsection']) and (child.find('title').text.strip() != 'Chapter summary'):
                            print 'WARNING: no shortcode for section "%s"'%child.find('title').text.strip()
                            shortcode = 'SHORTCODE'
                        else:
                            shortcode = None
                    else:
                        if (child.attrib.get('type') in ['subsubsection', 'subsubsubsection']) or (child.find('title').text.strip() == 'Chapter summary'):
                            print 'WARNING: section "%s" should not have a shortcode'%child.find('title').text.strip()
                        shortcode = shortCodeNode.text.strip()
                        child.remove(shortCodeNode)
                    """ # Commented out so that shortcodes do not get displayed
                    if shortcode is not None:
                        titleNode = child.find('title')
                        if len(titleNode) == 0:
                            if titleNode.text is None:
                                titleNode.text = ''
                            titleNode.text += ' [' + shortcode + ']'
                        else:
                            if titleNode[-1].tail is None:
                                titleNode[-1].tail = ''
                            titleNode[-1].tail += ' [' + shortcode + ']'
                    """
                childIndex += 1

            elif child.tag == 'latex':
                if child.attrib.get('display', 'inline') == 'block':
                    delimiters = '[]'
                else:
                    delimiters = '()'
                if child.text is None:
                    child.text = ''
                child.text = '\\' + delimiters[0] + child.text
                if len(child) > 0:
                    if child[-1].text is None:
                        child[-1].tail = ''
                    child[-1].tail += '\\' + delimiters[1]
                else:
                    child.text += '\\' + delimiters[1]
                utils.etree_replace_with_node_list(element, child, child)
                childIndex += len(child)

            elif child.tag in ['chem_compound', 'spec_note']:
                assert len(child) == 0, "<chem_compound> element not expected to have sub-elements."
                if child.text is None:
                    child.text = ''
                child.text = child.text.strip()
                assert child.text != '', "<chem_compound> element must contain text."

                compoundText = child.text
                pos = 0
                textOpen = False
                while pos < len(compoundText):
                    if 'a' <= compoundText[pos].lower() <= 'z':
                        if not textOpen:
                            compoundText = compoundText[:pos] + r'\text{' + compoundText[pos:]
                            textOpen = True
                            pos += len(r'\text{') + 1
                        else:
                            pos += 1
                    else:
                        if textOpen:
                            compoundText = compoundText[:pos] + '}' + compoundText[pos:]
                            textOpen = False
                            pos += 2
                        else:
                            pos += 1
                if textOpen:
                    compoundText += '}'
                compoundXml = utils.xmlify(r'\(' + compoundText + r'\)')

                compoundDom = etree.fromstring(compoundXml[compoundXml.find('<formula '):compoundXml.rfind('\n</p>')])
                utils.etree_replace_with_node_list(element, child, compoundDom)
                childIndex += len(child)

            else:
                path = [child.tag]
                node = child
                while True:
                    node = node.getparent()
                    if node is None:
                        break
                    path.append(node.tag)
                path.reverse()

                namespaces = {'m': 'http://www.w3.org/1998/Math/MathML'}
                valid = [
                    'emphasis',
                    'para',
                    'figure/type',
                    'exercise/problem', 'exercise/title',
                    'exercise/shortcodes/entry/number', 'exercise/shortcodes/entry/shortcode', 'exercise/shortcodes/entry/url', 'exercise/shortcodes/entry/todo-content',
                    'list/item/label',
                    'table/tgroup/tbody/row/entry',
                    'table/tgroup/colspec',
                    'definition/term', 'definition/meaning',
                    'sup',
                    'sub',
                    'm:mn', 'm:mo', 'm:mi', 'm:msup', 'm:mrow', 'm:math', 'm:mtable', 'm:mtr', 'm:mtd', 'm:msub', 'm:mfrac', 'm:msqrt', 'm:mspace', 'm:mstyle', 'm:mfenced', 'm:mtext', 'm:mroot', 'm:mref', 'm:msubsup', 'm:munderover', 'm:munder', 'm:mover', 'm:mphantom',
                    'equation',
                    'link',
                    'quote',
                    'rule/title', 'rule/statement', 'rule/proof',

                    'section/title',
                    'section/shortcode',
                    'image/arguments',
                    'image/src',
                    'number/coeff', 'number/exp', 'number/base',
                    'nuclear_notation/mass_number', 'nuclear_notation/atomic_number', 'nuclear_notation/symbol',
                    'pspicture/code', 'pspicture/usepackage',
                    'tikzpicture/code',
                    'video/title', 'video/shortcode', 'video/url', 'video/width', 'video/height',
                    'worked_example/answer/workstep/title', 'worked_example/question', 'worked_example/title',
                    'activity/title',
                    'math_extension/title',
                    'math_extension/body',
                    'math_identity',
                    'document/content/title',
                    'document/content/content',
                    'simulation/title', 'simulation/shortcode', 'simulation/url', 'simulation/width', 'simulation/height', 'simulation/embed',
                    'presentation/title', 'presentation/url', 'presentation/shortcode', 'presentation/embed',
                    'box',
                ]
                validSet = set([])
                for entry in valid:
                    entry = entry.split('/')
                    for i in range(len(entry)):
                        if ':' in entry[i]:
                            entry[i] = entry[i].split(':')
                            assert len(entry[i]) == 2
                            entry[i] = '{%s}%s'%(namespaces[entry[i][0]], entry[i][1])
                        validSet.add(tuple(entry[:i+1]))
                valid = validSet

                passed = False
                for entry in valid:
                    if tuple(path[-len(entry):]) == entry:
                        passed = True
                        break
                if not passed:
                    path = '/'.join(path)
                    for key, url in namespaces.iteritems():
                        path = path.replace('{%s}'%url, key+':')
                    LOGGER.info('Unhandled element: ' + path)

                childIndex += 1