Completed
Push — master ( 99e121...b5c7b7 )
by Michael
08:48
created

awips.dataaccess.DataAccessLayer.getSynopticObs()   B

Complexity

Conditions 6

Size

Total Lines 19
Code Lines 16

Duplication

Lines 19
Ratio 100 %

Importance

Changes 0
Metric Value
eloc 16
dl 19
loc 19
rs 8.6666
c 0
b 0
f 0
cc 6
nop 1
1
#
2
# Published interface for awips.dataaccess package
3
#
4
#
5
#     SOFTWARE HISTORY
6
#
7
#    Date            Ticket#       Engineer       Description
8
#    ------------    ----------    -----------    --------------------------
9
#    12/10/12                      njensen        Initial Creation.
10
#    Feb 14, 2013    1614          bsteffen       refactor data access framework
11
#                                                 to use single request.
12
#    04/10/13        1871          mnash          move getLatLonCoords to JGridData and add default args
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...
13
#    05/29/13        2023          dgilling       Hook up ThriftClientRouter.
14
#    03/03/14        2673          bsteffen       Add ability to query only ref times.
15
#    07/22/14        3185          njensen        Added optional/default args to newDataRequest
16
#    07/30/14        3185          njensen        Renamed valid identifiers to optional
17
#    Apr 26, 2015    4259          njensen        Updated for new JEP API
18
#    Apr 13, 2016    5379          tgurney        Add getIdentifierValues()
19
#    Jun 01, 2016    5587          tgurney        Add new signatures for
20
#                                                 getRequiredIdentifiers() and
21
#                                                 getOptionalIdentifiers()
22
#    Oct 07, 2016    ----          mjames@ucar    Added getForecastRun
23
#    Oct 18, 2016    5916          bsteffen       Add setLazyLoadGridLatLon
24
#    Oct 11, 2018                  mjames@ucar    Added getMetarObs() getSynopticObs()
25
#
26
#
27
28
29
import sys
30
import warnings
31
32
THRIFT_HOST = "edex"
33
34
USING_NATIVE_THRIFT = False
35
36
if 'jep' in sys.modules:
37
    # intentionally do not catch if this fails to import, we want it to
38
    # be obvious that something is configured wrong when running from within
39
    # Java instead of allowing false confidence and fallback behavior
40
    import JepRouter
0 ignored issues
show
introduced by
Unable to import 'JepRouter'
Loading history...
41
    router = JepRouter
42
else:
43
    from awips.dataaccess import ThriftClientRouter
44
    router = ThriftClientRouter.ThriftClientRouter(THRIFT_HOST)
45
    USING_NATIVE_THRIFT = True
46
47
48 View Code Duplication
def getMetarObs(response):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
49
    from datetime import datetime
50
    single_val_params = ["timeObs", "stationName", "longitude", "latitude",
51
                         "temperature", "dewpoint", "windDir",
52
                         "windSpeed", "seaLevelPress"]
53
    multi_val_params = ["presWeather", "skyCover", "skyLayerBase"]
54
    params = single_val_params + multi_val_params
0 ignored issues
show
Unused Code introduced by
The variable params seems to be unused.
Loading history...
55
    station_names, pres_weather, sky_cov, sky_layer_base = [],[],[],[]
0 ignored issues
show
Coding Style introduced by
Exactly one space required after comma
Loading history...
56
    obs = dict({params: [] for params in params})
57
    for ob in response:
0 ignored issues
show
unused-code introduced by
Too many nested blocks (7/5)
Loading history...
58
        avail_params = ob.getParameters()
59
        if "presWeather" in avail_params:
60
            pres_weather.append(ob.getString("presWeather"))
61
        elif "skyCover" in avail_params and "skyLayerBase" in avail_params:
62
            sky_cov.append(ob.getString("skyCover"))
63
            sky_layer_base.append(ob.getNumber("skyLayerBase"))
64
        else:
65
            # If we already have a record for this stationName, skip
66
            if ob.getString('stationName') not in station_names:
67
                station_names.append(ob.getString('stationName'))
68
                for param in single_val_params:
69
                    if param in avail_params:
70
                        if param == 'timeObs':
71
                            obs[param].append(datetime.fromtimestamp(ob.getNumber(param) / 1000.0))
72
                        else:
73
                            try:
74
                                obs[param].append(ob.getNumber(param))
75
                            except TypeError:
