awips.dataaccess.ThriftClientRouter   B
last analyzed

Complexity

Total Complexity 47

Size/Duplication

Total Lines 258
Duplicated Lines 80.23 %

Importance

Changes 0
Metric Value
wmc 47
eloc 190
dl 207
loc 258
rs 8.64
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A ThriftClientRouter.getAvailableTimes() 6 6 1
A ThriftClientRouter.__init__() 3 3 1
A ThriftClientRouter.getRequiredIdentifiers() 12 12 4
A ThriftClientRouter.getIdentifierValues() 10 10 3
A ThriftClientRouter.getOptionalIdentifiers() 12 12 4
A ThriftClientRouter.getAvailableLocationNames() 9 9 3
B ThriftClientRouter.getGridData() 49 49 8
A ThriftClientRouter.setLazyLoadGridLatLon() 2 2 1
A LazyGridLatLon.__init__() 8 8 1
A ThriftClientRouter.getGeometryData() 24 24 4
A ThriftClientRouter.getAvailableParameters() 9 9 3
A ThriftClientRouter.getAvailableLevels() 5 5 1
A LazyGridLatLon.__call__() 12 12 2
A ThriftClientRouter.getSupportedDatatypes() 7 7 3
B ThriftClientRouter.newDataRequest() 17 17 7
A ThriftClientRouter.getNotificationFilter() 4 5 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like awips.dataaccess.ThriftClientRouter 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
#
2
# Routes requests to the Data Access Framework through Python Thrift.
3
#
4
#
5
#    SOFTWARE HISTORY
6
#
7
#    Date            Ticket#       Engineer       Description
8
#    ------------    ----------    -----------    --------------------------
9
#    05/21/13        2023          dgilling       Initial Creation.
10
#    01/06/14        2537          bsteffen       Share geometry WKT.
11
#    03/03/14        2673          bsteffen       Add ability to query only ref times.
12
#    07/22/14        3185          njensen        Added optional/default args to newDataRequest
13
#    07/23/14        3185          njensen        Added new methods
14
#    07/30/14        3185          njensen        Renamed valid identifiers to optional
15
#    06/30/15        4569          nabowle        Use hex WKB for geometries.
16
#    04/13/15        5379          tgurney        Add getIdentifierValues()
17
#    06/01/16        5587          tgurney        Add new signatures for
18
#                                                 getRequiredIdentifiers() and
19
#                                                 getOptionalIdentifiers()
20
#    08/01/16        2416          tgurney        Add getNotificationFilter()
21
#    10/13/16        5916          bsteffen       Correct grid shape, allow lazy grid lat/lon
22
#    10/26/16        5919          njensen        Speed up geometry creation in getGeometryData()
23
#
24
25
import numpy
26
import six
27
import shapely.wkb
28
29
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.impl import DefaultDataRequest
30
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetAvailableLocationNamesRequest
31
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetAvailableTimesRequest
32
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetGeometryDataRequest
33
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetGridDataRequest
34
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetGridLatLonRequest
35
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetAvailableParametersRequest
36
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetAvailableLevelsRequest
37
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetRequiredIdentifiersRequest
38
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetOptionalIdentifiersRequest
39
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetIdentifierValuesRequest
40
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetSupportedDatatypesRequest
41
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetNotificationFilterRequest
42
43
from awips import ThriftClient
44
from awips.dataaccess import PyGeometryData
45
from awips.dataaccess import PyGridData
46
47
48 View Code Duplication
class LazyGridLatLon(object):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
49
50
    def __init__(self, client, nx, ny, envelope, crsWkt):
51
        self._latLonGrid = None
52
        self._client = client
53
        self._request = GetGridLatLonRequest()
54
        self._request.setNx(nx)
55
        self._request.setNy(ny)
56
        self._request.setEnvelope(envelope)
57
        self._request.setCrsWkt(crsWkt)
58
59
    def __call__(self):
60
        # Its important that the data is cached internally so that if multiple
61
        # GridData are sharing the same delegate then they can also share a
62
        # single request for the LatLon information.
