Spaces:
Sleeping
Sleeping
import os | |
os.system("pip install geopy -q") | |
import gradio as gr | |
from datetime import datetime | |
import requests | |
from geopy.geocoders import Nominatim | |
from datetime import datetime, date, timedelta | |
import pandas as pd | |
import json | |
from pathlib import Path | |
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_range(location_name, start_date, end_date): | |
""" | |
Fetches weather forecast for a given location and date range, returns a Markdown summary. | |
Also saves the forecast as a JSON file under the 'logs' directory. | |
Parameters: | |
location_name (str): Name of the location (e.g., city or landmark).\n | |
start_date (str or date): Start date of the forecast in 'YYYY-MM-DD' format or as a date object.\n | |
end_date (str or date): End date of the forecast in 'YYYY-MM-DD' format or as a date object.\n | |
Returns: | |
str: Markdown-formatted string with weather details or an error message.\n | |
""" | |
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 f"**โ ๏ธ 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 f"**โ ๏ธ Error:** 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 f"**โ ๏ธ Error:** 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) | |
# Save the forecast JSON | |
save_dir = Path("logs") | |
save_dir.mkdir(exist_ok=True) | |
save_path = save_dir / f"weather_{location_name}_{start_date}_to_{end_date}.json" | |
with open(save_path, "w", encoding="utf-8") as f: | |
json.dump(forecasts, f, indent=2) | |
# Generate Markdown output | |
md = f"## Weather Forecast for {location_name}\n\n" | |
for day in forecasts: | |
md += f"### ๐ {day['date']}\n" | |
md += f"- ๐ Sunrise: {day['sunrise']}\n" | |
md += f"- ๐ Sunset: {day['sunset']}\n" | |
md += f"- ๐ก๏ธ Temp: {day['temp_min']}ยฐC โ {day['temp_max']}ยฐC\n" | |
md += f"- ๐ค๏ธ Weather: {day['weather']}\n" | |
md += f"- โ๏ธ UV Index Max: {day['uv_max']}\n" | |
if "hourly" in day: | |
md += f"**Hourly Forecast:**\n" | |
for h in day["hourly"]: | |
md += f" - {h['time']}: {h['temp']}, {h['weather']}, UV {h['uv']}, Visibility {h['visibility']}\n" | |
elif "note" in day: | |
md += f"_{day['note']}_\n" | |
md += "\n" | |
return md | |
except Exception as e: | |
return f"**โ ๏ธ Error:** {str(e)}" | |
with gr.Blocks(title="TravAI Weather Forecast", theme=gr.themes.Soft(primary_hue="indigo")) as demo: | |
gr.Markdown(""" | |
# ๐ฆ๏ธ TravAI: Weather Forecast Tool | |
_Your smart travel weather companion, powered by Open-Meteo API._ | |
""") | |
with gr.Group(): | |
with gr.Row(): | |
location = gr.Textbox(label="๐ Location", placeholder="Enter a city or landmark", scale=2) | |
start = gr.Textbox(label="๐๏ธ Start Date", value=str(date.today()), scale=1) | |
end = gr.Textbox(label="๐ End Date", value=str(date.today() + timedelta(days=6)), scale=1) | |
submit_btn = gr.Button("๐ Get Forecast") | |
output = gr.Markdown(label="๐ Forecast Output") | |
submit_btn.click(fn=get_weather_forecast_range, | |
inputs=[location, start, end], | |
outputs=output) | |
gr.Markdown(""" | |
--- | |
โ๏ธ _Built with [Gradio](https://www.gradio.app) & [Open-Meteo](https://open-meteo.com) โข Developed during MCP Hackathon 2025_ | |
""") | |
if __name__ == "__main__": | |
demo.launch(mcp_server=True, share=True) |