import json import requests import re import datetime import dateutil.parser as dp class NoDefaultLocation(Exception): def __init__(self, message=None): default_message = "No default location set" if not message: message = default_message super().__init__(message) class NoLocationSpecified(Exception): def __init__(self, message=None): default_message = "No location specified" if not message: message = default_message super().__init__(message) class WeatherApi: def __init__(self, nick): self.nick = nick self.weather_api_url = "https://api.weatherapi.com/v1" self.api_key_filename = "./weather_api_key.txt" self.user_preferences_filename = "./weather_user_preferences.json" self.read_api_key() self.unit_suffices = { 'metric': { 'temp': 'c', 'rate': 'kph', 'volume': 'mm', 'baro': 'mb', 'distance': 'km' }, 'imperial': { 'temp': 'f', 'rate': 'mph', 'volume': 'in', 'baro': 'in', 'distance': 'miles' } } def read_api_key(self): with open(self.api_key_filename) as fp: api_key = fp.read() self.api_key = api_key.strip() def get_user_preferences(self, nick): try: with open(self.user_preferences_filename) as fp: user_preferences = json.load(fp) except FileNotFoundError: user_preferences = { nick: { 'location': None, 'units': 'metric' }, } except KeyError: user_preferences[nick] = { 'location': None, 'units': 'metric' } return user_preferences def set_user_preferences(self, nick, location=None, units=None): user_preferences = self.get_user_preferences(nick) # Only acceptable units are 'imperial' or 'metric' (no kelvin for now) if not units or (units and units not in ['metric', 'imperial']): units = 'metric' try: user_preferences[nick]['units'] = units if location: user_preferences[nick]['location'] = location except KeyError: user_preferences[nick] = { 'location': location, 'units': units } with open(self.user_preferences_filename, 'w') as fp: json.dump(user_preferences, fp) def get_current_conditions(self, nick, location=None, units=None): user_preferences = self.get_user_preferences(nick) units = user_preferences[nick]['units'] if nick in user_preferences.keys() else 'metric' if not location: location = user_preferences[nick]['location'] if user_preferences[nick]['location'] else raise NoDefaultLocation() # URL for current conditions current_url = f"{self.weather_api_url}/current.json?key={self.api_key}&q={location}&qai=no" # Make the request and get the results current_request = requests.get(current_url) current_data = current_request.content current_json = json.loads(current_data.decode('utf-8')) # Different formats for different units current = current_json['current'] location = current_json['location'] current_output_location = f"{location['name']}, {location['region']}, {location['country']}" temp = current[f"temp_{self.unit_suffices[units]['temp']}"] wind_speed = current[f"wind_{self.unit_suffices[units]['rate']}"] current_output_format = f"{current_output_location} | {temp}°{self.unit_suffices[units]['temp'].upper()} | {current['humidity']}% | {current['condition']['text']} | {current['wind_dir']} {current['wind_degree']}° {wind_speed} {self.unit_suffices[units]['rate']}" return current_output_format def get_forecast(self, nick, location=None, days=5, units=None): user_preferences = self.get_user_preferences(nick) if not units else 'metric' units = user_preferences[nick]['units'] if nick in user_preferences.keys() else 'metric' if not location: location = user_preferences[nick]['location'] # URL for forecast forecast_url = f"{self.weather_api_url}/forecast.json?key={self.api_key}&q={location}&days={days}&qai=no" # Make the request and get the results forecast_request = requests.get(forecast_url) forecast_data = forecast_request.content forecast_json = json.loads(forecast_data.decode('utf-8')) location = forecast_json['location'] forecast = forecast_json['forecast']['forecastday'] # Find the highest and lowest temperature over each 24 hour period forecast_temps = [] for day in forecast_json['forecast']['forecastday']: # Set the initial high and low values to absurd values daily_temp_range = { 'day': day['date'], 'day_of_week': datetime.datetime.strftime(dp.parse(day['date']), '%a'), 'high': -10000.0, 'low': 10000.0, 'conditions': '' } for hour in day['hour']: temp = hour[f"temp_{self.unit_suffices[units]['temp']}"] if temp >= daily_temp_range['high']: daily_temp_range['high'] = temp # We only want the conditions for when the temp is the highest daily_temp_range['conditions'] = hour['condition']['text'] if temp < daily_temp_range['low']: daily_temp_range['low'] = temp forecast_temps.append(daily_temp_range) # Different formats for different units forecast_output_location = f"{location['name']}, {location['region']}, {location['country']}" forecast_days = "" for forecast_temp in forecast_temps: forecast_days += f" | {forecast_temp['day_of_week']} - {forecast_temp['high']}°{self.unit_suffices[units]['temp'].upper()}/{forecast_temp['low']}°{self.unit_suffices[units]['temp'].upper()} - {forecast_temp['conditions']}" forecast_output_format = f"{forecast_output_location}{forecast_days}" return forecast_output_format