Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -2,13 +2,16 @@ import os
|
|
2 |
import gradio as gr
|
3 |
import requests
|
4 |
from openai import OpenAI
|
|
|
5 |
|
6 |
-
# API
|
7 |
weather_api_key = os.getenv("openweather")
|
8 |
openai_api_key = os.getenv("OPENAI_API_KEY")
|
9 |
client = OpenAI(api_key=openai_api_key)
|
10 |
|
11 |
-
#
|
|
|
|
|
12 |
def get_weather(city_name):
|
13 |
if not city_name.strip():
|
14 |
city_name = "Dubai"
|
@@ -16,7 +19,7 @@ def get_weather(city_name):
|
|
16 |
url = f"https://api.openweathermap.org/data/2.5/weather?q={city_name}&appid={weather_api_key}&units=metric"
|
17 |
data = requests.get(url).json()
|
18 |
|
19 |
-
if data
|
20 |
rain = data.get("rain", {}).get("1h", 0)
|
21 |
condition = data["weather"][0]["main"]
|
22 |
emoji_map = {
|
@@ -38,13 +41,14 @@ def get_weather(city_name):
|
|
38 |
"visibility": data.get("visibility", 10000) // 1000,
|
39 |
"rain_chance": f"{rain} mm"
|
40 |
}
|
41 |
-
|
42 |
-
|
43 |
-
except:
|
44 |
return None
|
45 |
|
46 |
|
47 |
-
#
|
|
|
|
|
48 |
def format_weather_display(data):
|
49 |
if not data:
|
50 |
return "<div style='text-align:center; color: #e74c3c; font-size: 18px; padding: 40px;'>β City not found. Please try again.</div>"
|
@@ -96,7 +100,9 @@ def format_weather_display(data):
|
|
96 |
"""
|
97 |
|
98 |
|
99 |
-
#
|
|
|
|
|
100 |
def travel_chat(msg, history):
|
101 |
messages = [{"role": "system", "content": "You are a helpful travel assistant. Suggest tourist attractions, activities, and travel tips for any city."}]
|
102 |
for h in history:
|
@@ -118,7 +124,9 @@ def travel_chat(msg, history):
|
|
118 |
return history, history
|
119 |
|
120 |
|
121 |
-
#
|
|
|
|
|
122 |
def get_attractions(city, country, temp, weather_desc):
|
123 |
try:
|
124 |
messages = [
|
@@ -139,19 +147,19 @@ def get_attractions(city, country, temp, weather_desc):
|
|
139 |
)
|
140 |
|
141 |
content = response.choices[0].message.content.strip()
|
142 |
-
|
143 |
attractions = []
|
144 |
-
for line in content.
|
145 |
line = line.strip().rstrip(',')
|
146 |
-
if line.startswith(
|
147 |
try:
|
148 |
-
attraction_tuple =
|
149 |
if isinstance(attraction_tuple, tuple) and len(attraction_tuple) == 2:
|
150 |
attractions.append(attraction_tuple)
|
151 |
-
except:
|
152 |
continue
|
|
|
153 |
return attractions if attractions else [("No attractions found", "Try another city.")]
|
154 |
-
except:
|
155 |
return [("Error", "Could not fetch attractions.")]
|
156 |
|
157 |
|
@@ -164,8 +172,9 @@ def format_attraction_card(name, details):
|
|
164 |
"""
|
165 |
|
166 |
|
|
|
167 |
# CSS
|
168 |
-
#
|
169 |
custom_css = """
|
170 |
body, .gradio-container {
|
171 |
background: linear-gradient(135deg, #f5f7fa 0%, #bbdefb 100%) !important;
|
@@ -176,7 +185,7 @@ body, .gradio-container {
|
|
176 |
text-align: center;
|
177 |
font-size: 2.8rem;
|
178 |
font-weight: 800;
|
179 |
-
margin: 20px 0
|
180 |
color: #0d47a1;
|
181 |
}
|
182 |
.section-header {
|
@@ -185,17 +194,10 @@ body, .gradio-container {
|
|
185 |
padding: 12px 20px;
|
186 |
border-radius: 12px 12px 0 0;
|
187 |
margin: 0;
|
188 |
-
font-size: 1.
|
189 |
font-weight: 600;
|
190 |
text-align: center;
|
191 |
}
|
192 |
-
.section-subheading {
|
193 |
-
text-align: center;
|
194 |
-
font-size: 0.95rem;
|
195 |
-
color: #444;
|
196 |
-
margin: 10px 0 15px 0;
|
197 |
-
font-style: italic;
|
198 |
-
}
|
199 |
.content-box {
|
200 |
background-color: #ffffff;
|
201 |
border-radius: 0 0 16px 16px;
|
@@ -236,27 +238,43 @@ body, .gradio-container {
|
|
236 |
text-align: center;
|
237 |
}
|
238 |
.footer {
|
239 |
-
background: linear-gradient(135deg, #0d47a1 0%, #002171 100%);
|
240 |
-
color: white;
|
241 |
-
padding: 20px;
|
242 |
text-align: center;
|
243 |
-
margin-top: 40px;
|
244 |
font-size: 14px;
|
245 |
-
|
|
|
|
|
246 |
}
|
247 |
"""
|
248 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
249 |
# UI
|
|
|
250 |
def launch_ui():
|
251 |
-
with gr.Blocks(css=custom_css, title="
|
252 |
-
#
|
253 |
-
gr.Markdown("<div id='main-title'>π
|
254 |
|
255 |
with gr.Row(equal_height=True):
|
256 |
# Weather section
|
257 |
with gr.Column(scale=1):
|
258 |
gr.Markdown("<div class='section-header'>π€οΈ Weather Dashboard</div>")
|
259 |
-
gr.Markdown("<div class='section-subheading'>Check current weather details</div>")
|
260 |
with gr.Group(elem_classes="content-box"):
|
261 |
city_input = gr.Textbox(label="ποΈ City Name", value="Dubai")
|
262 |
update_btn = gr.Button("π Get Weather Data")
|
@@ -264,8 +282,7 @@ def launch_ui():
|
|
264 |
|
265 |
# Chatbot section
|
266 |
with gr.Column(scale=1):
|
267 |
-
gr.Markdown("<div class='section-header'
|
268 |
-
gr.Markdown("<div class='section-subheading'>Ask for travel tips and suggestions</div>")
|
269 |
with gr.Group(elem_classes="content-box"):
|
270 |
chat = gr.Chatbot(height=350, type="messages")
|
271 |
with gr.Row():
|
@@ -275,16 +292,8 @@ def launch_ui():
|
|
275 |
|
276 |
# Attractions section
|
277 |
gr.Markdown("<div class='section-header'>ποΈ Top Attractions You Can Visit</div>")
|
278 |
-
gr.Markdown("<div class='section-subheading'>Plan your trip with these recommendations</div>")
|
279 |
attractions_html = gr.HTML()
|
280 |
|
281 |
-
# FOOTER
|
282 |
-
gr.HTML("""
|
283 |
-
<div class='footer'>
|
284 |
-
Built with β€οΈ | Powered by OpenWeather & OpenAI | Β© 2025 Travel Weather Guide
|
285 |
-
</div>
|
286 |
-
""")
|
287 |
-
|
288 |
def update_all(city):
|
289 |
return format_weather_display(get_weather(city)), generate_attraction_cards(city)
|
290 |
|
@@ -296,4 +305,10 @@ def launch_ui():
|
|
296 |
|
297 |
demo.load(fn=lambda: update_all("Dubai"), inputs=None, outputs=[weather_html, attractions_html])
|
298 |
|
|
|
|
|
|
|
299 |
demo.launch()
|
|
|
|
|
|
|
|
2 |
import gradio as gr
|
3 |
import requests
|
4 |
from openai import OpenAI
|
5 |
+
import ast
|
6 |
|
7 |
+
# API Keys
|
8 |
weather_api_key = os.getenv("openweather")
|
9 |
openai_api_key = os.getenv("OPENAI_API_KEY")
|
10 |
client = OpenAI(api_key=openai_api_key)
|
11 |
|
12 |
+
# -------------------------------
|
13 |
+
# Fetch Weather Data
|
14 |
+
# -------------------------------
|
15 |
def get_weather(city_name):
|
16 |
if not city_name.strip():
|
17 |
city_name = "Dubai"
|
|
|
19 |
url = f"https://api.openweathermap.org/data/2.5/weather?q={city_name}&appid={weather_api_key}&units=metric"
|
20 |
data = requests.get(url).json()
|
21 |
|
22 |
+
if data.get("cod") == 200:
|
23 |
rain = data.get("rain", {}).get("1h", 0)
|
24 |
condition = data["weather"][0]["main"]
|
25 |
emoji_map = {
|
|
|
41 |
"visibility": data.get("visibility", 10000) // 1000,
|
42 |
"rain_chance": f"{rain} mm"
|
43 |
}
|
44 |
+
return None
|
45 |
+
except Exception:
|
|
|
46 |
return None
|
47 |
|
48 |
|
49 |
+
# -------------------------------
|
50 |
+
# Weather Display HTML
|
51 |
+
# -------------------------------
|
52 |
def format_weather_display(data):
|
53 |
if not data:
|
54 |
return "<div style='text-align:center; color: #e74c3c; font-size: 18px; padding: 40px;'>β City not found. Please try again.</div>"
|
|
|
100 |
"""
|
101 |
|
102 |
|
103 |
+
# -------------------------------
|
104 |
+
# Chatbot
|
105 |
+
# -------------------------------
|
106 |
def travel_chat(msg, history):
|
107 |
messages = [{"role": "system", "content": "You are a helpful travel assistant. Suggest tourist attractions, activities, and travel tips for any city."}]
|
108 |
for h in history:
|
|
|
124 |
return history, history
|
125 |
|
126 |
|
127 |
+
# -------------------------------
|
128 |
+
# Attractions
|
129 |
+
# -------------------------------
|
130 |
def get_attractions(city, country, temp, weather_desc):
|
131 |
try:
|
132 |
messages = [
|
|
|
147 |
)
|
148 |
|
149 |
content = response.choices[0].message.content.strip()
|
|
|
150 |
attractions = []
|
151 |
+
for line in content.splitlines():
|
152 |
line = line.strip().rstrip(',')
|
153 |
+
if line.startswith("(") and line.endswith(")"):
|
154 |
try:
|
155 |
+
attraction_tuple = ast.literal_eval(line)
|
156 |
if isinstance(attraction_tuple, tuple) and len(attraction_tuple) == 2:
|
157 |
attractions.append(attraction_tuple)
|
158 |
+
except Exception:
|
159 |
continue
|
160 |
+
|
161 |
return attractions if attractions else [("No attractions found", "Try another city.")]
|
162 |
+
except Exception:
|
163 |
return [("Error", "Could not fetch attractions.")]
|
164 |
|
165 |
|
|
|
172 |
"""
|
173 |
|
174 |
|
175 |
+
# -------------------------------
|
176 |
# CSS
|
177 |
+
# -------------------------------
|
178 |
custom_css = """
|
179 |
body, .gradio-container {
|
180 |
background: linear-gradient(135deg, #f5f7fa 0%, #bbdefb 100%) !important;
|
|
|
185 |
text-align: center;
|
186 |
font-size: 2.8rem;
|
187 |
font-weight: 800;
|
188 |
+
margin: 20px 0;
|
189 |
color: #0d47a1;
|
190 |
}
|
191 |
.section-header {
|
|
|
194 |
padding: 12px 20px;
|
195 |
border-radius: 12px 12px 0 0;
|
196 |
margin: 0;
|
197 |
+
font-size: 1.3rem;
|
198 |
font-weight: 600;
|
199 |
text-align: center;
|
200 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
201 |
.content-box {
|
202 |
background-color: #ffffff;
|
203 |
border-radius: 0 0 16px 16px;
|
|
|
238 |
text-align: center;
|
239 |
}
|
240 |
.footer {
|
|
|
|
|
|
|
241 |
text-align: center;
|
|
|
242 |
font-size: 14px;
|
243 |
+
padding: 20px;
|
244 |
+
color: #555;
|
245 |
+
margin-top: 20px;
|
246 |
}
|
247 |
"""
|
248 |
|
249 |
+
# -------------------------------
|
250 |
+
# Generate Attraction Cards
|
251 |
+
# -------------------------------
|
252 |
+
def generate_attraction_cards(city):
|
253 |
+
weather = get_weather(city)
|
254 |
+
if not weather:
|
255 |
+
return "<div style='padding:20px; color:red;'>β οΈ Couldn't fetch attractions due to missing weather data.</div>"
|
256 |
+
|
257 |
+
attractions = get_attractions(
|
258 |
+
city=weather["city"],
|
259 |
+
country=weather["country"],
|
260 |
+
temp=weather["temperature"],
|
261 |
+
weather_desc=weather["description"]
|
262 |
+
)
|
263 |
+
return "<div class='card-grid'>" + "".join(format_attraction_card(name, details) for name, details in attractions) + "</div>"
|
264 |
+
|
265 |
+
|
266 |
+
# -------------------------------
|
267 |
# UI
|
268 |
+
# -------------------------------
|
269 |
def launch_ui():
|
270 |
+
with gr.Blocks(css=custom_css, title="TripMate AI") as demo:
|
271 |
+
# Main Title
|
272 |
+
gr.Markdown("<div id='main-title'>π TripMate AI</div>")
|
273 |
|
274 |
with gr.Row(equal_height=True):
|
275 |
# Weather section
|
276 |
with gr.Column(scale=1):
|
277 |
gr.Markdown("<div class='section-header'>π€οΈ Weather Dashboard</div>")
|
|
|
278 |
with gr.Group(elem_classes="content-box"):
|
279 |
city_input = gr.Textbox(label="ποΈ City Name", value="Dubai")
|
280 |
update_btn = gr.Button("π Get Weather Data")
|
|
|
282 |
|
283 |
# Chatbot section
|
284 |
with gr.Column(scale=1):
|
285 |
+
gr.Markdown("<div class='section-header'>π€ Travel Assistant</div>")
|
|
|
286 |
with gr.Group(elem_classes="content-box"):
|
287 |
chat = gr.Chatbot(height=350, type="messages")
|
288 |
with gr.Row():
|
|
|
292 |
|
293 |
# Attractions section
|
294 |
gr.Markdown("<div class='section-header'>ποΈ Top Attractions You Can Visit</div>")
|
|
|
295 |
attractions_html = gr.HTML()
|
296 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
297 |
def update_all(city):
|
298 |
return format_weather_display(get_weather(city)), generate_attraction_cards(city)
|
299 |
|
|
|
305 |
|
306 |
demo.load(fn=lambda: update_all("Dubai"), inputs=None, outputs=[weather_html, attractions_html])
|
307 |
|
308 |
+
# Footer
|
309 |
+
gr.Markdown("<div class='footer'>Β© 2025 TripMate AI β Your AI-powered travel companion.</div>")
|
310 |
+
|
311 |
demo.launch()
|
312 |
+
|
313 |
+
|
314 |
+
launch_ui()
|