import os import gradio as gr import requests import openai # Load API keys weather_api_key = os.getenv("openweather") openai.api_key = os.getenv("OPENAI_API_KEY") # ------------- WEATHER FETCH FUNCTION ------------- def get_weather(city_name): if not city_name.strip(): city_name = "Dubai" try: url = f"https://api.openweathermap.org/data/2.5/weather?q={city_name}&appid={weather_api_key}&units=metric" data = requests.get(url).json() if data["cod"] == 200: rain = data.get("rain", {}).get("1h", 0) condition = data["weather"][0]["main"] emoji_map = { "Clear": "β˜€οΈ", "Clouds": "☁️", "Rain": "🌧️", "Snow": "❄️", "Thunderstorm": "β›ˆοΈ", "Drizzle": "🌦️", "Mist": "🌫️", "Haze": "🌁", "Fog": "🌫️" } emoji = emoji_map.get(condition, "🌈") return { "city": data["name"], "country": data["sys"]["country"], "temperature": int(data["main"]["temp"]), "feels_like": int(data["main"]["feels_like"]), "humidity": data["main"]["humidity"], "pressure": data["main"]["pressure"], "description": f"{data['weather'][0]['description'].title()} {emoji}", "wind_speed": data["wind"]["speed"], "visibility": data.get("visibility", 10000) // 1000, "rain_chance": f"{rain} mm" } else: return None except: return None # ------------- WEATHER DISPLAY ------------- def format_weather_display(data): if not data: return "
❌ City not found. Please try again.
" font_color = "#2d3436" card_bg = "#e8f5e9" main_bg = "#ffffff" return f"""

πŸ“ {data['city']}, {data['country']}

{data['temperature']}Β°C

{data['description']}

πŸ’§
{data['rain_chance']}
Precipitation
πŸ“Š
{data['pressure']} mb
Pressure
πŸ’¨
{data['wind_speed']} km/h
Wind Speed
🌑️
{data['feels_like']}Β°C
Feels Like
πŸ‘οΈ
{data['visibility']} km
Visibility
πŸ’¦
{data['humidity']}%
Humidity
""" # ------------- CHATBOT FUNCTION (OpenAI) ------------- def travel_chat(user_input, history): try: messages = [{"role": "system", "content": "You are TripMate AI, a helpful travel assistant. Provide travel tips, cultural insights, and activity suggestions."}] for h in history: messages.append({"role": "user", "content": h["role"] == "user" and h["content"] or ""}) messages.append({"role": "assistant", "content": h["role"] == "assistant" and h["content"] or ""}) messages.append({"role": "user", "content": user_input}) response = openai.chat.completions.create( model="gpt-4o-mini", messages=messages ) reply = response.choices[0].message.content except: reply = "⚠️ Unable to get a response. Try again." history.append({"role": "user", "content": user_input}) history.append({"role": "assistant", "content": reply}) return history, history # ------------- PLACE SUGGESTIONS (OpenAI) ------------- def get_places_to_visit(city, country, temp, description): prompt = f""" Suggest 6-9 must-visit attractions or experiences in {city}, {country} considering that the weather is {description}, temperature is {temp}Β°C. Return the results as Python tuples in this format: ("Place Name", "Short description of the place and why it’s worth visiting") """ try: response = openai.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": prompt}] ) content = response.choices[0].message.content.strip() places = [] for line in content.split('\n'): line = line.strip().rstrip(',') if line.startswith('(') and line.endswith(')'): try: place_tuple = eval(line) if isinstance(place_tuple, tuple) and len(place_tuple) == 2: places.append(place_tuple) except: continue return places except Exception as e: return [("Error", str(e))] # Format place card def format_place_card(name, details): return f"""
{name}
{details}
""" # Generate place cards def generate_place_cards(city): weather = get_weather(city) if not weather: return "
⚠️ Couldn't fetch places due to missing weather data.
" places = get_places_to_visit( city=weather["city"], country=weather["country"], temp=weather["temperature"], description=weather["description"] ) return "
" + "".join(format_place_card(name, details) for name, details in places) + "
" # ------------- CSS ------------- custom_css = """ body, .gradio-container { background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%) !important; font-family: 'Segoe UI', 'Roboto', sans-serif; min-height: 100vh; } #main-title { text-align: center; font-size: 2.5rem; font-weight: 700; margin-bottom: 10px; color: #2e7d32; } .section-header { background: linear-gradient(135deg, #43a047 0%, #388e3c 100%); color: white; padding: 12px 20px; border-radius: 12px 12px 0 0; text-align: center; } .content-box { background-color: #ffffff; border-radius: 0 0 16px 16px; padding: 25px; min-height: 520px; } .card-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 18px; margin-top: 25px; } .crop-card { background: #ffffff; border: 1px solid #c8e6c9; border-radius: 16px; padding: 20px; } .crop-name { font-size: 20px; font-weight: 600; color: #2e7d32; margin-bottom: 12px; text-align: center; } .crop-description { font-size: 15px; color: #444; line-height: 1.5; text-align: center; } .footer { background: linear-gradient(135deg, #2e7d32 0%, #1b5e20 100%); color: white; padding: 30px; border-radius: 16px; margin-top: 30px; text-align: center; font-size: 14px; } """ # ------------- UI ------------- def launch_ui(): with gr.Blocks(css=custom_css, title="TripMate AI") as demo: gr.Markdown("
🌍 TripMate AI
") with gr.Row(equal_height=True): # Weather with gr.Column(scale=1): gr.Markdown("
🌀️ Weather
") with gr.Group(elem_classes="content-box"): city_input = gr.Textbox(label="City", value="Dubai", placeholder="Enter city name") update_btn = gr.Button("Get Weather") weather_html = gr.HTML() # Chat with gr.Column(scale=1): gr.Markdown("
πŸ€– Travel Assistant
") with gr.Group(elem_classes="content-box"): chat = gr.Chatbot(height=350, type="messages") with gr.Row(): message = gr.Textbox(placeholder="Ask about travel tips...", show_label=False, scale=4) ask_btn = gr.Button("Send", scale=1) state = gr.State([]) gr.Markdown("
πŸ—ΊοΈ Places to Visit
") place_cards_html = gr.HTML() gr.HTML(""" """) update_btn.click( fn=lambda city: (format_weather_display(get_weather(city)), generate_place_cards(city)), inputs=city_input, outputs=[weather_html, place_cards_html] ) city_input.submit( fn=lambda city: (format_weather_display(get_weather(city)), generate_place_cards(city)), inputs=city_input, outputs=[weather_html, place_cards_html] ) ask_btn.click(fn=travel_chat, inputs=[message, state], outputs=[chat, state]) message.submit(fn=travel_chat, inputs=[message, state], outputs=[chat, state]) demo.load(fn=lambda: (format_weather_display(get_weather("Dubai")), generate_place_cards("Dubai")), inputs=None, outputs=[weather_html, place_cards_html]) demo.launch() launch_ui()