flen-crypto commited on
Commit
03c3fed
·
verified ·
1 Parent(s): e2a5ab0

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +776 -0
app.py ADDED
@@ -0,0 +1,776 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import os
3
+ import sqlite3
4
+ from datetime import datetime
5
+ from PyQt6.QtWidgets import (
6
+ QApplication, QMainWindow, QWidget, QVBoxLayout, QPushButton, QLabel,
7
+ QStackedWidget, QListWidget, QTextEdit, QLineEdit, QHBoxLayout, QFileDialog,
8
+ QCalendarWidget, QMessageBox, QListWidgetItem, QFrame
9
+ )
10
+ from PyQt6.QtCore import Qt, QPropertyAnimation, QEasingCurve, QSize
11
+ from PyQt6.QtGui import QPixmap, QIcon, QFont, QColor
12
+ import folium
13
+ from geopy.geocoders import Nominatim
14
+ from geopy.exc import GeocoderUnavailable
15
+ import requests
16
+ from PIL import Image
17
+ import io
18
+ import base64
19
+ import openai
20
+ import configparser
21
+
22
+ # Configuration setup
23
+ config = configparser.ConfigParser()
24
+ config.read('config.ini')
25
+
26
+ # Initialize OpenAI API if key exists
27
+ if 'OPENAI' in config and 'api_key' in config['OPENAI']:
28
+ openai.api_key = config['OPENAI']['api_key']
29
+
30
+ class DatabaseManager:
31
+ def __init__(self, db_path='data/tt_travels.db'):
32
+ os.makedirs(os.path.dirname(db_path), exist_ok=True)
33
+ self.conn = sqlite3.connect(db_path)
34
+ self.create_tables()
35
+
36
+ def create_tables(self):
37
+ cursor = self.conn.cursor()
38
+ cursor.execute('''
39
+ CREATE TABLE IF NOT EXISTS trips (
40
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
41
+ title TEXT NOT NULL,
42
+ start_date TEXT,
43
+ end_date TEXT
44
+ )
45
+ ''')
46
+ cursor.execute('''
47
+ CREATE TABLE IF NOT EXISTS journal_entries (
48
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
49
+ trip_id INTEGER,
50
+ title TEXT NOT NULL,
51
+ content TEXT,
52
+ location TEXT,
53
+ timestamp TEXT,
54
+ image_path TEXT,
55
+ FOREIGN KEY (trip_id) REFERENCES trips(id)
56
+ )
57
+ ''')
58
+ cursor.execute('''
59
+ CREATE TABLE IF NOT EXISTS itinerary_items (
60
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
61
+ trip_id INTEGER,
62
+ day INTEGER,
63
+ title TEXT NOT NULL,
64
+ description TEXT,
65
+ time TEXT,
66
+ FOREIGN KEY (trip_id) REFERENCES trips(id)
67
+ )
68
+ ''')
69
+ self.conn.commit()
70
+
71
+ def add_trip(self, title, start_date=None, end_date=None):
72
+ cursor = self.conn.cursor()
73
+ cursor.execute('''
74
+ INSERT INTO trips (title, start_date, end_date)
75
+ VALUES (?, ?, ?)
76
+ ''', (title, start_date, end_date))
77
+ self.conn.commit()
78
+ return cursor.lastrowid
79
+
80
+ def add_journal_entry(self, trip_id, title, content, location=None, image_path=None):
81
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
82
+ cursor = self.conn.cursor()
83
+ cursor.execute('''
84
+ INSERT INTO journal_entries (trip_id, title, content, location, timestamp, image_path)
85
+ VALUES (?, ?, ?, ?, ?, ?)
86
+ ''', (trip_id, title, content, location, timestamp, image_path))
87
+ self.conn.commit()
88
+ return cursor.lastrowid
89
+
90
+ def add_itinerary_item(self, trip_id, day, title, description, time=None):
91
+ cursor = self.conn.cursor()
92
+ cursor.execute('''
93
+ INSERT INTO itinerary_items (trip_id, day, title, description, time)
94
+ VALUES (?, ?, ?, ?, ?)
95
+ ''', (trip_id, day, title, description, time))
96
+ self.conn.commit()
97
+ return cursor.lastrowid
98
+
99
+ def get_trips(self):
100
+ cursor = self.conn.cursor()
101
+ cursor.execute('SELECT id, title, start_date, end_date FROM trips')
102
+ return cursor.fetchall()
103
+
104
+ def get_journal_entries(self, trip_id):
105
+ cursor = self.conn.cursor()
106
+ cursor.execute('''
107
+ SELECT id, title, content, location, timestamp, image_path
108
+ FROM journal_entries
109
+ WHERE trip_id = ?
110
+ ''', (trip_id,))
111
+ return cursor.fetchall()
112
+
113
+ def get_itinerary(self, trip_id, day=None):
114
+ cursor = self.conn.cursor()
115
+ if day:
116
+ cursor.execute('''
117
+ SELECT id, title, description, time
118
+ FROM itinerary_items
119
+ WHERE trip_id = ? AND day = ?
120
+ ORDER BY time
121
+ ''', (trip_id, day))
122
+ else:
123
+ cursor.execute('''
124
+ SELECT day, id, title, description, time
125
+ FROM itinerary_items
126
+ WHERE trip_id = ?
127
+ ORDER BY day, time
128
+ ''', (trip_id,))
129
+ return cursor.fetchall()
130
+
131
+ def close(self):
132
+ self.conn.close()
133
+
134
+ class WelcomeWindow(QMainWindow):
135
+ def __init__(self):
136
+ super().__init__()
137
+ self.setWindowTitle("TT's Travels")
138
+ self.setFixedSize(800, 500)
139
+ self.setStyleSheet("""
140
+ background-color: #2c3e50;
141
+ color: #ecf0f1;
142
+ """)
143
+
144
+ central_widget = QWidget()
145
+ self.setCentralWidget(central_widget)
146
+ layout = QVBoxLayout(central_widget)
147
+ layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
148
+
149
+ # Logo and title
150
+ self.logo = QLabel()
151
+ self.logo.setAlignment(Qt.AlignmentFlag.AlignCenter)
152
+ self.logo.setPixmap(QPixmap("logo.png").scaled(200, 200, Qt.AspectRatioMode.KeepAspectRatio))
153
+ layout.addWidget(self.logo)
154
+
155
+ title = QLabel("TT's Travels")
156
+ title.setFont(QFont("Arial", 32, QFont.Weight.Bold))
157
+ title.setAlignment(Qt.AlignmentFlag.AlignCenter)
158
+ layout.addWidget(title)
159
+
160
+ subtitle = QLabel("Your Personal Travel Companion")
161
+ subtitle.setFont(QFont("Arial", 16))
162
+ subtitle.setAlignment(Qt.AlignmentFlag.AlignCenter)
163
+ layout.addWidget(subtitle)
164
+
165
+ # Start button
166
+ self.start_btn = QPushButton("Begin Journey")
167
+ self.start_btn.setFont(QFont("Arial", 14))
168
+ self.start_btn.setStyleSheet("""
169
+ QPushButton {
170
+ background-color: #3498db;
171
+ color: white;
172
+ border-radius: 15px;
173
+ padding: 15px 30px;
174
+ }
175
+ QPushButton:hover {
176
+ background-color: #2980b9;
177
+ }
178
+ """)
179
+ self.start_btn.setFixedSize(200, 50)
180
+ self.start_btn.clicked.connect(self.fade_out)
181
+ layout.addWidget(self.start_btn, alignment=Qt.AlignmentFlag.AlignCenter)
182
+
183
+ # Fade-in animation
184
+ self.opacity_effect = self.setGraphicsEffect(None)
185
+ self.setWindowOpacity(0)
186
+ self.fade_in_animation = QPropertyAnimation(self, b"windowOpacity")
187
+ self.fade_in_animation.setDuration(1500)
188
+ self.fade_in_animation.setStartValue(0)
189
+ self.fade_in_animation.setEndValue(1)
190
+ self.fade_in_animation.setEasingCurve(QEasingCurve.Type.InOutQuad)
191
+ self.fade_in_animation.start()
192
+
193
+ def fade_out(self):
194
+ self.fade_out_animation = QPropertyAnimation(self, b"windowOpacity")
195
+ self.fade_out_animation.setDuration(1000)
196
+ self.fade_out_animation.setStartValue(1)
197
+ self.fade_out_animation.setEndValue(0)
198
+ self.fade_out_animation.setEasingCurve(QEasingCurve.Type.InOutQuad)
199
+ self.fade_out_animation.finished.connect(self.open_main_app)
200
+ self.fade_out_animation.start()
201
+
202
+ def open_main_app(self):
203
+ self.main_app = MainAppWindow()
204
+ self.main_app.show()
205
+ self.close()
206
+
207
+ class MainAppWindow(QMainWindow):
208
+ def __init__(self):
209
+ super().__init__()
210
+ self.db = DatabaseManager()
211
+ self.setWindowTitle("TT's Travels")
212
+ self.setMinimumSize(1000, 700)
213
+
214
+ # Central stacked widget for modules
215
+ self.central_stack = QStackedWidget()
216
+ self.setCentralWidget(self.central_stack)
217
+
218
+ # Setup modules
219
+ self.setup_ui()
220
+ self.load_trips()
221
+
222
+ def setup_ui(self):
223
+ # Main container widget
224
+ main_widget = QWidget()
225
+ main_layout = QHBoxLayout(main_widget)
226
+
227
+ # Sidebar
228
+ sidebar = QFrame()
229
+ sidebar.setStyleSheet("background-color: #34495e;")
230
+ sidebar.setFixedWidth(200)
231
+ sidebar_layout = QVBoxLayout(sidebar)
232
+ sidebar_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
233
+
234
+ # Sidebar buttons
235
+ modules = [
236
+ ("Journal", self.show_journal),
237
+ ("Trip Builder", self.show_trip_builder),
238
+ ("Maps", self.show_maps),
239
+ ("Weather & AI", self.show_ai),
240
+ ("Calendar", self.show_calendar),
241
+ ("Export", self.show_export)
242
+ ]
243
+
244
+ for name, callback in modules:
245
+ btn = QPushButton(name)
246
+ btn.setStyleSheet("""
247
+ QPushButton {
248
+ background-color: #2c3e50;
249
+ color: white;
250
+ padding: 10px;
251
+ text-align: left;
252
+ border: none;
253
+ }
254
+ QPushButton:hover {
255
+ background-color: #3498db;
256
+ }
257
+ """)
258
+ btn.setFont(QFont("Arial", 12))
259
+ btn.clicked.connect(callback)
260
+ sidebar_layout.addWidget(btn)
261
+
262
+ sidebar_layout.addStretch()
263
+ main_layout.addWidget(sidebar)
264
+
265
+ # Main content area
266
+ self.content_stack = QStackedWidget()
267
+ main_layout.addWidget(self.content_stack, 1)
268
+
269
+ # Add modules to content stack
270
+ self.journal_widget = JournalWidget(self.db)
271
+ self.trip_builder_widget = TripBuilderWidget(self.db)
272
+ self.maps_widget = MapsWidget()
273
+ self.ai_widget = AIWidget()
274
+ self.calendar_widget = CalendarWidget()
275
+ self.export_widget = ExportWidget()
276
+
277
+ self.content_stack.addWidget(self.journal_widget)
278
+ self.content_stack.addWidget(self.trip_builder_widget)
279
+ self.content_stack.addWidget(self.maps_widget)
280
+ self.content_stack.addWidget(self.ai_widget)
281
+ self.content_stack.addWidget(self.calendar_widget)
282
+ self.content_stack.addWidget(self.export_widget)
283
+
284
+ self.central_stack.addWidget(main_widget)
285
+
286
+ def load_trips(self):
287
+ trips = self.db.get_trips()
288
+ self.journal_widget.load_trips(trips)
289
+ self.trip_builder_widget.load_trips(trips)
290
+
291
+ def show_journal(self):
292
+ self.content_stack.setCurrentIndex(0)
293
+
294
+ def show_trip_builder(self):
295
+ self.content_stack.setCurrentIndex(1)
296
+
297
+ def show_maps(self):
298
+ self.content_stack.setCurrentIndex(2)
299
+
300
+ def show_ai(self):
301
+ self.content_stack.setCurrentIndex(3)
302
+
303
+ def show_calendar(self):
304
+ self.content_stack.setCurrentIndex(4)
305
+
306
+ def show_export(self):
307
+ self.content_stack.setCurrentIndex(5)
308
+
309
+ class JournalWidget(QWidget):
310
+ def __init__(self, db):
311
+ super().__init__()
312
+ self.db = db
313
+ self.current_trip_id = None
314
+ self.setup_ui()
315
+
316
+ def setup_ui(self):
317
+ layout = QHBoxLayout(self)
318
+
319
+ # Left panel - Trip selection
320
+ left_panel = QWidget()
321
+ left_panel.setFixedWidth(250)
322
+ left_layout = QVBoxLayout(left_panel)
323
+
324
+ self.trip_list = QListWidget()
325
+ self.trip_list.itemClicked.connect(self.load_journal_entries)
326
+ left_layout.addWidget(QLabel("Your Trips:"))
327
+ left_layout.addWidget(self.trip_list)
328
+
329
+ new_trip_btn = QPushButton("New Trip")
330
+ new_trip_btn.clicked.connect(self.create_new_trip)
331
+ left_layout.addWidget(new_trip_btn)
332
+
333
+ # Right panel - Journal entries
334
+ right_panel = QWidget()
335
+ right_layout = QVBoxLayout(right_panel)
336
+
337
+ self.entry_list = QListWidget()
338
+ self.entry_list.itemClicked.connect(self.display_entry)
339
+ right_layout.addWidget(QLabel("Journal Entries:"))
340
+ right_layout.addWidget(self.entry_list)
341
+
342
+ # Entry display area
343
+ self.entry_display = QTextEdit()
344
+ self.entry_display.setReadOnly(True)
345
+ right_layout.addWidget(self.entry_display)
346
+
347
+ # New entry form
348
+ entry_form = QWidget()
349
+ form_layout = QVBoxLayout(entry_form)
350
+
351
+ self.entry_title = QLineEdit()
352
+ self.entry_title.setPlaceholderText("Title")
353
+ form_layout.addWidget(self.entry_title)
354
+
355
+ self.entry_content = QTextEdit()
356
+ self.entry_content.setPlaceholderText("Write your journal entry...")
357
+ form_layout.addWidget(self.entry_content)
358
+
359
+ self.location_entry = QLineEdit()
360
+ self.location_entry.setPlaceholderText("Location (optional)")
361
+ form_layout.addWidget(self.location_entry)
362
+
363
+ img_btn = QPushButton("Attach Image")
364
+ img_btn.clicked.connect(self.attach_image)
365
+ form_layout.addWidget(img_btn)
366
+
367
+ save_btn = QPushButton("Save Entry")
368
+ save_btn.clicked.connect(self.save_entry)
369
+ form_layout.addWidget(save_btn)
370
+
371
+ right_layout.addWidget(entry_form)
372
+
373
+ layout.addWidget(left_panel)
374
+ layout.addWidget(right_panel)
375
+
376
+ def load_trips(self, trips):
377
+ self.trip_list.clear()
378
+ for trip_id, title, start_date, end_date in trips:
379
+ item = QListWidgetItem(title)
380
+ item.setData(Qt.ItemDataRole.UserRole, trip_id)
381
+ self.trip_list.addItem(item)
382
+
383
+ def create_new_trip(self):
384
+ title, ok = QInputDialog.getText(self, "New Trip", "Enter trip name:")
385
+ if ok and title:
386
+ trip_id = self.db.add_trip(title)
387
+ self.load_trips(self.db.get_trips())
388
+
389
+ def load_journal_entries(self, item):
390
+ self.current_trip_id = item.data(Qt.ItemDataRole.UserRole)
391
+ entries = self.db.get_journal_entries(self.current_trip_id)
392
+ self.entry_list.clear()
393
+ for entry_id, title, content, location, timestamp, image_path in entries:
394
+ self.entry_list.addItem(f"{timestamp} - {title}")
395
+
396
+ def display_entry(self, item):
397
+ index = self.entry_list.row(item)
398
+ entries = self.db.get_journal_entries(self.current_trip_id)
399
+ if index < len(entries):
400
+ entry = entries[index]
401
+ display_text = f"<h2>{entry[1]}</h2>"
402
+ display_text += f"<p><i>{entry[4]}</i></p>"
403
+ if entry[3]:
404
+ display_text += f"<p>Location: {entry[3]}</p>"
405
+ display_text += f"<p>{entry[2]}</p>"
406
+ if entry[5]:
407
+ display_text += f'<img src="{entry[5]}" width="300">'
408
+ self.entry_display.setHtml(display_text)
409
+
410
+ def attach_image(self):
411
+ file_path, _ = QFileDialog.getOpenFileName(
412
+ self, "Select Image", "", "Images (*.png *.jpg *.jpeg)"
413
+ )
414
+ if file_path:
415
+ self.image_path = file_path
416
+
417
+ def save_entry(self):
418
+ if not self.current_trip_id:
419
+ QMessageBox.warning(self, "No Trip Selected", "Please select a trip first.")
420
+ return
421
+
422
+ title = self.entry_title.text()
423
+ content = self.entry_content.toPlainText()
424
+ location = self.location_entry.text()
425
+
426
+ if not title or not content:
427
+ QMessageBox.warning(self, "Missing Information", "Title and content are required.")
428
+ return
429
+
430
+ image_path = getattr(self, 'image_path', None)
431
+ self.db.add_journal_entry(
432
+ self.current_trip_id, title, content, location, image_path
433
+ )
434
+
435
+ # Reset form
436
+ self.entry_title.clear()
437
+ self.entry_content.clear()
438
+ self.location_entry.clear()
439
+ if hasattr(self, 'image_path'):
440
+ del self.image_path
441
+
442
+ # Reload entries
443
+ self.load_journal_entries(self.trip_list.currentItem())
444
+ QMessageBox.information(self, "Success", "Journal entry saved!")
445
+
446
+ class TripBuilderWidget(QWidget):
447
+ def __init__(self, db):
448
+ super().__init__()
449
+ self.db = db
450
+ self.current_trip_id = None
451
+ self.setup_ui()
452
+
453
+ def setup_ui(self):
454
+ layout = QHBoxLayout(self)
455
+
456
+ # Left panel - Trip selection
457
+ left_panel = QWidget()
458
+ left_panel.setFixedWidth(250)
459
+ left_layout = QVBoxLayout(left_panel)
460
+
461
+ self.trip_list = QListWidget()
462
+ self.trip_list.itemClicked.connect(self.load_itinerary)
463
+ left_layout.addWidget(QLabel("Your Trips:"))
464
+ left_layout.addWidget(self.trip_list)
465
+
466
+ new_trip_btn = QPushButton("New Trip")
467
+ new_trip_btn.clicked.connect(self.create_new_trip)
468
+ left_layout.addWidget(new_trip_btn)
469
+
470
+ # Right panel - Itinerary builder
471
+ right_panel = QWidget()
472
+ right_layout = QVBoxLayout(right_panel)
473
+
474
+ self.day_selector = QListWidget()
475
+ self.day_selector.setMaximumHeight(100)
476
+ self.day_selector.itemClicked.connect(self.load_day_items)
477
+ right_layout.addWidget(QLabel("Days:"))
478
+ right_layout.addWidget(self.day_selector)
479
+
480
+ self.item_list = QListWidget()
481
+ right_layout.addWidget(QLabel("Itinerary Items:"))
482
+ right_layout.addWidget(self.item_list)
483
+
484
+ # Item form
485
+ item_form = QWidget()
486
+ form_layout = QVBoxLayout(item_form)
487
+
488
+ self.item_title = QLineEdit()
489
+ self.item_title.setPlaceholderText("Activity Title")
490
+ form_layout.addWidget(self.item_title)
491
+
492
+ self.item_desc = QTextEdit()
493
+ self.item_desc.setPlaceholderText("Description")
494
+ form_layout.addWidget(self.item_desc)
495
+
496
+ self.item_time = QLineEdit()
497
+ self.item_time.setPlaceholderText("Time (e.g., 10:00 AM)")
498
+ form_layout.addWidget(self.item_time)
499
+
500
+ add_btn = QPushButton("Add Item")
501
+ add_btn.clicked.connect(self.add_itinerary_item)
502
+ form_layout.addWidget(add_btn)
503
+
504
+ right_layout.addWidget(item_form)
505
+
506
+ layout.addWidget(left_panel)
507
+ layout.addWidget(right_panel)
508
+
509
+ def load_trips(self, trips):
510
+ self.trip_list.clear()
511
+ for trip_id, title, start_date, end_date in trips:
512
+ item = QListWidgetItem(title)
513
+ item.setData(Qt.ItemDataRole.UserRole, trip_id)
514
+ self.trip_list.addItem(item)
515
+
516
+ def create_new_trip(self):
517
+ title, ok = QInputDialog.getText(self, "New Trip", "Enter trip name:")
518
+ if ok and title:
519
+ trip_id = self.db.add_trip(title)
520
+ self.load_trips(self.db.get_trips())
521
+
522
+ def load_itinerary(self, item):
523
+ self.current_trip_id = item.data(Qt.ItemDataRole.UserRole)
524
+ self.day_selector.clear()
525
+
526
+ # Add days 1-10 for simplicity
527
+ for day in range(1, 11):
528
+ self.day_selector.addItem(f"Day {day}")
529
+
530
+ self.current_day = 1
531
+ self.load_day_items()
532
+
533
+ def load_day_items(self, day_item=None):
534
+ if day_item:
535
+ self.current_day = self.day_selector.row(day_item) + 1
536
+
537
+ items = self.db.get_itinerary(self.current_trip_id, self.current_day)
538
+ self.item_list.clear()
539
+ for item_id, title, description, time in items:
540
+ display_text = f"{time}: {title}" if time else title
541
+ self.item_list.addItem(display_text)
542
+
543
+ def add_itinerary_item(self):
544
+ if not self.current_trip_id:
545
+ QMessageBox.warning(self, "No Trip Selected", "Please select a trip first.")
546
+ return
547
+
548
+ title = self.item_title.text()
549
+ description = self.item_desc.toPlainText()
550
+ time = self.item_time.text()
551
+
552
+ if not title:
553
+ QMessageBox.warning(self, "Missing Title", "Activity title is required.")
554
+ return
555
+
556
+ self.db.add_itinerary_item(
557
+ self.current_trip_id, self.current_day, title, description, time
558
+ )
559
+
560
+ # Reset form
561
+ self.item_title.clear()
562
+ self.item_desc.clear()
563
+ self.item_time.clear()
564
+
565
+ # Reload items
566
+ self.load_day_items()
567
+ QMessageBox.information(self, "Success", "Itinerary item added!")
568
+
569
+ class MapsWidget(QWidget):
570
+ def __init__(self):
571
+ super().__init__()
572
+ self.setup_ui()
573
+
574
+ def setup_ui(self):
575
+ layout = QVBoxLayout(self)
576
+ layout.addWidget(QLabel("Map View (Folium Integration)"))
577
+
578
+ # Placeholder for folium map
579
+ map_label = QLabel("Interactive map will be displayed here")
580
+ map_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
581
+ map_label.setStyleSheet("background-color: #e0e0e0; padding: 50px;")
582
+ layout.addWidget(map_label)
583
+
584
+ # Location search
585
+ search_layout = QHBoxLayout()
586
+ self.location_input = QLineEdit()
587
+ self.location_input.setPlaceholderText("Enter location")
588
+ search_layout.addWidget(self.location_input)
589
+
590
+ search_btn = QPushButton("Search")
591
+ search_btn.clicked.connect(self.search_location)
592
+ search_layout.addWidget(search_btn)
593
+
594
+ layout.addLayout(search_layout)
595
+
596
+ def search_location(self):
597
+ location = self.location_input.text()
598
+ if location:
599
+ try:
600
+ geolocator = Nominatim(user_agent="tt_travels")
601
+ location_data = geolocator.geocode(location)
602
+ if location_data:
603
+ # Create folium map (in a real app, this would be embedded)
604
+ m = folium.Map(location=[location_data.latitude, location_data.longitude], zoom_start=13)
605
+ folium.Marker(
606
+ [location_data.latitude, location_data.longitude],
607
+ popup=location
608
+ ).add_to(m)
609
+
610
+ # Save map to HTML and display
611
+ map_path = "data/map.html"
612
+ m.save(map_path)
613
+ QMessageBox.information(self, "Map Generated", f"Map saved to {map_path}")
614
+ else:
615
+ QMessageBox.warning(self, "Location Not Found", "Could not find the specified location.")
616
+ except GeocoderUnavailable:
617
+ QMessageBox.critical(self, "Geocoder Error", "Geocoding service unavailable.")
618
+
619
+ class AIWidget(QWidget):
620
+ def __init__(self):
621
+ super().__init__()
622
+ self.setup_ui()
623
+
624
+ def setup_ui(self):
625
+ layout = QVBoxLayout(self)
626
+ layout.addWidget(QLabel("AI Travel Assistant"))
627
+
628
+ # Weather section
629
+ weather_layout = QHBoxLayout()
630
+ self.weather_location = QLineEdit()
631
+ self.weather_location.setPlaceholderText("Enter location for weather")
632
+ weather_layout.addWidget(self.weather_location)
633
+
634
+ weather_btn = QPushButton("Get Weather")
635
+ weather_btn.clicked.connect(self.get_weather)
636
+ weather_layout.addWidget(weather_btn)
637
+
638
+ self.weather_display = QLabel()
639
+ self.weather_display.setWordWrap(True)
640
+ layout.addLayout(weather_layout)
641
+ layout.addWidget(self.weather_display)
642
+
643
+ # AI suggestions
644
+ ai_layout = QVBoxLayout()
645
+ ai_layout.addWidget(QLabel("AI Suggestions:"))
646
+
647
+ self.ai_prompt = QTextEdit()
648
+ self.ai_prompt.setPlaceholderText("Ask for travel suggestions...")
649
+ ai_layout.addWidget(self.ai_prompt)
650
+
651
+ ai_btn = QPushButton("Get AI Suggestions")
652
+ ai_btn.clicked.connect(self.get_ai_suggestions)
653
+ ai_layout.addWidget(ai_btn)
654
+
655
+ self.ai_response = QTextEdit()
656
+ self.ai_response.setReadOnly(True)
657
+ ai_layout.addWidget(self.ai_response)
658
+
659
+ layout.addLayout(ai_layout)
660
+
661
+ def get_weather(self):
662
+ location = self.weather_location.text()
663
+ if location:
664
+ try:
665
+ # Using Open-Meteo API for example
666
+ geolocator = Nominatim(user_agent="tt_travels")
667
+ loc = geolocator.geocode(location)
668
+ if loc:
669
+ url = f"https://api.open-meteo.com/v1/forecast?latitude={loc.latitude}&longitude={loc.longitude}&current_weather=true"
670
+ response = requests.get(url)
671
+ if response.status_code == 200:
672
+ data = response.json()
673
+ current = data.get('current_weather', {})
674
+ temp = current.get('temperature', 'N/A')
675
+ windspeed = current.get('windspeed', 'N/A')
676
+ weather_code = current.get('weathercode', 'N/A')
677
+ self.weather_display.setText(
678
+ f"Current weather in {location}: {temp}°C, Wind: {windspeed} km/h"
679
+ )
680
+ else:
681
+ self.weather_display.setText("Weather data unavailable.")
682
+ else:
683
+ self.weather_display.setText("Location not found.")
684
+ except Exception as e:
685
+ self.weather_display.setText(f"Error: {str(e)}")
686
+
687
+ def get_ai_suggestions(self):
688
+ if not openai.api_key:
689
+ self.ai_response.setText("OpenAI API key not configured.")
690
+ return
691
+
692
+ prompt = self.ai_prompt.toPlainText()
693
+ if not prompt:
694
+ self.ai_response.setText("Please enter a prompt.")
695
+ return
696
+
697
+ try:
698
+ response = openai.ChatCompletion.create(
699
+ model="gpt-3.5-turbo",
700
+ messages=[{"role": "user", "content": prompt}],
701
+ max_tokens=500
702
+ )
703
+ self.ai_response.setText(response.choices[0].message['content'].strip())
704
+ except Exception as e:
705
+ self.ai_response.setText(f"Error: {str(e)}")
706
+
707
+ class CalendarWidget(QWidget):
708
+ def __init__(self):
709
+ super().__init__()
710
+ self.setup_ui()
711
+
712
+ def setup_ui(self):
713
+ layout = QVBoxLayout(self)
714
+ layout.addWidget(QLabel("Travel Calendar"))
715
+
716
+ self.calendar = QCalendarWidget()
717
+ layout.addWidget(self.calendar)
718
+
719
+ # Event list
720
+ self.event_list = QListWidget()
721
+ layout.addWidget(self.event_list)
722
+
723
+ # Import button
724
+ import_btn = QPushButton("Import ICS File")
725
+ import_btn.clicked.connect(self.import_ics)
726
+ layout.addWidget(import_btn)
727
+
728
+ def import_ics(self):
729
+ file_path, _ = QFileDialog.getOpenFileName(
730
+ self, "Import Calendar", "", "ICS Files (*.ics)"
731
+ )
732
+ if file_path:
733
+ # Placeholder for ICS parsing
734
+ self.event_list.addItem(f"Imported: {os.path.basename(file_path)}")
735
+ QMessageBox.information(self, "Import Successful", "Calendar events imported!")
736
+
737
+ class ExportWidget(QWidget):
738
+ def __init__(self):
739
+ super().__init__()
740
+ self.setup_ui()
741
+
742
+ def setup_ui(self):
743
+ layout = QVBoxLayout(self)
744
+ layout.addWidget(QLabel("Export Data"))
745
+
746
+ # Export options
747
+ options = [
748
+ ("Export Journal as PDF", self.export_journal_pdf),
749
+ ("Export Trip as CSV", self.export_trip_csv),
750
+ ("Export All Data", self.export_all)
751
+ ]
752
+
753
+ for text, callback in options:
754
+ btn = QPushButton(text)
755
+ btn.clicked.connect(callback)
756
+ layout.addWidget(btn)
757
+
758
+ def export_journal_pdf(self):
759
+ QMessageBox.information(self, "Export", "Journal PDF export functionality would be implemented here.")
760
+
761
+ def export_trip_csv(self):
762
+ QMessageBox.information(self, "Export", "Trip CSV export functionality would be implemented here.")
763
+
764
+ def export_all(self):
765
+ QMessageBox.information(self, "Export", "Full data export functionality would be implemented here.")
766
+
767
+ if __name__ == "__main__":
768
+ app = QApplication(sys.argv)
769
+
770
+ # Set application style
771
+ app.setStyle("Fusion")
772
+
773
+ welcome = WelcomeWindow()
774
+ welcome.show()
775
+
776
+ sys.exit(app.exec())