TripMate_AI / app.py
Shafaq25's picture
Create app.py
ad800f0 verified
raw
history blame
10.6 kB
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 "<div style='text-align:center; color: #e74c3c; font-size: 18px; padding: 40px;'>❌ City not found. Please try again.</div>"
font_color = "#2d3436"
card_bg = "#e8f5e9"
main_bg = "#ffffff"
return f"""
<div style="background: {main_bg}; border-radius: 16px; padding: 25px; box-shadow: 0 10px 30px rgba(0,0,0,0.1);">
<div style="text-align: center; margin-bottom: 25px;">
<h2 style="margin: 0; color: {font_color}; font-size: 24px; font-weight: 600;">πŸ“ {data['city']}, {data['country']}</h2>
<h1 style="margin: 10px 0; font-size: 64px; color: {font_color}; font-weight: 300;">{data['temperature']}Β°C</h1>
<p style="margin: 5px 0; color: {font_color}; font-size: 18px; font-weight: 500;">{data['description']}</p>
</div>
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; margin-top: 25px;">
<div style="background: {card_bg}; border-radius: 12px; padding: 16px; text-align: center; color: {font_color};">
πŸ’§<br><strong>{data['rain_chance']}</strong><br><span>Precipitation</span>
</div>
<div style="background: {card_bg}; border-radius: 12px; padding: 16px; text-align: center; color: {font_color};">
πŸ“Š<br><strong>{data['pressure']} mb</strong><br><span>Pressure</span>
</div>
<div style="background: {card_bg}; border-radius: 12px; padding: 16px; text-align: center; color: {font_color};">
πŸ’¨<br><strong>{data['wind_speed']} km/h</strong><br><span>Wind Speed</span>
</div>
<div style="background: {card_bg}; border-radius: 12px; padding: 16px; text-align: center; color: {font_color};">
🌑️<br><strong>{data['feels_like']}°C</strong><br><span>Feels Like</span>
</div>
<div style="background: {card_bg}; border-radius: 12px; padding: 16px; text-align: center; color: {font_color};">
πŸ‘οΈ<br><strong>{data['visibility']} km</strong><br><span>Visibility</span>
</div>
<div style="background: {card_bg}; border-radius: 12px; padding: 16px; text-align: center; color: {font_color};">
πŸ’¦<br><strong>{data['humidity']}%</strong><br><span>Humidity</span>
</div>
</div>
</div>
"""
# ------------- 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"""
<div class='crop-card'>
<div class='crop-name'>{name}</div>
<div class='crop-description'>{details}</div>
</div>
"""
# Generate place cards
def generate_place_cards(city):
weather = get_weather(city)
if not weather:
return "<div style='padding:20px; color:red;'>⚠️ Couldn't fetch places due to missing weather data.</div>"
places = get_places_to_visit(
city=weather["city"],
country=weather["country"],
temp=weather["temperature"],
description=weather["description"]
)
return "<div class='card-grid'>" + "".join(format_place_card(name, details) for name, details in places) + "</div>"
# ------------- 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("<div id='main-title'>🌍 TripMate AI</div>")
with gr.Row(equal_height=True):
# Weather
with gr.Column(scale=1):
gr.Markdown("<div class='section-header'>🌀️ Weather</div>")
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("<div class='section-header'>πŸ€– Travel Assistant</div>")
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("<div class='section-header'>πŸ—ΊοΈ Places to Visit</div>")
place_cards_html = gr.HTML()
gr.HTML("""
<div class='footer'>
πŸ’š Built by Shafaq Mandha | Powered by OpenWeather & OpenAI | TripMate AI Β© 2025
</div>
""")
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()