olgamirth / maxtemp

Max Temperature for the next Two Weeks
MIT License
2 stars 0 forks source link

Beginnings Are Tough (But Not Impossible) #1

Open olgamirth opened 1 year ago

olgamirth commented 1 year ago

How is it that something stupid like variable assignments is causing me problems? I'm still pounding away at it.

I'm going to send you a copy of the file .tomorrow-io.api-key via email

Mike

olgamirth commented 1 year ago

Ah! f-strings!

JnyJny commented 1 year ago

I see what you were talking about; specifically building the url to feed to requests.

This is a great start; you've established that you can query the API and receive useful data in return. Now we want to start using the returned data to answer our question "What is the forecasted maximum temperature for the next fourteen days". I might not have the duration but that's the gist of the question.

Recall our talk about JSON and how easy it is to use?

https://github.com/olgamirth/maxtemp/blob/04446d1d1d434fad22e695e5955b89a3da3859e6/maxtemp.py#L21

Right now, the last line of your program is dumping response.text, the str representation of the data. You've told the API that you are willing to accept JSON and inspection of response.text shows that it is indeed a JSON payload.

You have a couple of choices on how to deal with the response JSON payload. You could interpret the payload yourself, which is pretty easy. You might want to do this if you want to do some custom transformation of the JSON data, but it's rare that I've had occasion to do that.

import json

data = json.loads(response.text)

The requests.Response class has you covered if that's too much to type:

data = response.json()

Now it's just a matter of having some a priori knowledge of the JSON payload structure. Our payload is kind of involved. It's , big breath, a dictionary of dictionaries of lists of dictionaries of dictionaries. That sounds complicated, and to be honest it sort of is, but part of the job of programming is managing and reducing complexity. So to make things more simple, we have to understand some part of the complexity to reduce it!

To get the maximum forecasted daily temperature from the data dictionary, you would need something like:


maxTemp0 = data['timelines']['daily'][0]['values']['temperatureMax']

How did I figure out the keys? I imported your code in the python repl and started poking at the returned data. The dictionary associated with timelines has three different keys; daily, hourly and minutely:

from maxtemp import *
...
>>> data.keys()
dict_keys(['timelines', 'location'])
>>> type(data['timelines'])
<class 'dict'>
>>> data['timelines'].keys()
dict_keys(['minutely', 'hourly', 'daily'])

To get a list of all the maximum forecasted temperatures, you would loop thru the 'daily' list of dictionaries.

olgamirth commented 1 year ago

Erik: Can I ask a stupid question? When you tried to run my code, did it actually work? Also, what version of python were you using? (I ask, because while my URL looks good, I'm still getting an invalid auth result ... I think I might know the reason, but I'm just wondering if it actually runs)

JnyJny commented 1 year ago

Excellent question, not a stupid question. I had to make one change to your code to get it to work locally for me.

You get the API key on this line: https://github.com/olgamirth/maxtemp/blob/04446d1d1d434fad22e695e5955b89a3da3859e6/maxtemp.py#L8

You are using an absolute path as an argument to open which happens to not exist on my machine. I registered for my own API key and put it in the same directory as maxtemp.py and called it ".tomorrow-io.api-key". Then I modified your code to:

api_key = open('.tomorrow-io.api-key').read()

After that the code ran for me. My guess is either the path to the API key is wrong or you might have inadvertently introduced some extra characters into your API key when you saved it to a file (like a newline or something, not sure).

Just went back and tried again with your API key and get the authorization error, so I think your key is broken somehow. You can go back to tomorrow.io and refresh your API key to get a new one. That may solve the problem.

olgamirth commented 1 year ago

fixed. api_key = api_key.rstrip()

Did a google search for "Python equivalent of Perl chomp". Yikes!

Also, I kinda cheated. I knew what I was getting back because I did a curl with my api-key and piped the result into jq. I SOOOO much more like your approach!

JnyJny commented 1 year ago

I see you've added poetry to the repo, that's great!

Use the tools you know to get things done and always be on the lookout for different approaches. You may not use them right away, but they may prove useful down the road.

JnyJny commented 1 year ago

The next thing we should work on is structuring your package. Right now your repo has a structure like this:

.
├── LICENSE
├── README.md
├── maxtemp
│   ├── __init__.py
│   └── maxtemp.py*
├── poetry.lock
├── pyproject.toml
└── tests
    └── __init__.py

I suspect you are running your program like this:

$ ./maxtemp/maxtemp.py
...

What we want to evolve to is something like this:

$ python3 -m maxtemp
...
$ maxtemp
...

The first example is using the "-m" option:

-m mod : run library module as a script (terminates option list)

To make this work, you need to provide a little bit of scaffolding in your package, specifically a file named ./maxtemp/__main__.py:

# maxtemp/__main__.py

def main() -> int:
     print("hello world from maxtemp")
     return 0

if __name__ == '__main__':
    exit(main())

To make the second usage possible $ maxtemp we have to add two lines to the pyproject.toml file:

[tool.poetry]
name = "maxtemp"
version = "0.1.0"
description = ""
authors = ["Michael Dahlberg <dahlberg@recursoft.org>"]
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.11"
requests = "^2.31.0"

# these are the two new lines that define a console script entry point that poetry will create for you.
[tool.poetry.scripts]
maxtemp = "maxtemp.__main__:main"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

When all that is updated, you'll need to refresh the version of your package installed into the poetry managed virtual environment using:

$ poetry shell
<venv> $ poetry install
...
<venv> $ rehash | hash # might be needed depending on your shell
<venv> $ maxtemp
hello world from maxtemp

Once we're here, we can talk about how to restructure maxtemp/maxtemp.py so that it can be used from __main__.py.