Test Failed
Push — master ( b34d36...3849ae )
by Dominik
08:34
created

_generate_triggers()   F

Complexity

Conditions 11

Size

Total Lines 73

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 116.8864

Importance

Changes 11
Bugs 0 Features 0
Metric Value
cc 11
c 11
b 0
f 0
dl 0
loc 73
ccs 1
cts 23
cp 0.0434
crap 116.8864
rs 3.4615

1 Method

Rating   Name   Duplication   Size   Complexity  
D _query_compiling() 0 64 10

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like _generate_triggers() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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
""" Trigger code for live OSMAlchemy/Overpass integration. """
29
30 1
import datetime
31 1
from sqlalchemy import inspect
0 ignored issues
show
Unused Code introduced by
Unused inspect imported from sqlalchemy
Loading history...
32 1
from sqlalchemy.event import listens_for
33 1
from sqlalchemy.orm import Query
34 1
from weakref import WeakSet
0 ignored issues
show
introduced by
standard import "from weakref import WeakSet" comes before "from sqlalchemy import inspect"
Loading history...
35
36 1
from .util.db import _import_osm_xml
37 1
from .util.online import _get_elements_by_query, _where_to_tree, _trees_to_overpassql, _normalise_overpassql
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (108/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
38
39 1
def _generate_triggers(osmalchemy, osmcachedquery, maxage=60*60*24):
40
    """ Generates the triggers for online functionality.
41
42
      osmalchemy - reference to the OSMAlchemy instance to be configured
43
      maxage - maximum age of objects before they are updated online, in seconds
44
    """
45
46
    _visited_queries = WeakSet()
47
48
    @listens_for(Query, "before_compile")
49
    def _query_compiling(query):
0 ignored issues
show
Unused Code introduced by
The variable _query_compiling seems to be unused.
Loading history...
50
        # Get the session associated with the query:
51
        session = query.session
52
53
        # Skip if the session was in a trigger before
54
        # Prevents recursion in import code
55
        if hasattr(session, "_osmalchemy_in_trigger"):
56
            return
57
58
        # Prevent recursion by skipping already-seen queries
59
        if query in _visited_queries:
60
            return
61
        else:
62
            _visited_queries.add(query)
63
64
        # Check whether this query affects our model
65
        affected_models = set([c["type"] for c in query.column_descriptions])
66
        our_models = set([osmalchemy.node, osmalchemy.way,  osmalchemy.relation,
0 ignored issues
show
Coding Style introduced by
Exactly one space required after comma
Loading history...
67
                          osmalchemy.element])
68
        if affected_models.isdisjoint(our_models):
69
            # None of our models is affected
70
            return
71
72
        # Check whether this query filters elements
73
        # Online update will only run on a specified set, not all data
74
        if query.whereclause is None:
75
            # No filters
76
            return
77
78
        # Analyse where clause looking for all looked-up fields
79
        trees = {}
80
        for target in our_models.intersection(affected_models):
81
            # Build expression trees first
82
            trees[target.__name__] = _where_to_tree(query.whereclause, target)
83
84
        # Compile to OverpassQL
85
        oql = _trees_to_overpassql(trees)
86
87
        # Look up query in cache
88
        hashed_oql = hash(_normalise_overpassql(oql))
89
        cached_query = session.query(osmcachedquery).filter_by(oql_hash=hashed_oql).scalar()
90
        # Check age if cached query was found
91
        if cached_query:
92
            timediff = datetime.datetime.now() - cached_query.oql_queried
93
            if timediff.seconds < maxage:
94
                # Return and do nothing if query was run recently
95
                return
96
97
        # Run query online
98
        xml = _get_elements_by_query(osmalchemy._overpass, oql)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _overpass was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
99
100
        # Import data
101
        session._osmalchemy_in_trigger = True
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like _osmalchemy_in_trigger was declared protected and should not be accessed from this context.

Prefixing a member variable _ is usually regarded as the equivalent of declaring it with protected visibility that exists in other languages. Consequentially, such a member should only be accessed from the same class or a child class:

class MyParent:
    def __init__(self):
        self._x = 1;
        self.y = 2;

class MyChild(MyParent):
    def some_method(self):
        return self._x    # Ok, since accessed from a child class

class AnotherClass:
    def some_method(self, instance_of_my_child):
        return instance_of_my_child._x   # Would be flagged as AnotherClass is not
                                         # a child class of MyParent
Loading history...
102
        _import_osm_xml(osmalchemy, session, xml)
103
        del session._osmalchemy_in_trigger
104
105
        # Store or update query time
106
        if not cached_query:
107
            cached_query = osmcachedquery()
108
            cached_query.oql_hash = hashed_oql
109
        cached_query.oql_queried = datetime.datetime.now()
110
        session.add(cached_query)
111
        session.commit()
112