Passed
Push — master ( 882e36...94d274 )
by Dominik
16:21
created

_dom_to_way()   B

Complexity

Conditions 4

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 4

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 4
c 3
b 0
f 0
dl 0
loc 25
ccs 14
cts 14
cp 1
crap 4
rs 8.5806
1
# ~*~ coding: utf-8 ~*~
2
#-
3
# OSMAlchemy - OpenStreetMap to SQLAlchemy bridge
4
# Copyright (c) 2016 Dominik George <[email protected]>
5
#
6
# Permission is hereby granted, free of charge, to any person obtaining a copy
7
# of this software and associated documentation files (the "Software"), to deal
8
# in the Software without restriction, including without limitation the rights
9
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
# copies of the Software, and to permit persons to whom the Software is
11
# furnished to do so, subject to the following conditions:
12
#
13
# The above copyright notice and this permission notice shall be included in all
14
# copies or substantial portions of the Software.
15
#
16
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
# SOFTWARE.
23
#
24
# Alternatively, you are free to use OSMAlchemy under Simplified BSD, The
25
# MirOS Licence, GPL-2+, LGPL-2.1+, AGPL-3+ or the same terms as Python
26
# itself.
27
28 1
""" Utility code for the OSMAlchemy database OSMAlchemy. """
29
30 1
import dateutil.parser
31 1
import operator
0 ignored issues
show
introduced by
standard import "import operator" comes before "import dateutil.parser"
Loading history...
Unused Code introduced by
The import operator seems to be unused.
Loading history...
32 1
import xml.dom.minidom as minidom
0 ignored issues
show
introduced by
standard import "import xml.dom.minidom as minidom" comes before "import dateutil.parser"
Loading history...
33 1
from sqlalchemy.sql.elements import BinaryExpression, BooleanClauseList, BindParameter
0 ignored issues
show
Unused Code introduced by
Unused BinaryExpression imported from sqlalchemy.sql.elements
Loading history...
Unused Code introduced by
Unused BooleanClauseList imported from sqlalchemy.sql.elements
Loading history...
Unused Code introduced by
Unused BindParameter imported from sqlalchemy.sql.elements
Loading history...
34 1
from sqlalchemy.sql.annotation import AnnotatedColumn
0 ignored issues
show
Bug introduced by
The name AnnotatedColumn does not seem to exist in module sqlalchemy.sql.annotation.
Loading history...
Unused Code introduced by
Unused AnnotatedColumn imported from sqlalchemy.sql.annotation
Loading history...
35
36 1
def _import_osm_dom(osma, session, dom):
37
    """ Import a DOM tree from OSM XML into an OSMAlchemy model.
38
39
    Not called directly; used by _import_osm_xml and _import_osm_file.
40
    """
41
42 1
    def _dom_attrs_to_any(e, element):
0 ignored issues
show
Coding Style Naming introduced by
The name e does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
43 1
        if "version" in e.attributes.keys():
44 1
            element.version = int(e.attributes["version"].value)
45 1
        if "changeset" in e.attributes.keys():
46 1
            element.changeset = int(e.attributes["changeset"].value)
47 1
        if "user" in e.attributes.keys():
48 1
            element.user = e.attributes["user"].value
49 1
        if "uid" in e.attributes.keys():
50 1
            element.uid = int(e.attributes["uid"].value)
51 1
        if "visible" in e.attributes.keys():
52 1
            element.visible = True if e.attributes["visible"].value == "true" else False
53 1
        if "timestamp" in e.attributes.keys():
54 1
            element.timestamp = dateutil.parser.parse(e.attributes["timestamp"].value)
55
56 1
    def _dom_tags_to_any(e, element):
0 ignored issues
show
Coding Style Naming introduced by
The name e does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
57
        # Remove all tags previously associated with element
58 1
        for k in list(element.tags.keys()):
59
            del element.tags[k]
60
61
        # Iterate over all <tag /> nodes in the DOM element
62 1
        for t in e.getElementsByTagName("tag"):
0 ignored issues
show
Coding Style Naming introduced by
The name t does not conform to the variable naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
63
            # Append data to tags
64 1
            element.tags[t.attributes["k"].value] = t.attributes["v"].value
65
66 1
    def _dom_to_node(e):
0 ignored issues
show
Coding Style Naming introduced by
The name e does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
67 1
        with session.no_autoflush:
68
            # Get mandatory node id
69 1
            id = int(e.attributes["id"].value)
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in id.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
Coding Style Naming introduced by
The name id does not conform to the variable naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
70
71
            # Find object in database and create if non-existent
72 1
            node = session.query(osma.node).filter_by(id=id).scalar()
73 1
            if node is None:
74 1
                node = osma.node(id=id)
75
76
            # Store mandatory latitude and longitude
77 1
            node.latitude = e.attributes["lat"].value
