Created
November 28, 2023 10:12
-
-
Save ritesh/8915d8cc9f868861b974936bc75539e7 to your computer and use it in GitHub Desktop.
openweathermap
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This code has been tested with Python 3.11.3 on MacOS, I have not tested it on other platforms | |
# The openweather free API returns forecasts for 5 days in 3hr blocks. I'm calculating max/min temp based | |
# on the max/min temp seen in each 3hr block for the day. | |
# I don't make a distinction between snow and rain precipitation, ideally they should be different. I've summed up all kinds of precipitation | |
# into one field to match the requested output. | |
# TODO: | |
# Only US zipcodes are supported and validated, the API supports other countries but I have not had time to test | |
# Needs more tests | |
import os | |
import sys | |
import urllib.request | |
import json | |
import re | |
# rudimentary check for valid US zip code | |
def valid_input(zipcode): | |
return re.match('^[0-9]{5}(?:-[0-9]{4})?$', zipcode) | |
# Api returns kelvin, convert to F | |
def k_to_f(k): | |
f = (k - 273.15) * 1.8 + 32 | |
return f | |
# Convert to US date which is | |
# Month-Day-Year, unlike the rest of the world :( | |
def us_date(date): | |
out = date.split('-') | |
return '-'.join([out[1], out[2], out[0]]) | |
# We're iterating over the output and creating the display dict which will what we want to show | |
def display(out): | |
display = {} | |
for t in out['list']: | |
# Gets us year-month-day | |
d = t['dt_txt'].split(' ')[0] | |
# Check if we have readings for this day | |
j = display.get(d) | |
if j is None: | |
# Add an entry for the day, with the max/min temp set and precipitation set to zero | |
j = display[d] = [t['main']['temp_max'], t['main']['temp_min'], 0] | |
if t['main']['temp_max'] > j[0]: | |
display[d][0] = t['main']['temp_max'] | |
if t['main']['temp_min'] < j[1]: | |
display[d][1] = t['main']['temp_min'] | |
# chances of precipitation, could be rain or snow! | |
precip = 0 | |
if t['pop'] > 0: | |
rain = t.get('rain') | |
snow = t.get('snow') | |
if rain is not None: | |
precip = precip + rain.get('3h') | |
if snow is not None: | |
precip = precip + snow.get('3h') | |
display[d][2] = display[d][2] + precip | |
return display | |
def fetch_weather(): | |
api_key = os.getenv('API_KEY') | |
if api_key is None: | |
print('Please set an OpenWeatherMap API key env variable API_KEY') | |
sys.exit(1) | |
inp = input('Enter a zip code. Example - 90210: ') | |
inp = inp.rstrip() | |
if valid_input(inp) is None: | |
print('Please provide a valid US zipcode (more formats coming soon)') | |
sys.exit(1) | |
api_endpoint = f'https://api.openweathermap.org/data/2.5/forecast?zip={inp}&appid={api_key}' | |
try: | |
f = urllib.request.urlopen(api_endpoint) | |
out = json.loads(f.read().decode('utf-8')) | |
except urllib.error.HTTPError as e: | |
print(f'failed with {e.code}, {e.reason}') | |
sys.exit(1) | |
return out | |
if __name__ == '__main__': | |
out = fetch_weather() | |
p = display(out) | |
print('# Date\t\tTemperature\tPrecipitation') | |
for k in p: | |
# Format as US date which is month-day-year | |
d = us_date(k) | |
# Get the list that contains the temp and precipitation | |
v = p[k] | |
# Convert to Fahrenheit from Kelvin, only need .2f precision | |
min = "{:.2f}".format(k_to_f(v[0])) | |
max = "{:.2f}".format(k_to_f(v[1])) | |
precip = "{:.2f}".format(v[2]) | |
print(f'# {d}\t{min}/{max}\t\t{precip}') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment