Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Snotel #15

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions climata/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@


parse_date = make_date_mapper('%Y-%m-%d')
parse_datetime = make_date_mapper('%Y-%m-%d %H:%M:%S')


class FilterOpt(object):
Expand Down Expand Up @@ -48,6 +49,24 @@ def parse(self, value):
return [value]
return value

class DateTimeOpt(FilterOpt):
date_only = True

def parse_datetime(self, value):
return parse_datetime(value)

def parse(self, value):
"""
Parse date
"""
value = super(DateTimeOpt, self).parse(value)
if value is None:
return None
if isinstance(value, str):
value = self.parse_datetime(value)
if isinstance(value, datetime) and self.date_only:
value = value.date()
return value

class DateOpt(FilterOpt):
date_only = True
Expand All @@ -64,8 +83,8 @@ def parse(self, value):
return None
if isinstance(value, str):
value = self.parse_date(value)
if isinstance(value, datetime) and self.date_only:
value = value.date()
if isinstance(value, datetime):
value = value.strftime("%Y-%m-%d %H:%M:%S")
return value


Expand Down
69 changes: 56 additions & 13 deletions climata/snotel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from suds.client import Client
from suds.sudsobject import asdict, Object as SudsObject
from climata.base import WebserviceLoader, FilterOpt, DateOpt
from climata.base import WebserviceLoader, FilterOpt, DateOpt, DateTimeOpt
from climata.base import fill_date_range, as_list

url = 'https://wcc.sc.egov.usda.gov/awdbWebService/services?WSDL'
Expand Down Expand Up @@ -53,6 +53,25 @@ def load(self):
else:
parse = str
self.data = [parse(row) for row in self.data]

def serialize_params(self, params, complex_type):
"""
The AWDB NRCS webservice allows for multiple parameters, need
to have the ability to query multiple stations at a time for
effeciency. If one of the parameters is a list, the client will
be able to make multiple types for the list.

Therefore, overwrite the `serialize_params` to not join vals if
it's a list
"""

if complex_type:
raise NotImplementedError("Cannot serialize %s!" % params)
else:
return {
self.get_url_param(key): val
for key, val in params.items()
}

# Some records may have additional fields; loop through entire
# array to ensure all field names are accounted for. (Otherwise BaseIO
Expand Down Expand Up @@ -83,10 +102,13 @@ class StationIO(SnotelIO):
county = FilterOpt(url_param='countyNames', multi=True)
basin = FilterOpt(url_param='hucs', multi=True)
parameter = FilterOpt(url_param='elementCds', multi=True)
network = FilterOpt(url_param='networkCds', multi=True)

# Additional options
min_latitude = FilterOpt(url_param='minLatitude')
max_latitude = FilterOpt(url_param='maxLatitude')
min_longitude = FilterOpt(url_param='minLongitude')
max_longitude = FilterOpt(url_param='maxLongitude')
min_elevation = FilterOpt(url_param='minElevation')
max_elevation = FilterOpt(url_param='maxElevation')
ordinals = FilterOpt(url_param='ordinals')
Expand All @@ -105,11 +127,19 @@ class StationIO(SnotelIO):

def load(self):
super(StationIO, self).load()
self.data = [
StationMetaIO(station=station, debug=self.debug).data[0]
for station in self.data
]
if len(self.data) > 0:
self.data = StationMetaMultipleIO(stations=self.data, debug=self.debug).data


class StationMetaMultipleIO(SnotelIO):
"""
Wrapper for getStationMetadataMultiple() - used internally by StationIO.
"""

data_function = 'getStationMetadataMultiple'

stations = FilterOpt(required=True, url_param='stationTriplets', multi=True)


class StationMetaIO(SnotelIO):
"""
Expand Down Expand Up @@ -351,7 +381,7 @@ class HourlyDataIO(TimeSeriesMapper, SnotelIO):
]

# Applicable WebserviceLoader default options
station = FilterOpt(required=True, url_param='stationTriplets')
station = FilterOpt(required=True, url_param='stationTriplets', multi=True)
parameter = FilterOpt(required=True, url_param='elementCd')
start_date = DateOpt(required=True, url_param='beginDate')
end_date = DateOpt(required=True, url_param='endDate')
Expand All @@ -368,14 +398,27 @@ class HourlyDataIO(TimeSeriesMapper, SnotelIO):

def load(self):
super(HourlyDataIO, self).load()
if self.data and 'values' in self.data[0]:
self.data = [
asdict(row)
for row in as_list(self.data[0]['values'])
]
else:
raise NoData
d = {} # since mutiple triplets can be used, then go through each station
for data in self.data:
stationTriplet = data['stationTriplet']
if data and 'values' in data:
data = [
asdict(row)
for row in as_list(data['values'])
]
else:
data = []
d[stationTriplet] = data
self.data = d

class StationHourlyDataElementIO(HourlyDataIO):
"""
Requests hourly data for the specified stations, just request the
data without asking for the elements.

"""
inner_io_class = HourlyDataIO
duration = "HOURLY"

class StationHourlyDataIO(StationDataIO):
"""
Expand Down