78 1
            node.longitude = e.attributes["lon"].value
79
80
            # Store other attributes and tags
81 1
            _dom_attrs_to_any(e, node)
82 1
            _dom_tags_to_any(e, node)
83
84
        # Add to session
85 1
        session.add(node)
86 1
        session.commit()
87
88 1
    def _dom_to_way(e):
0 ignored issues
show
Coding Style Naming introduced by
The name e does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
89 1
        with session.no_autoflush:
90
            # Get mandatory way id
91 1
            id = int(e.attributes["id"].value)
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in id.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
Coding Style Naming introduced by
The name id does not conform to the variable naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
92
93
            # Find object in database and create if non-existent
94 1
            way = session.query(osma.way).filter_by(id=id).scalar()
95 1
            if way is None:
96 1
                way = osma.way(id=id)
97
98
            # Find all related nodes
99 1
            for n in e.getElementsByTagName("nd"):
0 ignored issues
show
Coding Style Naming introduced by
The name n does not conform to the variable naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
100
                # Get node id and find object
101 1
                ref = int(n.attributes["ref"].value)
102 1
                node = session.query(osma.node).filter_by(id=ref).one()
103
                # Append to nodes in way
104 1
                way.nodes.append(node)
105
106
            # Store other attributes and tags
107 1
            _dom_attrs_to_any(e, way)
108 1
            _dom_tags_to_any(e, way)
109
110
        # Add to session
111 1
        session.add(way)
112 1
        session.commit()
113
114 1
    def _dom_to_relation(e):
0 ignored issues
show
Coding Style Naming introduced by
The name e does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
115 1
        with session.no_autoflush:
116
            # Get mandatory way id
117 1
            id = int(e.attributes["id"].value)
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in id.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
Coding Style Naming introduced by
The name id does not conform to the variable naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
118
119
            # Find object in database and create if non-existent
120 1
            relation = session.query(osma.relation).filter_by(id=id).scalar()
121 1
            if relation is None:
122 1
                relation = osma.relation(id=id)
123
124
            # Find all members
125 1
            for m in e.getElementsByTagName("member"):
0 ignored issues
show
Coding Style Naming introduced by
The name m does not conform to the variable naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
126
                # Get member attributes
127 1
                ref = int(m.attributes["ref"].value)
128 1
                type = m.attributes["type"].value
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in type.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
129
130 1
                if "role" in m.attributes.keys():
131 1
                    role = m.attributes["role"].value
132
                else:
133
                    role = ""
134 1
                element = session.query(osma.element).filter_by(id=ref, type=type).scalar()
135 1
                if element is None:
136
                    # We do not know the member yet, create a stub
137 1
                    if type == "node":
138 1
                        element = osma.node(id=ref)
139 1
                    elif type == "way":
140 1
                        element = osma.way(id=ref)
141 1
                    elif type == "relation":
142 1
                        element = osma.relation(id=ref)
143
                    # We need to commit here because element could be repeated
144 1
                    session.add(element)
145 1
                    session.commit()
146
                # Append to members
147 1
                relation.members.append((element, role))
148
149
            # Store other attributes and tags
150 1
            _dom_attrs_to_any(e, relation)
151 1
            _dom_tags_to_any(e, relation)
152
153
        # Add to session
154 1
        session.add(relation)
155 1
        session.commit()
156
157
    # Get root element
158 1
    osm = dom.documentElement
159
160
    # Iterate over children to find nodes, ways and relations
161 1
    for e in osm.childNodes:
0 ignored issues
show
Coding Style Naming introduced by
The name e does not conform to the variable naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
162
        # Determine element type
163 1
        if e.nodeName == "node":
164 1
            _dom_to_node(e)
165 1
        elif e.nodeName == "way":
166 1
            _dom_to_way(e)
167 1
        elif e.nodeName == "relation":
168 1
            _dom_to_relation(e)
169
170
        # Rmove children
171 1
        e.unlink()
172
173 1
def _import_osm_xml(osma, session, xml):
174
    """ Import a string in OSM XML format into an OSMAlchemy model.
175
176
      osma - reference to the OSMAlchemy model instance
177
      session - an SQLAlchemy session
178
      xml - string containing the XML data
179
    """
180
181
    # Parse string into DOM structure
182
    dom = minidom.parseString(xml)
183
184
    return _import_osm_dom(osma, session, dom)
185
186 1
def _import_osm_file(osma, session, file):
187
    """ Import a file in OSM XML format into an OSMAlchemy model.
188
189
      osma - reference to the OSMAlchemy model instance
190
      session - an SQLAlchemy session
191
      path - path to the file to import or open file object
192
    """
193
194
    # Parse document into DOM structure
195 1
    dom = minidom.parse(file)
196
197
    return _import_osm_dom(osma, session, dom)
198