Cancelled
Push — master ( be7f51...60e470 )
by Michael
11:04
created

ThriftClientRouter.getOptionalIdentifiers()   A

Complexity

Conditions 4

Size

Total Lines 12
Code Lines 11

Duplication

Lines 12
Ratio 100 %

Importance

Changes 0
Metric Value
eloc 11
dl 12
loc 12
rs 9.85
c 0
b 0
f 0
cc 4
nop 2
1
#
2
# Routes requests to the Data Access Framework through Python Thrift.
3
#
4
#
5
#
6
#    SOFTWARE HISTORY
7
#
8
#    Date            Ticket#       Engineer       Description
9
#    ------------    ----------    -----------    --------------------------
10
#    05/21/13        2023          dgilling       Initial Creation.
11
#    01/06/14        2537          bsteffen       Share geometry WKT.
12
#    03/03/14        2673          bsteffen       Add ability to query only ref times.
13
#    07/22/14        3185          njensen        Added optional/default args to newDataRequest
14
#    07/23/14        3185          njensen        Added new methods
15
#    07/30/14        3185          njensen        Renamed valid identifiers to optional
16
#    06/30/15        4569          nabowle        Use hex WKB for geometries.
17
#    04/13/15        5379          tgurney        Add getIdentifierValues()
18
#    06/01/16        5587          tgurney        Add new signatures for
19
#                                                 getRequiredIdentifiers() and
20
#                                                 getOptionalIdentifiers()
21
#    08/01/16        2416          tgurney        Add getNotificationFilter()
22
#    10/13/16        5916          bsteffen       Correct grid shape, allow lazy grid lat/lon
23
#    10/26/16        5919          njensen        Speed up geometry creation in getGeometryData()
24
#
25
26
27
import numpy
28
import six
29
import shapely.wkb
30
31
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.impl import DefaultDataRequest
32
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetAvailableLocationNamesRequest
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (111/100).

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

Loading history...
33
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetAvailableTimesRequest
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (103/100).

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

Loading history...
34
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetGeometryDataRequest
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (101/100).

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

Loading history...
35
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetGridDataRequest
36
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetGridLatLonRequest
37
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetAvailableParametersRequest
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
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetAvailableLevelsRequest
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (104/100).

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

Loading history...
39
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetRequiredIdentifiersRequest
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...
40
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetOptionalIdentifiersRequest
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...
41
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetIdentifierValuesRequest
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (105/100).

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

Loading history...
42
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetSupportedDatatypesRequest
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (107/100).

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

Loading history...
43
from dynamicserialize.dstypes.com.raytheon.uf.common.dataaccess.request import GetNotificationFilterRequest
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (107/100).

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

Loading history...
44
45
from awips import ThriftClient
46
from awips.dataaccess import PyGeometryData
47
from awips.dataaccess import PyGridData
48
49
50 View Code Duplication
class LazyGridLatLon(object):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
Unused Code introduced by
The variable __class__ seems to be unused.
Loading history...
51
52
    def __init__(self, client, nx, ny, envelope, crsWkt):
0 ignored issues
show
best-practice introduced by
Too many arguments (6/5)
Loading history...
53
        self._latLonGrid = None
54
        self._client = client
55
        self._request = GetGridLatLonRequest()
56
        self._request.setNx(nx)
57
        self._request.setNy(ny)
58
        self._request.setEnvelope(envelope)
59
        self._request.setCrsWkt(crsWkt)
60
61
    def __call__(self):
62
        # Its important that the data is cached internally so that if multiple
63
        # GridData are sharing the same delegate then they can also share a
64
        # single request for the LatLon information.
65
        if self._latLonGrid is None:
66
            response = self._client.sendRequest(self._request)
67
            nx = response.getNx()
68
            ny = response.getNy()
69
            latData = numpy.reshape(numpy.array(response.getLats()), (ny, nx))
70
            lonData = numpy.reshape(numpy.array(response.getLons()), (ny, nx))
71
            self._latLonGrid = (lonData, latData)
72
        return self._latLonGrid
73
74
75 View Code Duplication
class ThriftClientRouter(object):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
Unused Code introduced by
The variable __class__ seems to be unused.
Loading history...
76
77
    def __init__(self, host='localhost'):
