jarvis / utils /weather_forecast.py
megamined's picture
Initial commit
b36a86c
from datetime import datetime
from typing import Optional
import httpx
from pydantic import BaseModel, Field
from simpleaichat import AIChat
from utils.env import get_from_env
WEATHERAPI_BASE_URL = "http://api.weatherapi.com/v1"
class WeatherAPI(BaseModel):
"""Wrapper for WeatherAPI
Docs for using:
1. Go to https://www.weatherapi.com/ and sign up for an API key
2. Save your API WEATHERAPI_API_KEY env variable
"""
weatherapi_key: Optional[str] = get_from_env("WEATHERAPI_API_KEY")
forecast_response: dict = None
warning: str = ""
def _format_weather_info(self):
current = self.forecast_response["current"]
forecast = self.forecast_response["forecast"]["forecastday"][0]
location_name = self.forecast_response["location"]["name"]
current_status = current["condition"]["text"]
wind_speed = current["wind_kph"]
wind_degree = current["wind_degree"]
humidity = current["humidity"]
cloud = current["cloud"]
feels_like = current["feelslike_c"]
current_temp = current["temp_c"]
min_temp = forecast["day"]["mintemp_c"]
max_temp = forecast["day"]["maxtemp_c"]
total_precip_mm = forecast["day"]["totalprecip_mm"]
total_snow_cm = forecast["day"]["totalsnow_cm"]
chance_of_rain = forecast["day"]["daily_chance_of_rain"]
chance_of_snow = forecast["day"]["daily_chance_of_snow"]
status = forecast["day"]["condition"]["text"]
is_day = current["is_day"]
sunrise = forecast["astro"]["sunrise"]
sunset = forecast["astro"]["sunset"]
# Convert the sunrise and sunset times to datetime objects
sunrise_time = datetime.strptime(sunrise, "%H:%M %p").time()
# sunset_time = datetime.strptime(sunset, "%I:%M %p").time()
# Get the current time
now = datetime.now().time()
# Check if the current time is before or after sunrise
if now < sunrise_time:
next_event = f"The sun will rise at {sunrise}."
else:
next_event = f"The sun will set at {sunset}."
rain_times = []
snow_times = []
for i, c in enumerate(forecast["hour"]):
if c["will_it_rain"]:
rain_times.append(c["time"])
if c["will_it_snow"]:
snow_times.append(c["time"])
results = (
f"In {location_name}, the current weather is as follows:\n"
f"Detailed status: {current_status}\n"
f"Wind speed: {wind_speed} kph, direction: {wind_degree}°\n"
f"Humidity: {humidity}%\n"
f"Temperature: \n"
f" - Current: {current_temp}°C\n"
f" - High: {max_temp}°C\n"
f" - Low: {min_temp}°C\n"
f" - Feels like: {feels_like}°C\n"
f"Cloud cover: {cloud}%\n"
f"Precipitation:\n"
f" - Total precipitation: {total_precip_mm} mm\n"
f" - Total snowfall: {total_snow_cm} cm\n"
f"Chance of precipitation:\n"
f" - Chance of rain: {chance_of_rain}%\n"
f" - Chance of snow: {chance_of_snow}%\n"
f"Weather status for the day: {status}\n"
+ (
f"It is currently daytime.\n"
if is_day
else f"It is currently nighttime.\n"
)
+ next_event
)
if rain_times:
results += "There is a chance of rain at the following times:\n"
for time in rain_times:
results += f"- {time}\n"
if snow_times:
results += "There is a chance of snow at the following times:\n"
for time in snow_times:
results += f"- {time}\n"
return f"{self.warning}\n{results}"
def get_forecast(self, location: dict):
"""Get the current weather information for a specified location."""
forecast_url = f"{WEATHERAPI_BASE_URL}/forecast.json"
forecast_params = {
"key": self.weatherapi_key,
"q": location["city"],
"format": "json",
"days": 1,
}
r = httpx.get(forecast_url, params=forecast_params).json()
self.forecast_response = r
def run(self, query) -> str | None:
location = get_location(query)
if not location:
self.warning = (
"Could not identify any location in the query. Defaulted to Halifax."
)
location = {"city": "Halifax", "country": "CA"}
self.get_forecast(location)
weather_info = self._format_weather_info()
return weather_info
class GetLocationMetadata(BaseModel):
"""Location information"""
city: str = Field(description="The city of the location.")
state: int = Field(
description="The state or province of the location. Must be the full state name"
)
state_code: int = Field(
description="The state or province of the location. Must be a 2-char string."
)
country: str = Field(
description="The country of the location. Country must be a 2-char string"
)
def get_location(query: str) -> dict | None:
params = {"temperature": 0.0, "max_tokens": 100}
system_prompt = (
"You reply ONLY with the location information. If no location is detected, respond with None in "
"each field"
)
ai = AIChat(system=system_prompt, params=params)
# noinspection PyTypeChecker
location: dict = ai(query, output_schema=GetLocationMetadata)
if location["city"] == "None":
return None
return location