Skip to content

Instantly share code, notes, and snippets.

@ritesh
Created November 28, 2023 10:12
Show Gist options
  • Save ritesh/8915d8cc9f868861b974936bc75539e7 to your computer and use it in GitHub Desktop.
Save ritesh/8915d8cc9f868861b974936bc75539e7 to your computer and use it in GitHub Desktop.
openweathermap
# 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