| import requests |
| from geopy.geocoders import Nominatim |
| from datetime import datetime, date, timedelta |
| import pandas as pd |
| import gradio as gr |
|
|
| |
| WEATHER_CODES = { |
| 0: "Clear sky", 1: "Mainly clear", 2: "Partly cloudy", 3: "Overcast", |
| 45: "Fog", 48: "Depositing rime fog", 51: "Light drizzle", 53: "Moderate drizzle", 55: "Dense drizzle", |
| 61: "Slight rain", 63: "Moderate rain", 65: "Heavy rain", 71: "Slight snowfall", 73: "Moderate snowfall", |
| 75: "Heavy snowfall", 80: "Slight rain showers", 81: "Moderate rain showers", 82: "Violent rain showers", |
| 95: "Thunderstorm", 96: "Thunderstorm with hail" |
| } |
|
|
| def get_weather_forecast(location_name, target_date, include_hourly=False): |
| try: |
| if isinstance(target_date, str): |
| target_date = datetime.strptime(target_date, "%Y-%m-%d").date() |
| date_str = target_date.isoformat() |
|
|
| geolocator = Nominatim(user_agent="weather_api") |
| location = geolocator.geocode(location_name) |
| if not location: |
| return {"error": f"Could not find coordinates for '{location_name}'."} |
| lat, lon = location.latitude, location.longitude |
|
|
| url = "https://api.open-meteo.com/v1/forecast" |
| params = { |
| "latitude": lat, |
| "longitude": lon, |
| "daily": "sunrise,sunset,uv_index_max,temperature_2m_max,temperature_2m_min,weather_code", |
| "temperature_unit": "celsius", |
| "windspeed_unit": "kmh", |
| "timeformat": "iso8601", |
| "timezone": "auto", |
| "start_date": date_str, |
| "end_date": date_str |
| } |
|
|
| if include_hourly: |
| params["hourly"] = "temperature_2m,weather_code,uv_index,visibility" |
|
|
| response = requests.get(url, params=params) |
| if response.status_code != 200: |
| return {"error": f"API error {response.status_code}: {response.text}"} |
| raw = response.json() |
|
|
| if "daily" not in raw or date_str not in raw["daily"]["time"]: |
| return {"error": f"Weather data for {date_str} not available."} |
|
|
| idx = raw["daily"]["time"].index(date_str) |
| result = { |
| "date": date_str, |
| "sunrise": raw["daily"]["sunrise"][idx].split("T")[1], |
| "sunset": raw["daily"]["sunset"][idx].split("T")[1], |
| "uv_max": round(raw["daily"]["uv_index_max"][idx], 1), |
| "temp_min": round(raw["daily"]["temperature_2m_min"][idx]), |
| "temp_max": round(raw["daily"]["temperature_2m_max"][idx]), |
| "weather": WEATHER_CODES.get(int(raw["daily"]["weather_code"][idx]), "Unknown") |
| } |
|
|
| if include_hourly and "hourly" in raw: |
| hourly_df = pd.DataFrame({ |
| "time": raw["hourly"]["time"], |
| "temp": raw["hourly"]["temperature_2m"], |
| "code": raw["hourly"]["weather_code"], |
| "uv": raw["hourly"]["uv_index"], |
| "visibility": [v / 1000 for v in raw["hourly"]["visibility"]] |
| }) |
| hourly_df["time"] = pd.to_datetime(hourly_df["time"]) |
| hourly_df = hourly_df[hourly_df["time"].dt.date == target_date] |
| hourly_df["weather"] = hourly_df["code"].apply(lambda c: WEATHER_CODES.get(int(c), "Unknown")) |
|
|
| result["hourly"] = [ |
| { |
| "time": t.strftime("%Y-%m-%d %H:%M"), |
| "temp": f"{round(temp)}°C", |
| "weather": w, |
| "uv": round(uv, 1), |
| "visibility": f"{round(vis, 1)} km" |
| } |
| for t, temp, w, uv, vis in zip( |
| hourly_df["time"], hourly_df["temp"], hourly_df["weather"], |
| hourly_df["uv"], hourly_df["visibility"] |
| ) |
| ] |
| elif include_hourly: |
| result["note"] = "Hourly weather data unavailable for this date." |
|
|
| return result |
|
|
| except Exception as e: |
| return {"error": str(e)} |
|
|
|
|
| def get_weather_forecast_range(location_name, start_date, end_date): |
| try: |
| if isinstance(start_date, str): |
| start_date = datetime.strptime(start_date, "%Y-%m-%d").date() |
| if isinstance(end_date, str): |
| end_date = datetime.strptime(end_date, "%Y-%m-%d").date() |
|
|
| today = date.today() |
| days_ahead = (end_date - today).days |
|
|
| if days_ahead > 15: |
| return {"error": "Weather data only available up to 15 days from today."} |
|
|
| geolocator = Nominatim(user_agent="weather_api") |
| location = geolocator.geocode(location_name) |
| if not location: |
| return {"error": f"Could not find coordinates for '{location_name}'."} |
| lat, lon = location.latitude, location.longitude |
|
|
| include_hourly = days_ahead <= 6 |
|
|
| url = "https://api.open-meteo.com/v1/forecast" |
| params = { |
| "latitude": lat, |
| "longitude": lon, |
| "daily": "sunrise,sunset,uv_index_max,temperature_2m_max,temperature_2m_min,weather_code", |
| "temperature_unit": "celsius", |
| "windspeed_unit": "kmh", |
| "timeformat": "iso8601", |
| "timezone": "auto", |
| "start_date": start_date.isoformat(), |
| "end_date": end_date.isoformat() |
| } |
|
|
| if include_hourly: |
| params["hourly"] = "temperature_2m,weather_code,uv_index,visibility" |
|
|
| response = requests.get(url, params=params) |
| if response.status_code != 200: |
| return {"error": f"API error {response.status_code}: {response.text}"} |
| raw = response.json() |
|
|
| forecasts = [] |
| for idx, d in enumerate(raw["daily"]["time"]): |
| day_result = { |
| "date": d, |
| "sunrise": raw["daily"]["sunrise"][idx].split("T")[1], |
| "sunset": raw["daily"]["sunset"][idx].split("T")[1], |
| "uv_max": round(raw["daily"]["uv_index_max"][idx], 1), |
| "temp_min": round(raw["daily"]["temperature_2m_min"][idx]), |
| "temp_max": round(raw["daily"]["temperature_2m_max"][idx]), |
| "weather": WEATHER_CODES.get(int(raw["daily"]["weather_code"][idx]), "Unknown") |
| } |
|
|
| if include_hourly and "hourly" in raw: |
| hourly_df = pd.DataFrame({ |
| "time": raw["hourly"]["time"], |
| "temp": raw["hourly"]["temperature_2m"], |
| "code": raw["hourly"]["weather_code"], |
| "uv": raw["hourly"]["uv_index"], |
| "visibility": [v / 1000 for v in raw["hourly"]["visibility"]] |
| }) |
|
|
| hourly_df["time"] = pd.to_datetime(hourly_df["time"]) |
| target_date = datetime.strptime(d, "%Y-%m-%d").date() |
| df_day = hourly_df[hourly_df["time"].dt.date == target_date] |
|
|
| df_day["weather"] = df_day["code"].apply(lambda c: WEATHER_CODES.get(int(c), "Unknown")) |
|
|
| day_result["hourly"] = [ |
| { |
| "time": t.strftime("%Y-%m-%d %H:%M"), |
| "temp": f"{round(temp)}°C", |
| "weather": w, |
| "uv": round(uv, 1), |
| "visibility": f"{round(vis, 1)} km" |
| } |
| for t, temp, w, uv, vis in zip( |
| df_day["time"], df_day["temp"], df_day["weather"], |
| df_day["uv"], df_day["visibility"] |
| ) |
| ] |
| else: |
| day_result["note"] = "Hourly weather data is only available for the next 7 days." |
|
|
| forecasts.append(day_result) |
|
|
| return forecasts |
|
|
| except Exception as e: |
| return {"error": str(e)} |
| |
| demo= gr.Interface( |
| fn=get_weather_forecast_range, |
| inputs=[ |
| gr.Textbox(label="Location Name", placeholder="Enter a city or place name"), |
| gr.Textbox(label="Start Date (YYYY-MM-DD)", value=date.today()), |
| gr.Textbox(label="End Date (YYYY-MM-DD)", value=date.today() + timedelta(days=6)) |
| ], |
| outputs=gr.JSON(label="Weather Forecast"), |
| title="Weather Forecast Tool", |
| description="Get weather forecasts for a specific location and date range." |
| ) |
|
|
| demo.launch(mcp_server=True, share=True) |