Memoona648 commited on
Commit
0f2d909
Β·
verified Β·
1 Parent(s): b225c00

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +226 -0
app.py CHANGED
@@ -2,6 +2,12 @@ import streamlit as st
2
  from datetime import date
3
  from streamlit.components.v1 import html as st_html
4
 
 
 
 
 
 
 
5
  from planmate import utils
6
  from planmate.config import APP_TITLE, APP_TAGLINE, THEME, CURRENCY
7
  from planmate.flights import search_flights
@@ -12,6 +18,226 @@ from planmate.itinerary import build_itinerary
12
 
13
  st.set_page_config(page_title=APP_TITLE, page_icon="✈️", layout="wide")
14
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  # ---------- Modern Green Theme Styles ----------
16
  st.markdown(
17
  """
 
2
  from datetime import date
3
  from streamlit.components.v1 import html as st_html
4
 
5
+ from planmate import utils
6
+ from planmate.config import APP_TITLE, APP_TAGLINE, THEME, CURRENCY
7
+ from planmate.flights import search_flightsimport streamlit as st
8
+ from datetime import date
9
+ from streamlit.components.v1 import html as st_html
10
+
11
  from planmate import utils
12
  from planmate.config import APP_TITLE, APP_TAGLINE, THEME, CURRENCY
13
  from planmate.flights import search_flights
 
18
 
19
  st.set_page_config(page_title=APP_TITLE, page_icon="✈️", layout="wide")
20
 
21
+ # ---------- Modern Green Theme Styles ----------
22
+ st.markdown(
23
+ """
24
+ <style>
25
+ /* ... (all your existing CSS here) ... */
26
+ </style>
27
+ """,
28
+ unsafe_allow_html=True,
29
+ )
30
+
31
+ # ---------- Header ----------
32
+ colH1, colH2 = st.columns([0.8, 0.2])
33
+ with colH1:
34
+ try:
35
+ st.image("planmate-logo.png", width=250)
36
+ st.caption(APP_TAGLINE)
37
+ except:
38
+ st.title(APP_TITLE)
39
+ st.caption(APP_TAGLINE)
40
+
41
+ with colH2:
42
+ if st.button("πŸ”„ Start Over", use_container_width=True, help="Reset and start planning a new trip"):
43
+ for k in list(st.session_state.keys()):
44
+ del st.session_state[k]
45
+ st.rerun()
46
+
47
+ # ---------- Sidebar Inputs ----------
48
+ with st.sidebar:
49
+ st.markdown("### 🧳 Trip Planning")
50
+
51
+ src_city = st.text_input("πŸ›« From", value="Lahore", help="Enter your departure city")
52
+ dst_city = st.text_input("πŸ›¬ To", value="Dubai", help="Enter your destination city")
53
+
54
+ col1, col2 = st.columns(2)
55
+ start_date = col1.date_input("πŸ“… Start Date", value=date.today())
56
+ num_days = col2.number_input("πŸ“† Days", min_value=1, max_value=30, value=5, step=1)
57
+
58
+ col3, col4 = st.columns(2)
59
+ adults = col3.number_input("πŸ‘₯ Adults", min_value=1, max_value=9, value=1, step=1)
60
+ non_stop = col4.checkbox("✈️ Non-stop", value=False, help="Direct flights only")
61
+
62
+ with st.expander("βš™οΈ Advanced Options", expanded=False):
63
+ override_origin = st.text_input("Origin IATA Code", placeholder="e.g., LHE")
64
+ override_dest = st.text_input("Destination IATA Code", placeholder="e.g., DXB")
65
+
66
+ st.markdown("---")
67
+ go = st.button("πŸš€ Plan My Trip", type="primary", use_container_width=True)
68
+
69
+ # ---------- Helpers ----------
70
+ def set_planned(**kwargs):
71
+ st.session_state.planned = True
72
+ for k, v in kwargs.items():
73
+ st.session_state[k] = v
74
+
75
+ def not_planned():
76
+ return not st.session_state.get("planned", False)
77
+
78
+ def scroll_to(target_id: str):
79
+ st_html(
80
+ f"""
81
+ <script>
82
+ setTimeout(function() {{
83
+ const el = parent.document.getElementById("{target_id}");
84
+ if (el) el.scrollIntoView({{ behavior: "smooth", block: "start" }});
85
+ }}, 120);
86
+ </script>
87
+ """,
88
+ height=0,
89
+ )
90
+
91
+ if "show_flights" not in st.session_state:
92
+ st.session_state.show_flights = False
93
+
94
+ # ---------- First click: plan & fetch flights ----------
95
+ if go:
96
+ # Resolve countries
97
+ src_country = dst_country = None
98
+ try: src_country = geocode_city(src_city).get("country")
99
+ except: pass
100
+ try: dst_country = geocode_city(dst_city).get("country")
101
+ except: pass
102
+
103
+ # Resolve IATA
104
+ try:
105
+ if override_origin.strip():
106
+ origin_code, origin_label, origin_kind = override_origin.strip().upper(), f"(override) {override_origin.strip().upper()}", "OVERRIDE"
107
+ else:
108
+ code, name, kind = resolve_city_to_iata_ai(src_city, src_country)
109
+ origin_code, origin_label, origin_kind = code, f"{name} ({code})", kind
110
+
111
+ if override_dest.strip():
112
+ dest_code, dest_label, dest_kind = override_dest.strip().upper(), f"(override) {override_dest.strip().upper()}", "OVERRIDE"
113
+ else:
114
+ code, name, kind = resolve_city_to_iata_ai(dst_city, dst_country)
115
+ dest_code, dest_label, dest_kind = code, f"{name} ({code})", kind
116
+ except Exception as e:
117
+ st.markdown(f"<div class='warning-box'>City/IATA resolution failed: {str(e)}</div>", unsafe_allow_html=True)
118
+ st.stop()
119
+
120
+ ret_date = utils.compute_return_date(start_date, int(num_days))
121
+
122
+ try:
123
+ with st.spinner("πŸ” Searching for flights..."):
124
+ flights = search_flights(
125
+ origin_code, dest_code, utils.to_iso(start_date), utils.to_iso(ret_date),
126
+ int(adults), CURRENCY, non_stop=non_stop,
127
+ )
128
+ except Exception as e:
129
+ st.markdown(f"<div class='warning-box'>Flight search failed: {str(e)}</div>", unsafe_allow_html=True)
130
+ st.stop()
131
+
132
+ set_planned(
133
+ src_city=src_city, dst_city=dst_city, start_date=start_date, num_days=int(num_days),
134
+ adults=int(adults), non_stop=bool(non_stop),
135
+ origin_code=origin_code, origin_label=origin_label, origin_kind=origin_kind,
136
+ dest_code=dest_code, dest_label=dest_label, dest_kind=dest_kind,
137
+ ret_date=ret_date, flights=flights, flight_idx=None, show_flights=True,
138
+ weather=None, pois=None, selected_stay=None, itinerary_md=None, scroll_target=None,
139
+ )
140
+
141
+ # ---------- Show info if not planned ----------
142
+ if not_planned():
143
+ st.markdown("<div class='info-box'>Fill in your trip details in the sidebar and click <strong>Plan My Trip</strong>!</div>", unsafe_allow_html=True)
144
+ st.stop()
145
+
146
+ # ---------- Banner ----------
147
+ st.markdown(
148
+ f"<div class='success-banner'><strong>Routes Resolved:</strong> {st.session_state.src_city} β†’ {st.session_state.origin_label} [{st.session_state.origin_kind}] β€’ {st.session_state.dst_city} β†’ {st.session_state.dest_label} [{st.session_state.dest_kind}]</div>",
149
+ unsafe_allow_html=True
150
+ )
151
+
152
+ # ---------- Flights ---------- (existing code unchanged)
153
+ # ... same flight selection code as your current app ...
154
+
155
+ # ---------- Weather ---------- (existing code unchanged)
156
+ # ... same weather code as your current app ...
157
+
158
+ # ---------- Attractions & Stays (Geoapify updated) ----------
159
+ st.markdown('<hr class="section-divider">', unsafe_allow_html=True)
160
+ st.markdown('<div id="section-attractions"></div>', unsafe_allow_html=True)
161
+ st.subheader("πŸ“ Attractions & Accommodations")
162
+
163
+ if st.session_state.pois is None:
164
+ try:
165
+ with st.spinner("πŸ—ΊοΈ Discovering attractions and accommodations..."):
166
+ loc = geocode_city(st.session_state.dst_city)
167
+ st.session_state.pois = get_attractions_and_stays(lat=loc["lat"], lon=loc["lon"], radius=8000)
168
+ except Exception as e:
169
+ st.markdown(f"<div class='warning-box'>Attractions lookup failed: {str(e)}</div>", unsafe_allow_html=True)
170
+ st.session_state.pois = {"attractions": [], "stays": []}
171
+
172
+ pois = st.session_state.pois
173
+ attractions = pois.get("attractions", [])[:30]
174
+ stays = pois.get("stays", [])[:30]
175
+
176
+ # Select attractions
177
+ st.markdown("#### 🎯 **Top Attractions**")
178
+ selected_attractions = []
179
+ for i, a in enumerate(attractions[:12]):
180
+ col1, col2 = st.columns([0.85, 0.15])
181
+ with col1:
182
+ st.markdown(f"<div class='attraction-item'>πŸ“Œ <strong>{a['name']}</strong> <em>({a.get('kinds','')})</em></div>", unsafe_allow_html=True)
183
+ with col2:
184
+ add = st.checkbox("Add", key=f"attr-{i}", help=f"Include {a['name']} in your itinerary")
185
+ if add: selected_attractions.append(a)
186
+
187
+ if not selected_attractions:
188
+ st.markdown("<div class='info-box'>πŸ’‘ Select attractions for a personalized itinerary.</div>", unsafe_allow_html=True)
189
+
190
+ # Rank stays
191
+ st.markdown("#### 🏨 **Accommodations**")
192
+ st.caption("*Powered by Geoapify - for reference only*")
193
+ try:
194
+ ranked_stays = rank_accommodations(stays, prefs="central, well-reviewed, convenient")
195
+ except Exception:
196
+ ranked_stays = stays
197
+
198
+ stay_names = [s["name"] for s in ranked_stays[:15]]
199
+ chosen_stay_name = st.selectbox("Choose accommodation", options=["(None selected)"] + stay_names)
200
+ selected_stay = None if chosen_stay_name == "(None selected)" else next((s for s in ranked_stays if s["name"] == chosen_stay_name), None)
201
+ st.session_state.selected_stay = selected_stay
202
+
203
+ # ---------- Review & Booking ----------
204
+ # ... same booking simulation code as your current app ...
205
+
206
+ # ---------- Itinerary ----------
207
+ st.markdown('<hr class="section-divider">', unsafe_allow_html=True)
208
+ st.markdown('<div id="section-itinerary"></div>', unsafe_allow_html=True)
209
+ st.subheader("πŸ—“οΈ AI Itinerary")
210
+
211
+ if st.button("πŸ€– Generate Itinerary"):
212
+ with st.spinner("🧠 AI planning your perfect itinerary..."):
213
+ try:
214
+ st.session_state.itinerary_md = build_itinerary(
215
+ st.session_state.dst_city,
216
+ utils.to_iso(st.session_state.start_date),
217
+ int(st.session_state.num_days),
218
+ selected_attractions,
219
+ st.session_state.selected_stay,
220
+ st.session_state.weather.get("summary_text", ""),
221
+ )
222
+ st.session_state.scroll_target = "section-itinerary"
223
+ except Exception as e:
224
+ st.markdown(f"<div class='warning-box'>Itinerary generation failed: {str(e)}</div>", unsafe_allow_html=True)
225
+
226
+ if st.session_state.get("itinerary_md"):
227
+ st.markdown("<div class='modern-card'><h4>πŸ“‹ Your Personalized Itinerary</h4></div>", unsafe_allow_html=True)
228
+ st.markdown(st.session_state.itinerary_md)
229
+
230
+ # ---------- Scroll if needed ----------
231
+ if st.session_state.get("scroll_target"):
232
+ scroll_to(st.session_state["scroll_target"])
233
+
234
+ from planmate.weather import summarize_forecast_for_range, geocode_city
235
+ from planmate.attractions import get_attractions_and_stays
236
+ from planmate.llm import rank_accommodations, resolve_city_to_iata_ai
237
+ from planmate.itinerary import build_itinerary
238
+
239
+ st.set_page_config(page_title=APP_TITLE, page_icon="✈️", layout="wide")
240
+
241
  # ---------- Modern Green Theme Styles ----------
242
  st.markdown(
243
  """