76
                                obs[param].append(ob.getString(param))
77
                    else:
78
                        obs[param].append(None)
79
80
                obs['presWeather'].append(pres_weather);
0 ignored issues
show
Coding Style introduced by
Unnecessary semicolon
Loading history...
81
                obs['skyCover'].append(sky_cov);
0 ignored issues
show
Coding Style introduced by
Unnecessary semicolon
Loading history...
82
                obs['skyLayerBase'].append(sky_layer_base);
0 ignored issues
show
Coding Style introduced by
Unnecessary semicolon
Loading history...
83
                pres_weather = []
84
                sky_cov = []
85
                sky_layer_base = []
86
    return obs
87
88
89 View Code Duplication
def getSynopticObs(response):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
90
    from datetime import datetime
91
    station_names = []
92
    params = response[0].getParameters()
93
    sfcobs = dict({params: [] for params in params})
94
    for sfcob in response:
95
        # If we already have a record for this stationId, skip
96
        if sfcob.getString('stationId') not in station_names:
97
            station_names.append(sfcob.getString('stationId'))
98
            for param in params:
99
                if param == 'timeObs':
100
                    sfcobs[param].append(datetime.fromtimestamp(sfcob.getNumber(param) / 1000.0))
101
                else:
102
                    try:
103
                        sfcobs[param].append(sfcob.getNumber(param))
104
                    except TypeError:
105
                        sfcobs[param].append(sfcob.getString(param))
106
107
    return sfcobs
108
109
110 View Code Duplication
def getForecastRun(cycle, times):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
111
    """
112
    :param cycle: Forecast cycle reference time
113
    :param times: All available times/cycles
114
    :return: DataTime array for a single forecast run
115
    """
116
    fcstRun = []
117
    for t in times:
118
        if str(t)[:19] == str(cycle):
119
            fcstRun.append(t)
120
    return fcstRun
121
122
def getAvailableTimes(request, refTimeOnly=False):
123
    """
124
    Get the times of available data to the request.
125
126
    Args:
127
            request: the IDataRequest to get data for
128
            refTimeOnly: optional, use True if only unique refTimes should be
129
                          returned (without a forecastHr)
130
131
    Returns:
132
            a list of DataTimes
133
    """
134
    return router.getAvailableTimes(request, refTimeOnly)
135
136
137
def getGridData(request, times=[]):
1 ignored issue
show
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...
138
    """
139
    Gets the grid data that matches the request at the specified times.  Each
140
    combination of parameter, level, and dataTime will be returned as a
141
    separate IGridData.
142
143
    Args:
144
            request: the IDataRequest to get data for
145
            times: a list of DataTimes, a TimeRange, or None if the data is time
146
                    agnostic
147
148
    Returns:
149
            a list of IGridData
150
    """
151
    return router.getGridData(request, times)
152
153
154
def getGeometryData(request, times=[]):
1 ignored issue
show
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...
155
    """
156
    Gets the geometry data that matches the request at the specified times.
157
    Each combination of geometry, level, and dataTime will be returned as a
158
    separate IGeometryData.
159
160
    Args:
161
            request: the IDataRequest to get data for
162
            times: a list of DataTimes, a TimeRange, or None if the data is time
163
                    agnostic
164
165
    Returns:
166
            a list of IGeometryData
167
    """
168
    return router.getGeometryData(request, times)
169
170
171
def getAvailableLocationNames(request):
172
    """
173
    Gets the available location names that match the request without actually
174
    requesting the data.
175
176
    Args:
177
            request: the request to find matching location names for
178
179
    Returns:
180
            a list of strings of available location names.
181
    """
182
    return router.getAvailableLocationNames(request)
183
184
185
def getAvailableParameters(request):
186
    """
187
    Gets the available parameters names that match the request without actually
188
    requesting the data.
189
190
    Args:
191
            request: the request to find matching parameter names for
192
193
    Returns:
194
            a list of strings of available parameter names.
195
    """
196
    return router.getAvailableParameters(request)
197
198
199
def getAvailableLevels(request):
200
    """
201
    Gets the available levels that match the request without actually
202
    requesting the data.
203
204
    Args:
205
            request: the request to find matching levels for
206
207
    Returns:
208
            a list of strings of available levels.
209
    """
210
    return router.getAvailableLevels(request)