78
        self._client = ThriftClient.ThriftClient(host)
79
        self._lazyLoadGridLatLon = False
80
81
    def setLazyLoadGridLatLon(self, lazyLoadGridLatLon):
82
        self._lazyLoadGridLatLon = lazyLoadGridLatLon
83
84
    def getAvailableTimes(self, request, refTimeOnly):
85
        timesRequest = GetAvailableTimesRequest()
86
        timesRequest.setRequestParameters(request)
87
        timesRequest.setRefTimeOnly(refTimeOnly)
88
        response = self._client.sendRequest(timesRequest)
89
        return response
90
91
    def getGridData(self, request, times):
0 ignored issues
show
Comprehensibility introduced by
This function exceeds the maximum number of variables (20/15).
Loading history...
92
        gridDataRequest = GetGridDataRequest()
93
        gridDataRequest.setIncludeLatLonData(not self._lazyLoadGridLatLon)
94
        gridDataRequest.setRequestParameters(request)
95
        # if we have an iterable times instance, then the user must have asked
96
        # for grid data with the List of DataTime objects
97
        # else, we assume it was a single TimeRange that was meant for the
98
        # request
99
        try:
100
            iter(times)
101
            gridDataRequest.setRequestedTimes(times)
102
        except TypeError:
103
            gridDataRequest.setRequestedPeriod(times)
104
        response = self._client.sendRequest(gridDataRequest)
105
106
        locSpecificData = {}
107
        locNames = list(response.getSiteNxValues().keys())
108
        for location in locNames:
109
            nx = response.getSiteNxValues()[location]
110
            ny = response.getSiteNyValues()[location]
111
            if self._lazyLoadGridLatLon:
112
                envelope = response.getSiteEnvelopes()[location]
113
                crsWkt = response.getSiteCrsWkt()[location]
114
                delegate = LazyGridLatLon(
115
                    self._client, nx, ny, envelope, crsWkt)
116
                locSpecificData[location] = (nx, ny, delegate)
117
            else:
118
                latData = numpy.reshape(numpy.array(
119
                    response.getSiteLatGrids()[location]), (ny, nx))
120
                lonData = numpy.reshape(numpy.array(
121
                    response.getSiteLonGrids()[location]), (ny, nx))
122
                locSpecificData[location] = (nx, ny, (lonData, latData))
123
        retVal = []
124
        for gridDataRecord in response.getGridData():
125
            locationName = gridDataRecord.getLocationName()
126
            if locationName is not None:
127
                if six.PY2:
128
                    locData = locSpecificData[locationName]
129
                else:
130
                    locData = locSpecificData[locationName.encode('utf-8')]
131
            else:
132
                locData = locSpecificData[locationName]
133
            if self._lazyLoadGridLatLon:
134
                retVal.append(PyGridData.PyGridData(gridDataRecord, locData[
135
                              0], locData[1], latLonDelegate=locData[2]))
136
            else:
137
                retVal.append(PyGridData.PyGridData(
138
                    gridDataRecord, locData[0], locData[1], locData[2]))
139
        return retVal
140
141
    def getGeometryData(self, request, times):
142
        geoDataRequest = GetGeometryDataRequest()
143
        geoDataRequest.setRequestParameters(request)
144
        # if we have an iterable times instance, then the user must have asked
145
        # for geometry data with the List of DataTime objects
146
        # else, we assume it was a single TimeRange that was meant for the
147
        # request
148
        try:
149
            iter(times)
150
            geoDataRequest.setRequestedTimes(times)
151
        except TypeError:
152
            geoDataRequest.setRequestedPeriod(times)
153
        response = self._client.sendRequest(geoDataRequest)
154
        geometries = []
155
        for wkb in response.getGeometryWKBs():
156
            # the wkb is a numpy.ndarray of dtype int8
157
            # convert the bytearray to a byte string and load it
158
            geometries.append(shapely.wkb.loads(wkb.tostring()))
159
160
        retVal = []
161
        for geoDataRecord in response.getGeoData():
162
            geom = geometries[geoDataRecord.getGeometryWKBindex()]
163
            retVal.append(PyGeometryData.PyGeometryData(geoDataRecord, geom))
164
        return retVal
165
166
    def getAvailableLocationNames(self, request):
