2022-09-16 11:52:14 +00:00
|
|
|
import json
|
2022-09-17 20:59:25 +00:00
|
|
|
import requests
|
2022-09-16 11:52:14 +00:00
|
|
|
import re
|
|
|
|
import datetime
|
2022-09-17 20:59:25 +00:00
|
|
|
import dateutil.parser as dp
|
2022-09-16 11:52:14 +00:00
|
|
|
|
|
|
|
|
2022-09-21 12:11:28 +00:00
|
|
|
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)
|
|
|
|
|
|
|
|
|
2022-09-16 11:52:14 +00:00
|
|
|
class WeatherApi:
|
|
|
|
|
2022-09-17 20:59:25 +00:00
|
|
|
def __init__(self, nick):
|
|
|
|
self.nick = nick
|
2022-09-16 11:52:14 +00:00
|
|
|
self.weather_api_url = "https://api.weatherapi.com/v1"
|
|
|
|
self.api_key_filename = "./weather_api_key.txt"
|
2022-09-17 20:59:25 +00:00
|
|
|
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'
|
|
|
|
}
|
|
|
|
}
|
2022-09-16 11:52:14 +00:00
|
|
|
|
|
|
|
def read_api_key(self):
|
|
|
|
with open(self.api_key_filename) as fp:
|
2022-09-17 20:59:25 +00:00
|
|
|
api_key = fp.read()
|
|
|
|
self.api_key = api_key.strip()
|
|
|
|
|
2022-09-16 11:52:14 +00:00
|
|
|
def get_user_preferences(self, nick):
|
2022-09-17 20:59:25 +00:00
|
|
|
try:
|
|
|
|
with open(self.user_preferences_filename) as fp:
|
|
|
|
user_preferences = json.load(fp)
|
|
|
|
except FileNotFoundError:
|
|
|
|
user_preferences = {
|
|
|
|
nick: {
|
|
|
|
'location': None,
|
|
|
|
'units': 'metric'
|
|
|
|
},
|
|
|
|
}
|
2022-09-21 12:11:28 +00:00
|
|
|
except KeyError:
|
|
|
|
user_preferences[nick] = {
|
|
|
|
'location': None,
|
|
|
|
'units': 'metric'
|
|
|
|
}
|
2022-09-17 20:59:25 +00:00
|
|
|
|
|
|
|
return user_preferences
|
|
|
|
|
|
|
|
def set_user_preferences(self, nick, location=None, units=None):
|
|
|
|
user_preferences = self.get_user_preferences(nick)
|
2022-09-16 11:52:14 +00:00
|
|
|
|
2022-09-17 20:59:25 +00:00
|
|
|
# Only acceptable units are 'imperial' or 'metric' (no kelvin for now)
|
|
|
|
if not units or (units and units not in ['metric', 'imperial']):
|
|
|
|
units = 'metric'
|
|
|
|
|
2022-09-21 12:11:28 +00:00
|
|
|
try:
|
|
|
|
user_preferences[nick]['units'] = units
|
2022-09-17 20:59:25 +00:00
|
|
|
|
2022-09-21 12:11:28 +00:00
|
|
|
if location:
|
|
|
|
user_preferences[nick]['location'] = location
|
|
|
|
|
|
|
|
except KeyError:
|
|
|
|
user_preferences[nick] = {
|
|
|
|
'location': location,
|
|
|
|
'units': units
|
|
|
|
}
|
2022-09-17 20:59:25 +00:00
|
|
|
|
|
|
|
with open(self.user_preferences_filename, 'w') as fp:
|
|
|
|
json.dump(user_preferences, fp)
|
|
|
|
|
2022-09-17 21:50:55 +00:00
|
|
|
def get_current_conditions(self, nick, location=None, units=None):
|
2022-09-17 20:59:25 +00:00
|
|
|
user_preferences = self.get_user_preferences(nick)
|
2022-09-21 12:11:28 +00:00
|
|
|
units = user_preferences[nick]['units'] if nick in user_preferences.keys() else 'metric'
|
2022-09-17 20:59:25 +00:00
|
|
|
|
|
|
|
if not location:
|
2023-05-12 12:28:08 +00:00
|
|
|
if user_preferences[nick]['location']:
|
|
|
|
location = user_preferences[nick]['location']
|
2023-05-12 12:29:13 +00:00
|
|
|
else:
|
|
|
|
raise NoDefaultLocation()
|
2022-09-16 11:52:14 +00:00
|
|
|
|
2022-09-17 21:50:55 +00:00
|
|
|
# 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'
|
2022-09-21 12:11:28 +00:00
|
|
|
units = user_preferences[nick]['units'] if nick in user_preferences.keys() else 'metric'
|
2022-09-17 21:50:55 +00:00
|
|
|
|
|
|
|
if not location:
|
|
|
|
location = user_preferences[nick]['location']
|
|
|
|
|
|
|
|
|
2022-09-21 12:11:28 +00:00
|
|
|
# URL for forecast
|
2022-09-16 11:52:14 +00:00
|
|
|
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
|
2022-09-17 20:59:25 +00:00
|
|
|
forecast_request = requests.get(forecast_url)
|
|
|
|
forecast_data = forecast_request.content
|
2022-09-16 11:52:14 +00:00
|
|
|
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'],
|
2022-09-17 20:59:25 +00:00
|
|
|
'day_of_week': datetime.datetime.strftime(dp.parse(day['date']), '%a'),
|
|
|
|
'high': -10000.0,
|
|
|
|
'low': 10000.0,
|
2022-09-16 11:52:14 +00:00
|
|
|
'conditions': ''
|
2022-09-17 20:59:25 +00:00
|
|
|
}
|
2022-09-16 11:52:14 +00:00
|
|
|
|
|
|
|
for hour in day['hour']:
|
2022-09-17 20:59:25 +00:00
|
|
|
temp = hour[f"temp_{self.unit_suffices[units]['temp']}"]
|
2022-09-16 11:52:14 +00:00
|
|
|
|
2022-09-17 20:59:25 +00:00
|
|
|
if temp >= daily_temp_range['high']:
|
2022-09-16 11:52:14 +00:00
|
|
|
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
|
|
|
|
|
2022-09-17 20:59:25 +00:00
|
|
|
forecast_temps.append(daily_temp_range)
|
2022-09-16 11:52:14 +00:00
|
|
|
|
|
|
|
# Different formats for different units
|
2022-09-17 20:59:25 +00:00
|
|
|
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}"
|
2022-09-16 11:52:14 +00:00
|
|
|
|
2022-09-17 20:59:25 +00:00
|
|
|
return forecast_output_format
|
2022-09-16 11:52:14 +00:00
|
|
|
|
|
|
|
|