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"""
"""
# 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("")
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("")
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("")
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()