We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Sorting causes problems with import
### Description / Beschreibung

When using different `uri_prefix`es for, e.g. a domain import, the sorting by `uri` destroys the order in the file, and parent Attributes are imported *after* their children (with a different `uri_prefix` earlier in the alphabet). This is the problematic line:

https://github.com/rdmorganiser/rdmo/blob/master/rdmo/core/xml.py#L52

### Expected behaviour / Erwartetes Verhalten

I am not sure if we could get rid of the sorting, we could also sort by `path` (which would give the field some meaning in the xml again). Ideas? @triole @MyPyDavid 


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `rdmo/core/xml.py`
Content:
```
1 import logging
2 import re
3 
4 import defusedxml.ElementTree as ET
5 
6 log = logging.getLogger(__name__)
7 
8 
9 def read_xml_file(file_name):
10     try:
11         return ET.parse(file_name).getroot()
12     except Exception as e:
13         log.error('Xml parsing error: ' + str(e))
14 
15 
16 def parse_xml_string(string):
17     try:
18         return ET.fromstring(string)
19     except Exception as e:
20         log.error('Xml parsing error: ' + str(e))
21 
22 
23 def flat_xml_to_elements(treenode):
24     elements = []
25     ns_map = get_ns_map(treenode)
26     uri_attrib = get_ns_tag('dc:uri', ns_map)
27 
28     for node in treenode:
29 
30         element = {
31             'uri': get_uri(node, ns_map),
32             'type': node.tag
33         }
34 
35         for subnode in node:
36             tag = strip_ns(subnode.tag, ns_map)
37 
38             if uri_attrib in subnode.attrib:
39                 # this node has an uri!
40                 element[tag] = subnode.attrib[uri_attrib]
41             elif 'lang' in subnode.attrib:
42                 # this node has the lang attribute!
43                 element['%s_%s' % (tag, subnode.attrib['lang'])] = subnode.text
44             elif list(subnode):
45                 # this node is a list!
46                 element[tag] = [subsubnode.attrib[uri_attrib] for subsubnode in subnode]
47             else:
48                 element[tag] = subnode.text
49 
50         elements.append(element)
51 
52     elements = sort_elements_by_key(elements, 'uri')
53     return elements
54 
55 
56 def get_ns_tag(tag, ns_map):
57     tag_split = tag.split(':')
58     try:
59         return '{%s}%s' % (ns_map[tag_split[0]], tag_split[1])
60     except KeyError:
61         return None
62 
63 
64 def get_ns_map(treenode):
65     ns_map = {}
66     treestring = ET.tostring(treenode, encoding='utf8', method='xml')
67 
68     for match in re.finditer(r'(xmlns:)(.*?)(=")(.*?)(")', str(treestring)):
69         if match:
70             ns_map[match.group(2)] = match.group(4)
71 
72     return ns_map
73 
74 
75 def get_uri(treenode, ns_map):
76     if treenode is not None:
77         ns_tag = get_ns_tag('dc:uri', ns_map)
78         if ns_tag is not None:
79             return treenode.attrib.get(ns_tag)
80 
81 
82 def strip_ns(tag, ns_map):
83     for ns in ns_map.values():
84         if tag.startswith('{%s}' % ns):
85             return tag.replace('{%s}' % ns, '')
86     return tag
87 
88 
89 def filter_elements_by_type(elements, element_type):
90     for element in elements:
91         if element['type'] == element_type:
92             yield element
93 
94 
95 def sort_elements_by_key(dictlist, key, reverse=False):
96     return sorted(dictlist, key=lambda k: k[key], reverse=reverse)
97
```

