-
Notifications
You must be signed in to change notification settings - Fork 75
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
Adds acis.py for ACIS Web Services functionality #177
Adds acis.py for ACIS Web Services functionality #177
Conversation
Updates docstring text to better reflect parameters
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I have a few comments, but really they just relate to better integration into Siphon. Also, can you move this into the simplewebservice
directory? That's where we're starting to accumulate things like this.
Overall, the code does seem fine and clearly would simplify access to the service. Would be good to eventually add an example to show how useful this is.
siphon/acis.py
Outdated
timeout=60 | ||
|
||
try: | ||
response = urllib.request.urlopen(req, timeout=timeout) # 10-Minute Timeout |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that comment is correct any more 😁
Would also need to be updated to use requests
.
siphon/acis.py
Outdated
return json.loads(response.read()) | ||
except urllib.request.HTTPError as error: | ||
if error.code == 400: | ||
print(error.msg) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of catching the error, printing the message, and returning False
, I think it's better to just let the error bubble to the top. That turns the code for users from (currently):
response = acisRequest(...)
if response == False:
# Handle error
else:
# Do stuff with data
into
try:
response = acisRequest(...)
# Do stuff with data
except HTTPError:
# Handle errors
It's shorter code in the library, just as easy for users, and doesn't involve having changing return types. If you're worried about having multiple exception types, you could create a custom Exception
subclass and raise that from inside the function.
siphon/acis.py
Outdated
import json | ||
import socket | ||
|
||
def acisRequest(method, params): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about acis_request
? That matches our existing naming structure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That certainly can be done.
siphon/acis.py
Outdated
|
||
Returns | ||
---------- | ||
Connection Success: A dictionary that is derived from JSON data from the remote API |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would need to be updated based on discussion below. Also Sphinx will pitch a fit unless the length of "---------" matches the length of "Returns".
siphon/acis.py
Outdated
|
||
Parameters | ||
---------- | ||
method - The Web Services request method (StnMeta, StnData, MultiStnData, etc) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Parameters docs look like:
method : str
The Web Services request method (StnMeta, StnData, MultiStnData, etc.)
siphon/acis.py
Outdated
|
||
base_url = 'http://data.rcc-acis.org/' # ACIS Web API URL | ||
|
||
req = urllib.request.Request(base_url+method, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think you could adapt this to use the requests
module? We're already using it internally, it would solve your Python 2 problem, and it sets our own "User-Agent" header. I'm happy to look into how to do this if you can clarify how the data
argument gets used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The data argument is simply a JSON object containing parameters (Station ID, dates, variables, etc) for the HTTP request. I'm more than happy to adapt it, there was no reason for using urllib other than that's what was used in the old Python 2 version of this code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, then I'll leave you to it, but feel free to scream if you run into problems.
I am working on some use case examples. Will those go into |
Yeah, I think that makes sense, though if you have several, you could do |
- Updates acis_request to use the requests library.
- Adds examples and resources for Siphon documentation
Latest push adds two examples to the documentation and finalizes some of the acis_request function code. Exception handling is certainly open to feedback. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like I failed to submit this yesterday. Oops.
Exception handling actually looks fine, at least to my eyes.
Some general style comments, especially for examples, to make the bots happy:
- a space after every comma
- Only use single quotes
- a space after the colon in a dictionary
Otherwise, I think these examples look great!
siphon/simplewebservice/acis.py
Outdated
base_url = 'http://data.rcc-acis.org/' # ACIS Web API URL | ||
|
||
if method == 'MultiStnData': | ||
timeout=300 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could use some more spaces around the =
here. Or if you like:
timeout = 300 if method == 'MultiStnData' else 60
but that's just a matter of personal taste.
siphon/simplewebservice/acis.py
Outdated
timeout=60 | ||
|
||
try: | ||
response = requests.post(base_url+method, data=params, timeout=timeout) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you put some spaces around the +
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, if you change requests.post()
to create_http_session().post()
(after doing from ..http_util import create_http_session
), you'll get e.g. "Siphon(0.6.0)" in the logs for ACIS. Not sure if you'd be interested in seeing how much use the service gets from us--or if you even have access to those logs yourself. This one really doesn't matter to me since we'll never see the logs.
siphon/simplewebservice/acis.py
Outdated
Parameters | ||
---------- | ||
method : str | ||
The Web Services request method (MultiStn,StnMeta,etc) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the exhaustive list of methods up above? If so, probably good to just list all 5 here rather than abbreviating the list.
siphon/simplewebservice/acis.py
Outdated
|
||
class ACIS_API_Exception(Exception): | ||
""" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you kill the blank line?
siphon/simplewebservice/acis.py
Outdated
timeout=60 | ||
|
||
try: | ||
response = requests.post(base_url+method, json=params, timeout=timeout) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add spaces around the +
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, if you change requests.post()
to create_http_session().post()
(after doing from ..http_util import create_http_session
), you'll get e.g. "Siphon(0.6.0)" in the logs for ACIS. Not sure if you'd be interested in seeing how much use the service gets from us--or if you even have access to those logs yourself. This one really doesn't matter to me since we'll never see the logs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neat! I'll have to double check with the developer that manages the servers to make sure, but I know it'd be interesting to see. (We're more than happy to share those statistics if we get them)
examples/acis/Mapping_Example.py
Outdated
# all 4 days of data, we would simply remove that parameter. | ||
# | ||
# To wrap up this example, we will finally plot our precipitation sums and | ||
# departures onto a map using CartoPy and MetPy. To do this we will utilize |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't look like you're actually using MetPy here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You caught me! I was, but then I noticed the implementation I had was poor. Looks like I forgot to proofread that part of the tutorial afterwards.
siphon/simplewebservice/acis.py
Outdated
except requests.exceptions.TooManyRedirects: | ||
raise ACIS_API_Exception("Bad URL. Check your ACIS connection method string.") | ||
except ValueError: | ||
raise ACIS_API_Exception("No data returned! The ACIS parameter dictionary\ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Single quotes, please. Also, instead of the line continuation (\
), you can just end the line with a '
, and then open a new string with a '
on the next line; since it's within the ()
, Python will concatenate them for you. So:
raise ACIS_API_Exception('No data returned! The ACIS parameter dictionary'
'may be incorrectly formatted')
We'll also need to add a test. Probably easiest to start with one from If setting that up is a problem, I'm happy to jump in and help out. |
- Changes use of requests directly in acis_request() to use siphon create_http_session() function.
Alright, I have a unit test file that runs fine when run I run the individual function. How can I run the test to generate the recorded data you mentioned? Thanks! |
This version adds the unit test, fixes the formatting errors mentioned, and adds the unit test data to the fixtures folder. I completely missed that the data was already present in my fork (I imagined a gitignore for some reason). I'll monitor the bots to see what they find, thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a minor change on the test, otherwise that looks just like what I was looking for. Modulo some minor style things, this is almost there.
siphon/tests/test_acis.py
Outdated
recorder = get_recorder(__file__) | ||
|
||
@recorder.use_cassette('acis_request') | ||
def test_acis(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you break this up into 4 individual tests, one for each request method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I certainly can
siphon/tests/test_acis.py
Outdated
"""Testing ACIS Gridded Data request.""" | ||
data = acis_request('GridData', {'loc': '-95.36, 29.76', 'sdate': '2000-01', | ||
'edate': '2000-07', 'grid': '3', 'elems': [ | ||
{'name': 'maxt', 'interval': 'mly', 'reduce': 'max', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm unsure of how to take care of this PEP8 error. I thought line 44 might have been too long, but instead I created the E128 error.
siphon/tests/test_acis.py:44:25: E122 continuation line missing indentation or outdented siphon/tests/test_acis.py:45:25: E128 continuation line under-indented for visual indent
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Try this:
data = acis_request('GridData',
{'loc': '-95.36, 29.76', 'sdate': '2000-01', 'edate': '2000-07',
'grid': '3', 'elems': [{'name': 'maxt', 'interval': 'mly',
'reduce': 'max', 'smry': 'max'}]})
Arguments to the function, keys in the dictionary, items in the list--they line up when wrapped.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That seems to have worked on my end, we'll see if that silences the Travis-CI. (Right after I make the acis_request function in acis.py have an imperative mood)
recorder = get_recorder(__file__) | ||
|
||
|
||
@recorder.use_cassette('acis_request') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used one "cassette" for all 5 tests.. Is that alright? They're all JSON strings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's fine for now. If we ever need to regenerate, we'll have to separate them, since the cassette is only ever generated from a single test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Found a few things, but nothing I’d hold this up for. Feel free to ignore. Let me know and I’ll merge.
examples/acis/Mapping_Example.py
Outdated
|
||
for stn in myData['data']: | ||
# Skip threaded stations! They have no lat/lons | ||
if stn['meta']['sids'][-1][-1] == '9': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe
if stn[‘meta’][‘sids’][-1].endswith(‘9’)
examples/acis/Basic_Overview.py
Outdated
avgt = [] | ||
dates = [] | ||
for obs in myData['data']: | ||
if obs[0][-2:] == '01': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about:
If obs[0].endswith(‘01’)
?
examples/acis/Mapping_Example.py
Outdated
if stn['meta']['sids'][-1][-1] == '9': | ||
continue | ||
# Skip stations with missing data | ||
if stn['smry'][0] == 'M' or stn['smry'][1] == 'M': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What comes before “M” if it’s at index 1? White space? If so, what about:
If stn[‘smry’].lstrip().startswith(‘M’)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case 'smry' is actually a list of summary values. So in this case the 'smry' would have one value for the total precip and another for the departure from normal.
That push should take care of most of the changes you found.. I 'dismissed' your review I guess? |
I have it set to clear the review when new code is uploaded. This is good. Thanks! 🎉 Congrats on your PR! |
Adds acis.py, which provides a function for connecting to ACIS Web Services. This function is Python 3 compatible, but will need to use urllib2 to become Python2.7 compatible.