Shafaq25 commited on
Commit
ad800f0
·
verified ·
1 Parent(s): 92d1d75

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +266 -0
app.py ADDED
@@ -0,0 +1,266 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gradio as gr
3
+ import requests
4
+ import openai
5
+
6
+ # Load API keys
7
+ weather_api_key = os.getenv("openweather")
8
+ openai.api_key = os.getenv("OPENAI_API_KEY")
9
+
10
+ # ------------- WEATHER FETCH FUNCTION -------------
11
+ def get_weather(city_name):
12
+ if not city_name.strip():
13
+ city_name = "Dubai"
14
+ try:
15
+ url = f"https://api.openweathermap.org/data/2.5/weather?q={city_name}&appid={weather_api_key}&units=metric"
16
+ data = requests.get(url).json()
17
+ if data["cod"] == 200:
18
+ rain = data.get("rain", {}).get("1h", 0)
19
+ condition = data["weather"][0]["main"]
20
+ emoji_map = {
21
+ "Clear": "☀️", "Clouds": "☁️", "Rain": "🌧️",
22
+ "Snow": "❄️", "Thunderstorm": "⛈️", "Drizzle": "🌦️",
23
+ "Mist": "🌫️", "Haze": "🌁", "Fog": "🌫️"
24
+ }
25
+ emoji = emoji_map.get(condition, "🌈")
26
+ return {
27
+ "city": data["name"],
28
+ "country": data["sys"]["country"],
29
+ "temperature": int(data["main"]["temp"]),
30
+ "feels_like": int(data["main"]["feels_like"]),
31
+ "humidity": data["main"]["humidity"],
32
+ "pressure": data["main"]["pressure"],
33
+ "description": f"{data['weather'][0]['description'].title()} {emoji}",
34
+ "wind_speed": data["wind"]["speed"],
35
+ "visibility": data.get("visibility", 10000) // 1000,
36
+ "rain_chance": f"{rain} mm"
37
+ }
38
+ else:
39
+ return None
40
+ except:
41
+ return None
42
+
43
+ # ------------- WEATHER DISPLAY -------------
44
+ def format_weather_display(data):
45
+ if not data:
46
+ return "<div style='text-align:center; color: #e74c3c; font-size: 18px; padding: 40px;'>❌ City not found. Please try again.</div>"
47
+
48
+ font_color = "#2d3436"
49
+ card_bg = "#e8f5e9"
50
+ main_bg = "#ffffff"
51
+
52
+ return f"""
53
+ <div style="background: {main_bg}; border-radius: 16px; padding: 25px; box-shadow: 0 10px 30px rgba(0,0,0,0.1);">
54
+ <div style="text-align: center; margin-bottom: 25px;">
55
+ <h2 style="margin: 0; color: {font_color}; font-size: 24px; font-weight: 600;">📍 {data['city']}, {data['country']}</h2>
56
+ <h1 style="margin: 10px 0; font-size: 64px; color: {font_color}; font-weight: 300;">{data['temperature']}°C</h1>
57
+ <p style="margin: 5px 0; color: {font_color}; font-size: 18px; font-weight: 500;">{data['description']}</p>
58
+ </div>
59
+ <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; margin-top: 25px;">
60
+ <div style="background: {card_bg}; border-radius: 12px; padding: 16px; text-align: center; color: {font_color};">
61
+ 💧<br><strong>{data['rain_chance']}</strong><br><span>Precipitation</span>
62
+ </div>
63
+ <div style="background: {card_bg}; border-radius: 12px; padding: 16px; text-align: center; color: {font_color};">
64
+ 📊<br><strong>{data['pressure']} mb</strong><br><span>Pressure</span>
65
+ </div>
66
+ <div style="background: {card_bg}; border-radius: 12px; padding: 16px; text-align: center; color: {font_color};">
67
+ 💨<br><strong>{data['wind_speed']} km/h</strong><br><span>Wind Speed</span>
68
+ </div>
69
+ <div style="background: {card_bg}; border-radius: 12px; padding: 16px; text-align: center; color: {font_color};">
70
+ 🌡️<br><strong>{data['feels_like']}°C</strong><br><span>Feels Like</span>
71
+ </div>
72
+ <div style="background: {card_bg}; border-radius: 12px; padding: 16px; text-align: center; color: {font_color};">
73
+ 👁️<br><strong>{data['visibility']} km</strong><br><span>Visibility</span>
74
+ </div>
75
+ <div style="background: {card_bg}; border-radius: 12px; padding: 16px; text-align: center; color: {font_color};">
76
+ 💦<br><strong>{data['humidity']}%</strong><br><span>Humidity</span>
77
+ </div>
78
+ </div>
79
+ </div>
80
+ """
81
+
82
+ # ------------- CHATBOT FUNCTION (OpenAI) -------------
83
+ def travel_chat(user_input, history):
84
+ try:
85
+ messages = [{"role": "system", "content": "You are TripMate AI, a helpful travel assistant. Provide travel tips, cultural insights, and activity suggestions."}]
86
+ for h in history:
87
+ messages.append({"role": "user", "content": h["role"] == "user" and h["content"] or ""})
88
+ messages.append({"role": "assistant", "content": h["role"] == "assistant" and h["content"] or ""})
89
+ messages.append({"role": "user", "content": user_input})
90
+
91
+ response = openai.chat.completions.create(
92
+ model="gpt-4o-mini",
93
+ messages=messages
94
+ )
95
+ reply = response.choices[0].message.content
96
+ except:
97
+ reply = "⚠️ Unable to get a response. Try again."
98
+ history.append({"role": "user", "content": user_input})
99
+ history.append({"role": "assistant", "content": reply})
100
+ return history, history
101
+
102
+ # ------------- PLACE SUGGESTIONS (OpenAI) -------------
103
+ def get_places_to_visit(city, country, temp, description):
104
+ prompt = f"""
105
+ Suggest 6-9 must-visit attractions or experiences in {city}, {country} considering that the weather is {description}, temperature is {temp}°C.
106
+ Return the results as Python tuples in this format:
107
+ ("Place Name", "Short description of the place and why it’s worth visiting")
108
+ """
109
+ try:
110
+ response = openai.chat.completions.create(
111
+ model="gpt-4o-mini",
112
+ messages=[{"role": "user", "content": prompt}]
113
+ )
114
+ content = response.choices[0].message.content.strip()
115
+ places = []
116
+ for line in content.split('\n'):
117
+ line = line.strip().rstrip(',')
118
+ if line.startswith('(') and line.endswith(')'):
119
+ try:
120
+ place_tuple = eval(line)
121
+ if isinstance(place_tuple, tuple) and len(place_tuple) == 2:
122
+ places.append(place_tuple)
123
+ except:
124
+ continue
125
+ return places
126
+ except Exception as e:
127
+ return [("Error", str(e))]
128
+
129
+ # Format place card
130
+ def format_place_card(name, details):
131
+ return f"""
132
+ <div class='crop-card'>
133
+ <div class='crop-name'>{name}</div>
134
+ <div class='crop-description'>{details}</div>
135
+ </div>
136
+ """
137
+
138
+ # Generate place cards
139
+ def generate_place_cards(city):
140
+ weather = get_weather(city)
141
+ if not weather:
142
+ return "<div style='padding:20px; color:red;'>⚠️ Couldn't fetch places due to missing weather data.</div>"
143
+ places = get_places_to_visit(
144
+ city=weather["city"],
145
+ country=weather["country"],
146
+ temp=weather["temperature"],
147
+ description=weather["description"]
148
+ )
149
+ return "<div class='card-grid'>" + "".join(format_place_card(name, details) for name, details in places) + "</div>"
150
+
151
+ # ------------- CSS -------------
152
+ custom_css = """
153
+ body, .gradio-container {
154
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%) !important;
155
+ font-family: 'Segoe UI', 'Roboto', sans-serif;
156
+ min-height: 100vh;
157
+ }
158
+ #main-title {
159
+ text-align: center;
160
+ font-size: 2.5rem;
161
+ font-weight: 700;
162
+ margin-bottom: 10px;
163
+ color: #2e7d32;
164
+ }
165
+ .section-header {
166
+ background: linear-gradient(135deg, #43a047 0%, #388e3c 100%);
167
+ color: white;
168
+ padding: 12px 20px;
169
+ border-radius: 12px 12px 0 0;
170
+ text-align: center;
171
+ }
172
+ .content-box {
173
+ background-color: #ffffff;
174
+ border-radius: 0 0 16px 16px;
175
+ padding: 25px;
176
+ min-height: 520px;
177
+ }
178
+ .card-grid {
179
+ display: grid;
180
+ grid-template-columns: repeat(3, 1fr);
181
+ gap: 18px;
182
+ margin-top: 25px;
183
+ }
184
+ .crop-card {
185
+ background: #ffffff;
186
+ border: 1px solid #c8e6c9;
187
+ border-radius: 16px;
188
+ padding: 20px;
189
+ }
190
+ .crop-name {
191
+ font-size: 20px;
192
+ font-weight: 600;
193
+ color: #2e7d32;
194
+ margin-bottom: 12px;
195
+ text-align: center;
196
+ }
197
+ .crop-description {
198
+ font-size: 15px;
199
+ color: #444;
200
+ line-height: 1.5;
201
+ text-align: center;
202
+ }
203
+ .footer {
204
+ background: linear-gradient(135deg, #2e7d32 0%, #1b5e20 100%);
205
+ color: white;
206
+ padding: 30px;
207
+ border-radius: 16px;
208
+ margin-top: 30px;
209
+ text-align: center;
210
+ font-size: 14px;
211
+ }
212
+ """
213
+
214
+ # ------------- UI -------------
215
+ def launch_ui():
216
+ with gr.Blocks(css=custom_css, title="TripMate AI") as demo:
217
+ gr.Markdown("<div id='main-title'>🌍 TripMate AI</div>")
218
+
219
+ with gr.Row(equal_height=True):
220
+ # Weather
221
+ with gr.Column(scale=1):
222
+ gr.Markdown("<div class='section-header'>🌤️ Weather</div>")
223
+ with gr.Group(elem_classes="content-box"):
224
+ city_input = gr.Textbox(label="City", value="Dubai", placeholder="Enter city name")
225
+ update_btn = gr.Button("Get Weather")
226
+ weather_html = gr.HTML()
227
+
228
+ # Chat
229
+ with gr.Column(scale=1):
230
+ gr.Markdown("<div class='section-header'>🤖 Travel Assistant</div>")
231
+ with gr.Group(elem_classes="content-box"):
232
+ chat = gr.Chatbot(height=350, type="messages")
233
+ with gr.Row():
234
+ message = gr.Textbox(placeholder="Ask about travel tips...", show_label=False, scale=4)
235
+ ask_btn = gr.Button("Send", scale=1)
236
+ state = gr.State([])
237
+
238
+ gr.Markdown("<div class='section-header'>🗺️ Places to Visit</div>")
239
+ place_cards_html = gr.HTML()
240
+
241
+ gr.HTML("""
242
+ <div class='footer'>
243
+ 💚 Built by Shafaq Mandha | Powered by OpenWeather & OpenAI | TripMate AI © 2025
244
+ </div>
245
+ """)
246
+
247
+ update_btn.click(
248
+ fn=lambda city: (format_weather_display(get_weather(city)), generate_place_cards(city)),
249
+ inputs=city_input,
250
+ outputs=[weather_html, place_cards_html]
251
+ )
252
+ city_input.submit(
253
+ fn=lambda city: (format_weather_display(get_weather(city)), generate_place_cards(city)),
254
+ inputs=city_input,
255
+ outputs=[weather_html, place_cards_html]
256
+ )
257
+ ask_btn.click(fn=travel_chat, inputs=[message, state], outputs=[chat, state])
258
+ message.submit(fn=travel_chat, inputs=[message, state], outputs=[chat, state])
259
+
260
+ demo.load(fn=lambda: (format_weather_display(get_weather("Dubai")), generate_place_cards("Dubai")),
261
+ inputs=None,
262
+ outputs=[weather_html, place_cards_html])
263
+
264
+ demo.launch()
265
+
266
+ launch_ui()