def test_xml_create_tag_tag_order_all_single(load_inpxml):

    from masci_tools.util.xml.common_functions import eval_xpath
    from masci_tools.util.xml.xml_setters_basic import xml_create_tag

    xmltree, schema_dict = load_inpxml(TEST_INPXML_PATH)
    root = xmltree.getroot()

    tags = [
        'cutoffs', 'scfLoop', 'coreElectrons', 'xcFunctional', 'magnetism',
        'soc', 'prodBasis', 'expertModes', 'geometryOptimization', 'ldaU'
    ]

    node = eval_xpath(root, '/fleurInput/calculationSetup')

    assert [child.tag for child in node.iterchildren()] == tags

    order = [
        'cutoffs', 'scfLoop', 'coreElectrons', 'xcFunctional', 'magnetism',
        'test_tag', 'soc', 'prodBasis', 'expertModes', 'geometryOptimization',
        'ldaU'
    ]
    xmltree = xml_create_tag(xmltree,
                             '/fleurInput/calculationSetup',
                             'test_tag',
                             tag_order=order)

    node = eval_xpath(root, '/fleurInput/calculationSetup')

    assert [child.tag for child in node.iterchildren()] == order
def test_xml_create_tag_element_append(load_inpxml):

    from masci_tools.util.xml.common_functions import eval_xpath
    from masci_tools.util.xml.xml_setters_basic import xml_create_tag
    from lxml import etree

    new_element = etree.Element('test_tag')
    new_element.attrib['test_attrib'] = 'test'

    xmltree, schema_dict = load_inpxml(TEST_INPXML_PATH)
    root = xmltree.getroot()

    tags = [
        'cutoffs', 'scfLoop', 'coreElectrons', 'xcFunctional', 'magnetism',
        'soc', 'prodBasis', 'expertModes', 'geometryOptimization', 'ldaU'
    ]

    node = eval_xpath(root, '/fleurInput/calculationSetup')

    assert [child.tag for child in node.iterchildren()] == tags

    xmltree = xml_create_tag(xmltree, '/fleurInput/calculationSetup',
                             new_element)

    node = eval_xpath(root, '/fleurInput/calculationSetup')

    tags.append('test_tag')
    assert [child.tag for child in node.iterchildren()] == tags
    assert [child.attrib.items()
            for child in node.iterchildren()][-1] == [('test_attrib', 'test')]
def test_xml_create_tag_misaligned_order(load_inpxml):
    """
    Test automatic correction of order
    """

    from masci_tools.util.xml.xml_setters_basic import xml_create_tag
    from masci_tools.util.xml.common_functions import eval_xpath

    xmltree, schema_dict = load_inpxml(TEST_INPXML_PATH)
    root = xmltree.getroot()

    xml_create_tag(xmltree, '/fleurInput/atomSpecies/species',
                   'ldaU')  #This creates an invalid order
    xml_create_tag(xmltree, '/fleurInput/atomSpecies/species', 'lo')

    order = [
        'mtSphere', 'atomicCutoffs', 'electronConfig', 'energyParameters',
        'ldaU', 'lo'
    ]
    with pytest.raises(
            ValueError,
            match=r'Existing order does not correspond to tag_order list'):
        xml_create_tag(xmltree,
                       '/fleurInput/atomSpecies/species',
                       'ldaU',
                       tag_order=order,
                       correct_order=False)

    with pytest.warns(
            UserWarning,
            match=
            r'Existing order does not correspond to tag_order list. Correcting it'
    ):
        xml_create_tag(xmltree,
                       '/fleurInput/atomSpecies/species',
                       'ldaU',
                       tag_order=order)

    tags = [[
        'mtSphere', 'atomicCutoffs', 'electronConfig', 'energyParameters',
        'ldaU', 'ldaU', 'lo', 'lo', 'lo'
    ],
            [
                'mtSphere', 'atomicCutoffs', 'electronConfig',
                'energyParameters', 'ldaU', 'ldaU', 'lo', 'lo'
            ]]

    nodes = eval_xpath(root, '/fleurInput/atomSpecies/species')

    assert [[child.tag for child in node.iterchildren()]
            for node in nodes] == tags
def test_xml_create_tag_errors(load_inpxml):

    from masci_tools.util.xml.xml_setters_basic import xml_create_tag

    xmltree, schema_dict = load_inpxml(TEST_INPXML_PATH)

    with pytest.raises(
            ValueError,
            match=
            r"Could not create tag 'test_tag' because atleast one subtag is missing."
    ):
        xml_create_tag(xmltree, '/fleurInput/calculationSetup/not_existent',
                       'test_tag')

    order = [
        'mtSphere', 'atomicCutoffs', 'electronConfig', 'energyParameters', 'lo'
    ]
    with pytest.raises(
            ValueError,
            match=r"The tag 'test_tag' was not found in the order list"):
        xml_create_tag(xmltree,
                       '/fleurInput/atomSpecies/species',
                       'test_tag',
                       tag_order=order)

    order = ['atomicCutoffs', 'electronConfig', 'energyParameters', 'lo']
    with pytest.raises(
            ValueError,
            match=
            r"Did not find existing elements in the tag_order list: {'mtSphere'}"
    ):
        xml_create_tag(xmltree,
                       '/fleurInput/atomSpecies/species',
                       'lo',
                       tag_order=order)