63
        if self._latLonGrid is None:
64
            response = self._client.sendRequest(self._request)
65
            nx = response.getNx()
66
            ny = response.getNy()
67
            latData = numpy.reshape(numpy.array(response.getLats()), (ny, nx))
68
            lonData = numpy.reshape(numpy.array(response.getLons()), (ny, nx))
69
            self._latLonGrid = (lonData, latData)
70
        return self._latLonGrid
71
72
73 View Code Duplication
class ThriftClientRouter(object):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
74
75
    def __init__(self, host='localhost'):
76
        self._client = ThriftClient.ThriftClient(host)
77
        self._lazyLoadGridLatLon = False
78
79
    def setLazyLoadGridLatLon(self, lazyLoadGridLatLon):
80
        self._lazyLoadGridLatLon = lazyLoadGridLatLon
81
82
    def getAvailableTimes(self, request, refTimeOnly):
83
        timesRequest = GetAvailableTimesRequest()
84
        timesRequest.setRequestParameters(request)
85
        timesRequest.setRefTimeOnly(refTimeOnly)
86
        response = self._client.sendRequest(timesRequest)
87
        return response
88
89
    def getGridData(self, request, times):
90
        gridDataRequest = GetGridDataRequest()
91
        gridDataRequest.setIncludeLatLonData(not self._lazyLoadGridLatLon)
92
        gridDataRequest.setRequestParameters(request)
93
        # if we have an iterable times instance, then the user must have asked
94
        # for grid data with the List of DataTime objects
95
        # else, we assume it was a single TimeRange that was meant for the
96
        # request
97
        try:
98
            iter(times)
99
            gridDataRequest.setRequestedTimes(times)
100
        except TypeError:
101
            gridDataRequest.setRequestedPeriod(times)
102
        response = self._client.sendRequest(gridDataRequest)
103
104
        locSpecificData = {}
105
        locNames = list(response.getSiteNxValues().keys())
106
        for location in locNames:
107
            nx = response.getSiteNxValues()[location]
108
            ny = response.getSiteNyValues()[location]
109
            if self._lazyLoadGridLatLon:
110
                envelope = response.getSiteEnvelopes()[location]
111
                crsWkt = response.getSiteCrsWkt()[location]
112
                delegate = LazyGridLatLon(
113
                    self._client, nx, ny, envelope, crsWkt)
114
                locSpecificData[location] = (nx, ny, delegate)
115
            else:
116
                latData = numpy.reshape(numpy.array(
117
                    response.getSiteLatGrids()[location]), (ny, nx))
118
                lonData = numpy.reshape(numpy.array(
119
                    response.getSiteLonGrids()[location]), (ny, nx))
120
                locSpecificData[location] = (nx, ny, (lonData, latData))
121
        retVal = []
122
        for gridDataRecord in response.getGridData():
123
            locationName = gridDataRecord.getLocationName()
124
            if locationName is not None:
125
                if six.PY2:
126
                    locData = locSpecificData[locationName]
127
                else:
128
                    locData = locSpecificData[locationName.encode('utf-8')]
129
            else:
130
                locData = locSpecificData[locationName]
131
            if self._lazyLoadGridLatLon:
132
                retVal.append(PyGridData.PyGridData(gridDataRecord, locData[
133
                              0], locData[1], latLonDelegate=locData[2]))
134
            else:
135
                retVal.append(PyGridData.PyGridData(
136
                    gridDataRecord, locData[0], locData[1], locData[2]))
137
        return retVal
138
139
    def getGeometryData(self, request, times):
140
        geoDataRequest = GetGeometryDataRequest()
141
        geoDataRequest.setRequestParameters(request)
142
        # if we have an iterable times instance, then the user must have asked
143
        # for geometry data with the List of DataTime objects
144
        # else, we assume it was a single TimeRange that was meant for the
145
        # request
146
        try:
147
            iter(times)
148
            geoDataRequest.setRequestedTimes(times)
149
        except TypeError:
150
            geoDataRequest.setRequestedPeriod(times)