Path: `rdmo/core/constants.py`
Content:
```
1 from django.utils.translation import gettext_lazy as _
2 
3 VALUE_TYPE_TEXT = 'text'
4 VALUE_TYPE_URL = 'url'
5 VALUE_TYPE_INTEGER = 'integer'
6 VALUE_TYPE_FLOAT = 'float'
7 VALUE_TYPE_BOOLEAN = 'boolean'
8 VALUE_TYPE_DATETIME = 'datetime'
9 VALUE_TYPE_OPTIONS = 'option'
10 VALUE_TYPE_EMAIL = 'email'
11 VALUE_TYPE_PHONE = 'phone'
12 VALUE_TYPE_FILE = 'file'
13 VALUE_TYPE_CHOICES = (
14     (VALUE_TYPE_TEXT, _('Text')),
15     (VALUE_TYPE_URL, _('URL')),
16     (VALUE_TYPE_INTEGER, _('Integer')),
17     (VALUE_TYPE_FLOAT, _('Float')),
18     (VALUE_TYPE_BOOLEAN, _('Boolean')),
19     (VALUE_TYPE_DATETIME, _('Datetime')),
20     (VALUE_TYPE_EMAIL, _('E-mail')),
21     (VALUE_TYPE_PHONE, _('Phone')),
22     (VALUE_TYPE_OPTIONS, _('Option')),
23     (VALUE_TYPE_FILE, _('File'))
24 )
25 
26 PERMISSIONS = {
27     'condition': (
28         'conditions.add_condition', 'conditions.change_condition', 'conditions.delete_condition'
29     ),
30     'attribute': (
31         'domain.add_attribute', 'domain.change_attribute', 'domain.delete_attribute'
32     ),
33     'optionset': (
34         'options.add_optionset', 'options.change_optionset', 'options.delete_optionset'
35     ),
36     'option': (
37         'options.add_option', 'options.change_option', 'options.delete_option'
38     ),
39     'catalog': (
40         'questions.add_catalog', 'questions.change_catalog', 'questions.delete_catalog'
41     ),
42     'section': (
43         'questions.add_section', 'questions.change_section', 'questions.delete_section'
44     ),
45     'questionset': (
46         'questions.add_questionset', 'questions.change_questionset', 'questions.delete_questionset'
47     ),
48     'question': (
49         'questions.add_question', 'questions.change_question', 'questions.delete_question'
50     ),
51     'task': (
52         'tasks.add_task', 'tasks.change_task', 'tasks.delete_task'
53     ),
54     'view': (
55         'views.add_view', 'views.change_view', 'views.delete_view'
56     )
57 }
58
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/rdmo/core/constants.py b/rdmo/core/constants.py
--- a/rdmo/core/constants.py
+++ b/rdmo/core/constants.py
@@ -55,3 +55,12 @@
         'views.add_view', 'views.change_view', 'views.delete_view'
     )
 }
+
+IMPORT_SORT_ORDER = (
+    'domain',
+    'conditions',
+    'options',
+    'questions',
+    'tasks',
+    'views'
+)
diff --git a/rdmo/core/xml.py b/rdmo/core/xml.py
--- a/rdmo/core/xml.py
+++ b/rdmo/core/xml.py
@@ -3,6 +3,9 @@
 
 import defusedxml.ElementTree as ET
 
+from .constants import IMPORT_SORT_ORDER
+
+
 log = logging.getLogger(__name__)
 
 
@@ -49,7 +52,7 @@
 
         elements.append(element)
 
-    elements = sort_elements_by_key(elements, 'uri')
+    elements = sorted(elements, key=sort_elements)
     return elements
 
 
@@ -92,5 +95,13 @@
             yield element
 
 
-def sort_elements_by_key(dictlist, key, reverse=False):
-    return sorted(dictlist, key=lambda k: k[key], reverse=reverse)
+def sort_elements(element):
+    # remove the uri_prefix from the uri to create the key to be sorted by
+    sort_key = element['uri'].replace(element['uri_prefix'], '')
+
+    # remove the app name from the sort_key and replace it by its import order
+    for i, item in enumerate(IMPORT_SORT_ORDER):
+        if sort_key.startswith(item):
+            sort_key = sort_key.replace(item, str(i))
+
+    return sort_key

{"golden_diff": "diff --git a/rdmo/core/constants.py b/rdmo/core/constants.py\n--- a/rdmo/core/constants.py\n+++ b/rdmo/core/constants.py\n@@ -55,3 +55,12 @@\n         'views.add_view', 'views.change_view', 'views.delete_view'\n     )\n }\n+\n+IMPORT_SORT_ORDER = (\n+    'domain',\n+    'conditions',\n+    'options',\n+    'questions',\n+    'tasks',\n+    'views'\n+)\ndiff --git a/rdmo/core/xml.py b/rdmo/core/xml.py\n--- a/rdmo/core/xml.py\n+++ b/rdmo/core/xml.py\n@@ -3,6 +3,9 @@\n \n import defusedxml.ElementTree as ET\n \n+from .constants import IMPORT_SORT_ORDER\n+\n+\n log = logging.getLogger(__name__)\n \n \n@@ -49,7 +52,7 @@\n \n         elements.append(element)\n \n-    elements = sort_elements_by_key(elements, 'uri')\n+    elements = sorted(elements, key=sort_elements)\n     return elements\n \n \n@@ -92,5 +95,13 @@\n             yield element\n \n \n-def sort_elements_by_key(dictlist, key, reverse=False):\n-    return sorted(dictlist, key=lambda k: k[key], reverse=reverse)\n+def sort_elements(element):\n+    # remove the uri_prefix from the uri to create the key to be sorted by\n+    sort_key = element['uri'].replace(element['uri_prefix'], '')\n+\n+    # remove the app name from the sort_key and replace it by its import order\n+    for i, item in enumerate(IMPORT_SORT_ORDER):\n+        if sort_key.startswith(item):\n+            sort_key = sort_key.replace(item, str(i))\n+\n+    return sort_key\n", "issue": "Sorting causes problems with import\n### Description / Beschreibung\r\n\r\nWhen using different `uri_prefix`es for, e.g. a domain import, the sorting by `uri` destroys the order in the file, and parent Attributes are imported *after* their children (with a different `uri_prefix` earlier in the alphabet). This is the problematic line:\r\n\r\nhttps://github.com/rdmorganiser/rdmo/blob/master/rdmo/core/xml.py#L52\r\n\r\n### Expected behaviour / Erwartetes Verhalten\r\n\r\nI am not sure if we could get rid of the sorting, we could also sort by `path` (which would give the field some meaning in the xml again). Ideas? @triole @MyPyDavid \r\n\n", "before_files": [{"content": "import logging\nimport re\n\nimport defusedxml.ElementTree as ET\n\nlog = logging.getLogger(__name__)\n\n\ndef read_xml_file(file_name):\n    try:\n        return ET.parse(file_name).getroot()\n    except Exception as e:\n        log.error('Xml parsing error: ' + str(e))\n\n\ndef parse_xml_string(string):\n    try:\n        return ET.fromstring(string)\n    except Exception as e:\n        log.error('Xml parsing error: ' + str(e))\n\n\ndef flat_xml_to_elements(treenode):\n    elements = []\n    ns_map = get_ns_map(treenode)\n    uri_attrib = get_ns_tag('dc:uri', ns_map)\n\n    for node in treenode:\n\n        element = {\n            'uri': get_uri(node, ns_map),\n            'type': node.tag\n        }\n\n        for subnode in node:\n            tag = strip_ns(subnode.tag, ns_map)\n\n            if uri_attrib in subnode.attrib:\n                # this node has an uri!\n                element[tag] = subnode.attrib[uri_attrib]\n            elif 'lang' in subnode.attrib:\n                # this node has the lang attribute!\n                element['%s_%s' % (tag, subnode.attrib['lang'])] = subnode.text\n            elif list(subnode):\n                # this node is a list!\n                element[tag] = [subsubnode.attrib[uri_attrib] for subsubnode in subnode]\n            else:\n                element[tag] = subnode.text\n\n        elements.append(element)\n\n    elements = sort_elements_by_key(elements, 'uri')\n    return elements\n\n\ndef get_ns_tag(tag, ns_map):\n    tag_split = tag.split(':')\n    try:\n        return '{%s}%s' % (ns_map[tag_split[0]], tag_split[1])\n    except KeyError:\n        return None\n\n\ndef get_ns_map(treenode):\n    ns_map = {}\n    treestring = ET.tostring(treenode, encoding='utf8', method='xml')\n\n    for match in re.finditer(r'(xmlns:)(.*?)(=\")(.*?)(\")', str(treestring)):\n        if match:\n            ns_map[match.group(2)] = match.group(4)\n\n    return ns_map\n\n\ndef get_uri(treenode, ns_map):\n    if treenode is not None:\n        ns_tag = get_ns_tag('dc:uri', ns_map)\n        if ns_tag is not None:\n            return treenode.attrib.get(ns_tag)\n\n\ndef strip_ns(tag, ns_map):\n    for ns in ns_map.values():\n        if tag.startswith('{%s}' % ns):\n            return tag.replace('{%s}' % ns, '')\n    return tag\n\n\ndef filter_elements_by_type(elements, element_type):\n    for element in elements:\n        if element['type'] == element_type:\n            yield element\n\n\ndef sort_elements_by_key(dictlist, key, reverse=False):\n    return sorted(dictlist, key=lambda k: k[key], reverse=reverse)\n", "path": "rdmo/core/xml.py"}, {"content": "from django.utils.translation import gettext_lazy as _\n\nVALUE_TYPE_TEXT = 'text'\nVALUE_TYPE_URL = 'url'\nVALUE_TYPE_INTEGER = 'integer'\nVALUE_TYPE_FLOAT = 'float'\nVALUE_TYPE_BOOLEAN = 'boolean'\nVALUE_TYPE_DATETIME = 'datetime'\nVALUE_TYPE_OPTIONS = 'option'\nVALUE_TYPE_EMAIL = 'email'\nVALUE_TYPE_PHONE = 'phone'\nVALUE_TYPE_FILE = 'file'\nVALUE_TYPE_CHOICES = (\n    (VALUE_TYPE_TEXT, _('Text')),\n    (VALUE_TYPE_URL, _('URL')),\n    (VALUE_TYPE_INTEGER, _('Integer')),\n    (VALUE_TYPE_FLOAT, _('Float')),\n    (VALUE_TYPE_BOOLEAN, _('Boolean')),\n    (VALUE_TYPE_DATETIME, _('Datetime')),\n    (VALUE_TYPE_EMAIL, _('E-mail')),\n    (VALUE_TYPE_PHONE, _('Phone')),\n    (VALUE_TYPE_OPTIONS, _('Option')),\n    (VALUE_TYPE_FILE, _('File'))\n)\n\nPERMISSIONS = {\n    'condition': (\n        'conditions.add_condition', 'conditions.change_condition', 'conditions.delete_condition'\n    ),\n    'attribute': (\n        'domain.add_attribute', 'domain.change_attribute', 'domain.delete_attribute'\n    ),\n    'optionset': (\n        'options.add_optionset', 'options.change_optionset', 'options.delete_optionset'\n    ),\n    'option': (\n        'options.add_option', 'options.change_option', 'options.delete_option'\n    ),\n    'catalog': (\n        'questions.add_catalog', 'questions.change_catalog', 'questions.delete_catalog'\n    ),\n    'section': (\n        'questions.add_section', 'questions.change_section', 'questions.delete_section'\n    ),\n    'questionset': (\n        'questions.add_questionset', 'questions.change_questionset', 'questions.delete_questionset'\n    ),\n    'question': (\n        'questions.add_question', 'questions.change_question', 'questions.delete_question'\n    ),\n    'task': (\n        'tasks.add_task', 'tasks.change_task', 'tasks.delete_task'\n    ),\n    'view': (\n        'views.add_view', 'views.change_view', 'views.delete_view'\n    )\n}\n", "path": "rdmo/core/constants.py"}], "after_files": [{"content": "import logging\nimport re\n\nimport defusedxml.ElementTree as ET\n\nfrom .constants import IMPORT_SORT_ORDER\n\n\nlog = logging.getLogger(__name__)\n\n\ndef read_xml_file(file_name):\n    try:\n        return ET.parse(file_name).getroot()\n    except Exception as e:\n        log.error('Xml parsing error: ' + str(e))\n\n\ndef parse_xml_string(string):\n    try:\n        return ET.fromstring(string)\n    except Exception as e:\n        log.error('Xml parsing error: ' + str(e))\n\n\ndef flat_xml_to_elements(treenode):\n    elements = []\n    ns_map = get_ns_map(treenode)\n    uri_attrib = get_ns_tag('dc:uri', ns_map)\n\n    for node in treenode:\n\n        element = {\n            'uri': get_uri(node, ns_map),\n            'type': node.tag\n        }\n\n        for subnode in node:\n            tag = strip_ns(subnode.tag, ns_map)\n\n            if uri_attrib in subnode.attrib:\n                # this node has an uri!\n                element[tag] = subnode.attrib[uri_attrib]\n            elif 'lang' in subnode.attrib:\n                # this node has the lang attribute!\n                element['%s_%s' % (tag, subnode.attrib['lang'])] = subnode.text\n            elif list(subnode):\n                # this node is a list!\n                element[tag] = [subsubnode.attrib[uri_attrib] for subsubnode in subnode]\n            else:\n                element[tag] = subnode.text\n\n        elements.append(element)\n\n    elements = sorted(elements, key=sort_elements)\n    return elements\n\n\ndef get_ns_tag(tag, ns_map):\n    tag_split = tag.split(':')\n    try:\n        return '{%s}%s' % (ns_map[tag_split[0]], tag_split[1])\n    except KeyError:\n        return None\n\n\ndef get_ns_map(treenode):\n    ns_map = {}\n    treestring = ET.tostring(treenode, encoding='utf8', method='xml')\n\n    for match in re.finditer(r'(xmlns:)(.*?)(=\")(.*?)(\")', str(treestring)):\n        if match:\n            ns_map[match.group(2)] = match.group(4)\n\n    return ns_map\n\n\ndef get_uri(treenode, ns_map):\n    if treenode is not None:\n        ns_tag = get_ns_tag('dc:uri', ns_map)\n        if ns_tag is not None:\n            return treenode.attrib.get(ns_tag)\n\n\ndef strip_ns(tag, ns_map):\n    for ns in ns_map.values():\n        if tag.startswith('{%s}' % ns):\n            return tag.replace('{%s}' % ns, '')\n    return tag\n\n\ndef filter_elements_by_type(elements, element_type):\n    for element in elements:\n        if element['type'] == element_type:\n            yield element\n\n\ndef sort_elements(element):\n    # remove the uri_prefix from the uri to create the key to be sorted by\n    sort_key = element['uri'].replace(element['uri_prefix'], '')\n\n    # remove the app name from the sort_key and replace it by its import order\n    for i, item in enumerate(IMPORT_SORT_ORDER):\n        if sort_key.startswith(item):\n            sort_key = sort_key.replace(item, str(i))\n\n    return sort_key\n", "path": "rdmo/core/xml.py"}, {"content": "from django.utils.translation import gettext_lazy as _\n\nVALUE_TYPE_TEXT = 'text'\nVALUE_TYPE_URL = 'url'\nVALUE_TYPE_INTEGER = 'integer'\nVALUE_TYPE_FLOAT = 'float'\nVALUE_TYPE_BOOLEAN = 'boolean'\nVALUE_TYPE_DATETIME = 'datetime'\nVALUE_TYPE_OPTIONS = 'option'\nVALUE_TYPE_EMAIL = 'email'\nVALUE_TYPE_PHONE = 'phone'\nVALUE_TYPE_FILE = 'file'\nVALUE_TYPE_CHOICES = (\n    (VALUE_TYPE_TEXT, _('Text')),\n    (VALUE_TYPE_URL, _('URL')),\n    (VALUE_TYPE_INTEGER, _('Integer')),\n    (VALUE_TYPE_FLOAT, _('Float')),\n    (VALUE_TYPE_BOOLEAN, _('Boolean')),\n    (VALUE_TYPE_DATETIME, _('Datetime')),\n    (VALUE_TYPE_EMAIL, _('E-mail')),\n    (VALUE_TYPE_PHONE, _('Phone')),\n    (VALUE_TYPE_OPTIONS, _('Option')),\n    (VALUE_TYPE_FILE, _('File'))\n)\n\nPERMISSIONS = {\n    'condition': (\n        'conditions.add_condition', 'conditions.change_condition', 'conditions.delete_condition'\n    ),\n    'attribute': (\n        'domain.add_attribute', 'domain.change_attribute', 'domain.delete_attribute'\n    ),\n    'optionset': (\n        'options.add_optionset', 'options.change_optionset', 'options.delete_optionset'\n    ),\n    'option': (\n        'options.add_option', 'options.change_option', 'options.delete_option'\n    ),\n    'catalog': (\n        'questions.add_catalog', 'questions.change_catalog', 'questions.delete_catalog'\n    ),\n    'section': (\n        'questions.add_section', 'questions.change_section', 'questions.delete_section'\n    ),\n    'questionset': (\n        'questions.add_questionset', 'questions.change_questionset', 'questions.delete_questionset'\n    ),\n    'question': (\n        'questions.add_question', 'questions.change_question', 'questions.delete_question'\n    ),\n    'task': (\n        'tasks.add_task', 'tasks.change_task', 'tasks.delete_task'\n    ),\n    'view': (\n        'views.add_view', 'views.change_view', 'views.delete_view'\n    )\n}\n\nIMPORT_SORT_ORDER = (\n    'domain',\n    'conditions',\n    'options',\n    'questions',\n    'tasks',\n    'views'\n)\n", "path": "rdmo/core/constants.py"}]}