def test_xml_create_tag_tag_order_multiple_occurrences_list(load_inpxml):

    from masci_tools.util.xml.common_functions import eval_xpath
    from masci_tools.util.xml.xml_setters_basic import xml_create_tag

    xmltree, schema_dict = load_inpxml(TEST_INPXML_PATH)
    root = xmltree.getroot()

    tags = [[
        'mtSphere',
        'atomicCutoffs',
        'electronConfig',
        'energyParameters',
        'lo',
        'lo',
    ], [
        'mtSphere', 'atomicCutoffs', 'electronConfig', 'energyParameters', 'lo'
    ]]

    nodes = eval_xpath(root, '/fleurInput/atomSpecies/species')

    assert [[child.tag for child in node.iterchildren()]
            for node in nodes] == tags

    order = [
        'test_tag', 'mtSphere', 'atomicCutoffs', 'electronConfig',
        'energyParameters', 'lo'
    ]
    xmltree = xml_create_tag(xmltree,
                             '/fleurInput/atomSpecies/species',
                             'test_tag',
                             tag_order=order,
                             occurrences=[-1])

    tags = [[
        'mtSphere',
        'atomicCutoffs',
        'electronConfig',
        'energyParameters',
        'lo',
        'lo',
    ],
            [
                'test_tag', 'mtSphere', 'atomicCutoffs', 'electronConfig',
                'energyParameters', 'lo'
            ]]

    nodes = eval_xpath(root, '/fleurInput/atomSpecies/species')

    assert [[child.tag for child in node.iterchildren()]
            for node in nodes] == tags
def xml_create_tag_schema_dict(xmltree,
                               schema_dict,
                               xpath,
                               base_xpath,
                               element,
                               create_parents=False,
                               occurrences=None):
    """
    This method evaluates an xpath expression and creates a tag in a xmltree under the
    returned nodes.
    If there are no nodes evaluated the subtags can be created with `create_parents=True`

    The tag is always inserted in the correct place if a order is enforced by the schema

    :param xmltree: an xmltree that represents inp.xml
    :param schema_dict: InputSchemaDict containing all information about the structure of the input
    :param xpath: a path where to place a new tag
    :param base_xpath: path where to place a new tag without complex syntax ([] conditions and so on)
    :param element: a tag name or etree Element to be created
    :param create_parents: bool optional (default False), if True and the given xpath has no results the
                           the parent tags are created recursively
    :param occurrences: int or list of int. Which occurence of the parent nodes to create a tag.
                        By default all nodes are used.

    :raises ValueError: If the nodes are missing and `create_parents=False`

    :returns: xmltree with created tags
    """
    from masci_tools.util.xml.xml_setters_basic import xml_create_tag
    from masci_tools.util.xml.common_functions import check_complex_xpath, split_off_tag

    check_complex_xpath(xmltree, base_xpath, xpath)

    tag_info = schema_dict['tag_info'][base_xpath]

    if not etree.iselement(element):
        #Get original case of the tag
        element_name = (tag_info['simple']
                        | tag_info['complex']).original_case[element]
        try:
            element = etree.Element(element_name)
        except ValueError as exc:
            raise ValueError(
                f"Failed to construct etree Element from '{element_name}'"
            ) from exc
    else:
        element_name = element.tag

    if len(tag_info['order']) == 0:
        tag_order = None
    else:
        tag_order = tag_info['order']

    several_tags = element_name in tag_info['several']

    parent_nodes = eval_xpath(xmltree, xpath, list_return=True)

    if len(parent_nodes) == 0:
        if create_parents:
            parent_xpath, parent_name = split_off_tag(base_xpath)
            complex_parent_xpath, _ = split_off_tag(xpath)
            xmltree = xml_create_tag_schema_dict(xmltree,
                                                 schema_dict,
                                                 complex_parent_xpath,
                                                 parent_xpath,
                                                 parent_name,
                                                 create_parents=create_parents)
        else:
            raise ValueError(
                f"Could not create tag '{element_name}' because atleast one subtag is missing. "
                'Use create=True to create the subtags')

    return xml_create_tag(xmltree,
                          xpath,
                          element,
                          tag_order=tag_order,
                          occurrences=occurrences,
                          several=several_tags)