File size: 5,663 Bytes
b36a86c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
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