211
212
213
def getRequiredIdentifiers(request):
214
    """
215
    Gets the required identifiers for this request.  These identifiers
216
    must be set on a request for the request of this datatype to succeed.
217
218
    Args:
219
            request: the request to find required identifiers for
220
221
    Returns:
222
            a list of strings of required identifiers
223
    """
224
    if str(request) == request:
225
        warnings.warn("Use getRequiredIdentifiers(IDataRequest) instead",
226
                      DeprecationWarning)
227
    return router.getRequiredIdentifiers(request)
228
229
230
def getOptionalIdentifiers(request):
231
    """
232
    Gets the optional identifiers for this request.
233
234
    Args:
235
            request: the request to find optional identifiers for
236
237
    Returns:
238
            a list of strings of optional identifiers
239
    """
240
    if str(request) == request:
241
        warnings.warn("Use getOptionalIdentifiers(IDataRequest) instead",
242
                      DeprecationWarning)
243
    return router.getOptionalIdentifiers(request)
244
245
246
def getIdentifierValues(request, identifierKey):
247
    """
248
    Gets the allowed values for a particular identifier on this datatype.
249
250
    Args:
251
            request: the request to find identifier values for
252
            identifierKey: the identifier to find values for
253
254
    Returns:
255
            a list of strings of allowed values for the specified identifier
256
    """
257
    return router.getIdentifierValues(request, identifierKey)
258
259
def newDataRequest(datatype=None, **kwargs):
260
    """"
261
    Creates a new instance of IDataRequest suitable for the runtime environment.
262
    All args are optional and exist solely for convenience.
263
264
    Args:
265
            datatype: the datatype to create a request for
266
            parameters: a list of parameters to set on the request
267
            levels: a list of levels to set on the request
268
            locationNames: a list of locationNames to set on the request
269
            envelope: an envelope to limit the request
270
            **kwargs: any leftover kwargs will be set as identifiers
271
272
    Returns:
273
            a new IDataRequest
274
    """
275
    return router.newDataRequest(datatype, **kwargs)
276
277
def getSupportedDatatypes():
278
    """
279
    Gets the datatypes that are supported by the framework
280
281
    Returns:
282
            a list of strings of supported datatypes
283
    """
284
    return router.getSupportedDatatypes()
285
286
287
def changeEDEXHost(newHostName):
288
    """
289
    Changes the EDEX host the Data Access Framework is communicating with. Only
290
    works if using the native Python client implementation, otherwise, this
291
    method will throw a TypeError.
292
293
    Args:
294
            newHostHame: the EDEX host to connect to
295
    """
296
    if USING_NATIVE_THRIFT:
297
        global THRIFT_HOST
1 ignored issue
show
Coding Style introduced by
Usage of the global statement should be avoided.

Usage of global can make code hard to read and test, its usage is generally not recommended unless you are dealing with legacy code.

Loading history...
298
        THRIFT_HOST = newHostName
299
        global router
1 ignored issue
show
Coding Style introduced by
Usage of the global statement should be avoided.

Usage of global can make code hard to read and test, its usage is generally not recommended unless you are dealing with legacy code.

Loading history...
300
        router = ThriftClientRouter.ThriftClientRouter(THRIFT_HOST)
0 ignored issues
show
introduced by
The variable ThriftClientRouter does not seem to be defined for all execution paths.
Loading history...
301
    else:
302
        raise TypeError("Cannot call changeEDEXHost when using JepRouter.")
303
304
def setLazyLoadGridLatLon(lazyLoadGridLatLon):
305
    """
306
    Provide a hint to the Data Access Framework indicating whether to load the
307
    lat/lon data for a grid immediately or wait until it is needed. This is
308
    provided as a performance tuning hint and should not affect the way the
309
    Data Access Framework is used. Depending on the internal implementation of
310
    the Data Access Framework this hint might be ignored. Examples of when this
311
    should be set to True are when the lat/lon information is not used or when
312
    it is used only if certain conditions within the data are met. It could be
313
    set to False if it is guaranteed that all lat/lon information is needed and
314
    it would be better to get any performance overhead for generating the
315
    lat/lon data out of the way during the initial request.
316
317
318
    Args:
319
            lazyLoadGridLatLon: Boolean value indicating whether to lazy load.
320
    """
321
    try:
322
        router.setLazyLoadGridLatLon(lazyLoadGridLatLon)
323
    except AttributeError:
324
        # The router is not required to support this capability.
325
        pass
326