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)