167
        locNamesRequest = GetAvailableLocationNamesRequest()
168
        locNamesRequest.setRequestParameters(request)
169
        response = self._client.sendRequest(locNamesRequest)
170
        if six.PY2:
171
            return response
172
        if response is not None:
173
            return [x.decode('utf-8') for x in response]
174
        return response
175
176
    def getAvailableParameters(self, request):
177
        paramReq = GetAvailableParametersRequest()
178
        paramReq.setRequestParameters(request)
179
        response = self._client.sendRequest(paramReq)
180
        if six.PY2:
181
            return response
182
        if response is not None:
183
            return [x.decode('utf-8') for x in response]
184
        return response
185
186
    def getAvailableLevels(self, request):
187
        levelReq = GetAvailableLevelsRequest()
188
        levelReq.setRequestParameters(request)
189
        response = self._client.sendRequest(levelReq)
190
        return response
191
192
    def getRequiredIdentifiers(self, request):
193
        if str(request) == request:
194
            # Handle old version getRequiredIdentifiers(str)
195
            request = self.newDataRequest(request)
196
        idReq = GetRequiredIdentifiersRequest()
197
        idReq.setRequest(request)
198
        response = self._client.sendRequest(idReq)
199
        if six.PY2:
200
            return response
201
        if response is not None:
202
            return [x.decode('utf-8') for x in response]
203
        return response
204
205
    def getOptionalIdentifiers(self, request):
206
        if str(request) == request:
207
            # Handle old version getOptionalIdentifiers(str)
208
            request = self.newDataRequest(request)
209
        idReq = GetOptionalIdentifiersRequest()
210
        idReq.setRequest(request)
211
        response = self._client.sendRequest(idReq)
212
        if six.PY2:
213
            return response
214
        if response is not None:
215
            return [x.decode('utf-8') for x in response]
216
        return response
217
218
    def getIdentifierValues(self, request, identifierKey):
219
        idValReq = GetIdentifierValuesRequest()
220
        idValReq.setIdentifierKey(identifierKey)
221
        idValReq.setRequestParameters(request)
222
        response = self._client.sendRequest(idValReq)
223
        if six.PY2:
224
            return response
225
        if response is not None:
226
            return [x.decode('utf-8') for x in response]
227
        return response
228
229
    def newDataRequest(self, datatype, parameters=[], levels=[], locationNames=[], envelope=None, **kwargs):
1 ignored issue
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...
Bug Best Practice introduced by
The default value [] might cause unintended side-effects.

Objects as default values are only created once in Python and not on each invocation of the function. If the default object is modified, this modification is carried over to the next invocation of the method.

# Bad:
# If array_param is modified inside the function, the next invocation will
# receive the modified object.
def some_function(array_param=[]):
    # ...

# Better: Create an array on each invocation
def some_function(array_param=None):
    array_param = array_param or []
    # ...
Loading history...
best-practice introduced by
Too many arguments (6/5)
Loading history...
Coding Style introduced by
This method could be written as a function/class method.

If a method does not access any attributes of the class, it could also be implemented as a function or static method. This can help improve readability. For example

class Foo:
    def some_method(self, x, y):
        return x + y;

could be written as

class Foo:
    @classmethod
    def some_method(cls, x, y):
        return x + y;
Loading history...
230
        req = DefaultDataRequest()
231
        if datatype:
232
            req.setDatatype(datatype)
233
        if parameters:
234
            req.setParameters(*parameters)
235
        if levels:
236
            req.setLevels(*levels)
237
        if locationNames:
238
            req.setLocationNames(*locationNames)
239
        if envelope:
240
            req.setEnvelope(envelope)
241
        if kwargs:
242
            # any args leftover are assumed to be identifiers
243
            req.identifiers = kwargs
244
        return req
245
246
    def getSupportedDatatypes(self):
247
        response = self._client.sendRequest(GetSupportedDatatypesRequest())
248
        if six.PY2:
249
            return response
250
        if response is not None:
251
            return [x.decode('utf-8') for x in response]
252
        return response
253
254
    def getNotificationFilter(self, request):
255
        notifReq = GetNotificationFilterRequest()
256
        notifReq.setRequestParameters(request)
257
        response = self._client.sendRequest(notifReq)
258
        return response
259