Source code for AutomateTheBoringStuff.Ch14.P5_quickWeather

#! python3
"""Quick weather

Prints 3-day weather information for a location specified in the command line.

"""

import json, requests, sys, shelve, datetime


[docs]def getWeather(loc: str, apikey: str) -> dict: """Get weather Uses `OpenWeatherMap.org`_ API to get JSON data of given location with given API key. Data is stored as a :class:`dict` and the current date and time (in UTC) is also stored using :meth:`datetime.datetime.now`. Args: loc: Location to get weather data of in ``City,Country Code`` format. apikey: API key used to interface with `OpenWeatherMap.org`_'s API. Returns: Dictionary with weather JSON data and current date time (in UTC) added. .. _OpenWeatherMap.org: https://openweathermap.org/api """ # Download the JSON data from OpenWeatherMap.org's API. url = 'http://api.openweathermap.org/data/2.5/forecast?q=%s&APPID=%s' % (loc, apikey) response = requests.get(url) response.raise_for_status() # Load JSON data into a Python variable. data = json.loads(response.text) now = datetime.datetime.now(tz=datetime.timezone.utc) data["savedTime"] = now return data
[docs]def main() -> None: """P5_quickWeather.py Displays given location's 3-day weather information. Returns: None. Weather data is printed to terminal and JSON data is stored in a :py:mod:`shelve` shelf, ``weather``. Note: To prevent excessive API requests, JSON data is stored in a :py:mod:`shelve` shelf and only redownloaded every 10 minutes. Time is kept track using :meth:`datetime.datetime.now` and :class:`datetime.timedelta`. """ # Compute location from command line arguments. if len(sys.argv) < 2: print('Usage: P5_quickWeather.py city,country code') sys.exit() location = ' '.join(sys.argv[1:]) # Get API Key from file with open("apikey.txt") as file: apiKey = file.read() # Open shelf to read data weatherShelf = shelve.open("weather") # Download and save data to shelf if not list(weatherShelf.keys()): # Shelf empty, download data weatherShelf["data"] = getWeather(location, apiKey) else: # Check for 10 minute interval between API requests timeNow = datetime.datetime.now(tz=datetime.timezone.utc) savedTime = weatherShelf["data"]["savedTime"] timedelta = timeNow - savedTime interval = datetime.timedelta(minutes=10) if timedelta < interval: city = weatherShelf["data"]["city"] print("RequestError: Need to wait %s minutes. Using saved data for: %s, %s" % (round((interval - timedelta).total_seconds()/60, 2), city["name"], city["country"])) else: weatherShelf["data"] = getWeather(location, apiKey) # Print weather descriptions w = weatherShelf["data"]['list'] count = int(weatherShelf["data"]["cnt"]) # Print current weather print('Current weather in %s:' % location) print(w[0]['weather'][0]['main'], '-', w[0]['weather'][0]['description']) currentDate = datetime.datetime.strptime(w[0]["dt_txt"][:10], '%Y-%m-%d') tomorrowDate = currentDate + datetime.timedelta(days=1) dayAfterDate = currentDate + datetime.timedelta(days=2) print() for i in range(1, count): currentDate = datetime.datetime.strptime(w[i]["dt_txt"][:10], '%Y-%m-%d') # If current date is greater than tomorrow date, print tomorrow weather if currentDate > tomorrowDate: print('Tomorrow:') print(w[i]['weather'][0]['main'], '-', w[i]['weather'][0]['description']) tomorrowDate = currentDate + datetime.timedelta(days=7) # past the 5-day forecast print() # If current date is greater than day after date, print day after tomorrow weather elif currentDate > dayAfterDate: print('Day after tomorrow:') print(w[i]['weather'][0]['main'], '-', w[i]['weather'][0]['description']) break weatherShelf.close()
if __name__ == '__main__': main()