151
        response = self._client.sendRequest(geoDataRequest)
152
        geometries = []
153
        for wkb in response.getGeometryWKBs():
154
            # the wkb is a numpy.ndarray of dtype int8
155
            # convert the bytearray to a byte string and load it
156
            geometries.append(shapely.wkb.loads(wkb.tostring()))
157
158
        retVal = []
159
        for geoDataRecord in response.getGeoData():
160
            geom = geometries[geoDataRecord.getGeometryWKBindex()]
161
            retVal.append(PyGeometryData.PyGeometryData(geoDataRecord, geom))
162
        return retVal
163
164
    def getAvailableLocationNames(self, request):
165
        locNamesRequest = GetAvailableLocationNamesRequest()
166
        locNamesRequest.setRequestParameters(request)
167
        response = self._client.sendRequest(locNamesRequest)
168
        if six.PY2:
169
            return response
170
        if response is not None:
171
            return [x.decode('utf-8') for x in response]
172
        return response
173
174
    def getAvailableParameters(self, request):
175
        paramReq = GetAvailableParametersRequest()
176
        paramReq.setRequestParameters(request)
177
        response = self._client.sendRequest(paramReq)
178
        if six.PY2:
179
            return response
180
        if response is not None:
181
            return [x.decode('utf-8') for x in response]
182
        return response
183
184
    def getAvailableLevels(self, request):
185
        levelReq = GetAvailableLevelsRequest()
186
        levelReq.setRequestParameters(request)
187
        response = self._client.sendRequest(levelReq)
188
        return response
189
190
    def getRequiredIdentifiers(self, request):
191
        if str(request) == request:
192
            # Handle old version getRequiredIdentifiers(str)
193
            request = self.newDataRequest(request)
194
        idReq = GetRequiredIdentifiersRequest()
195
        idReq.setRequest(request)
196
        response = self._client.sendRequest(idReq)
197
        if six.PY2:
198
            return response
199
        if response is not None:
200
            return [x.decode('utf-8') for x in response]
201
        return response
202
203
    def getOptionalIdentifiers(self, request):
204
        if str(request) == request:
205
            # Handle old version getOptionalIdentifiers(str)
206
            request = self.newDataRequest(request)
207
        idReq = GetOptionalIdentifiersRequest()
208
        idReq.setRequest(request)
209
        response = self._client.sendRequest(idReq)
210
        if six.PY2:
211
            return response
212
        if response is not None:
213
            return [x.decode('utf-8') for x in response]
214
        return response
215
216
    def getIdentifierValues(self, request, identifierKey):
217
        idValReq = GetIdentifierValuesRequest()
218
        idValReq.setIdentifierKey(identifierKey)
219
        idValReq.setRequestParameters(request)
220
        response = self._client.sendRequest(idValReq)
221
        if six.PY2:
222
            return response
223
        if response is not None:
224
            return [x.decode('utf-8') for x in response]
225
        return response
226
227
    def newDataRequest(self, datatype, parameters=[], levels=[], locationNames=[],
228
                       envelope=None, **kwargs):
229
        req = DefaultDataRequest()
230
        if datatype:
231
            req.setDatatype(datatype)
232
        if parameters:
233
            req.setParameters(*parameters)
234
        if levels:
235
            req.setLevels(*levels)
236
        if locationNames:
237
            req.setLocationNames(*locationNames)
238
        if envelope:
239
            req.setEnvelope(envelope)
240
        if kwargs:
241
            # any args leftover are assumed to be identifiers
242
            req.identifiers = kwargs
243
        return req
244
245
    def getSupportedDatatypes(self):
246
        response = self._client.sendRequest(GetSupportedDatatypesRequest())
247
        if six.PY2:
248
            return response
249
        if response is not None:
250
            return [x.decode('utf-8') for x in response]
251
        return response
252
253
    def getNotificationFilter(self, request):
254
        notifReq = GetNotificationFilterRequest()
255
        notifReq.setRequestParameters(request)
256
        response = self._client.sendRequest(notifReq)
257
        return response
258