SamiKoen Claude commited on
Commit
ab4139b
·
1 Parent(s): 827a20a

Add conversation history tracking and HTML export

Browse files

- Created conversation_tracker.py module for tracking chat history
- Saves last 100 conversations to JSON file
- Beautiful mobile-responsive HTML export with search functionality
- Added export button to Gradio interface
- Similar to WhatsApp version but integrated with Gradio

Features:
- Real-time conversation saving
- HTML export with statistics (total messages, today's count)
- Search functionality in exported HTML
- Product highlighting (MADONE, MARLIN, etc)
- Mobile responsive design
- Date grouping and timestamps

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>

__pycache__/conversation_tracker.cpython-312.pyc ADDED
Binary file (15.1 kB). View file
 
app.py CHANGED
@@ -29,6 +29,9 @@ from enhanced_features import (
29
  )
30
  from image_renderer import extract_product_info_for_gallery, format_message_with_images
31
 
 
 
 
32
  # Import smart warehouse with GPT intelligence and price
33
  try:
34
  from smart_warehouse_with_price import get_warehouse_stock_smart_with_price
@@ -1075,7 +1078,11 @@ with gr.Blocks(css=custom_css, theme="soft", title="Trek Asistanı", head=storag
1075
  elem_id="msg-input"
1076
  )
1077
 
1078
- # Restore button tamamen kaldırıldı
 
 
 
 
1079
 
1080
  # Session ve profil sistemi tamamen kaldırıldı
1081
 
@@ -1111,7 +1118,11 @@ with gr.Blocks(css=custom_css, theme="soft", title="Trek Asistanı", head=storag
1111
  chat_history[-1] = (message, response)
1112
  yield "", chat_history
1113
 
1114
- # Chat kaydetme sistemi kaldırıldı
 
 
 
 
1115
 
1116
  except Exception as e:
1117
  error_msg = f"Üzgünüm, bir hata oluştu: {str(e)}"
@@ -1121,6 +1132,16 @@ with gr.Blocks(css=custom_css, theme="soft", title="Trek Asistanı", head=storag
1121
  yield "", chat_history
1122
 
1123
  msg.submit(respond, [msg, chatbot], [msg, chatbot], show_progress=True)
 
 
 
 
 
 
 
 
 
 
1124
 
1125
  if __name__ == "__main__":
1126
  demo.launch(debug=True)
 
29
  )
30
  from image_renderer import extract_product_info_for_gallery, format_message_with_images
31
 
32
+ # Import conversation tracker
33
+ from conversation_tracker import add_conversation, export_html_file
34
+
35
  # Import smart warehouse with GPT intelligence and price
36
  try:
37
  from smart_warehouse_with_price import get_warehouse_stock_smart_with_price
 
1078
  elem_id="msg-input"
1079
  )
1080
 
1081
+ # Export button for conversation history
1082
+ with gr.Row():
1083
+ export_btn = gr.Button("📥 Konuşma Geçmişini İndir", variant="secondary")
1084
+ download_file = gr.File(label="İndir", visible=False)
1085
+ export_status = gr.Markdown("")
1086
 
1087
  # Session ve profil sistemi tamamen kaldırıldı
1088
 
 
1118
  chat_history[-1] = (message, response)
1119
  yield "", chat_history
1120
 
1121
+ # Konuşmayı kaydet
1122
+ try:
1123
+ add_conversation(message, response)
1124
+ except Exception as e:
1125
+ print(f"Error saving conversation: {e}")
1126
 
1127
  except Exception as e:
1128
  error_msg = f"Üzgünüm, bir hata oluştu: {str(e)}"
 
1132
  yield "", chat_history
1133
 
1134
  msg.submit(respond, [msg, chatbot], [msg, chatbot], show_progress=True)
1135
+
1136
+ def export_conversations():
1137
+ """Export conversation history to HTML"""
1138
+ try:
1139
+ filename = export_html_file()
1140
+ return gr.update(visible=True, value=filename), f"✅ Konuşma geçmişi hazır!"
1141
+ except Exception as e:
1142
+ return gr.update(visible=False), f"❌ Export hatası: {str(e)}"
1143
+
1144
+ export_btn.click(export_conversations, outputs=[download_file, export_status])
1145
 
1146
  if __name__ == "__main__":
1147
  demo.launch(debug=True)
conversation_tracker.py ADDED
@@ -0,0 +1,452 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Conversation tracking and HTML export for BF chatbot"""
2
+
3
+ import json
4
+ import os
5
+ from datetime import datetime
6
+ from typing import List, Dict, Any
7
+ import html
8
+
9
+ # Konuşma geçmişini saklamak için
10
+ CONVERSATIONS_FILE = "conversations.json"
11
+ MAX_CONVERSATIONS = 100 # Son 100 konuşmayı sakla
12
+
13
+ def load_conversations():
14
+ """Load conversation history from file"""
15
+ if os.path.exists(CONVERSATIONS_FILE):
16
+ try:
17
+ with open(CONVERSATIONS_FILE, 'r', encoding='utf-8') as f:
18
+ return json.load(f)
19
+ except:
20
+ return []
21
+ return []
22
+
23
+ def save_conversations(conversations):
24
+ """Save conversations to file"""
25
+ try:
26
+ # Keep only last MAX_CONVERSATIONS
27
+ if len(conversations) > MAX_CONVERSATIONS:
28
+ conversations = conversations[-MAX_CONVERSATIONS:]
29
+
30
+ with open(CONVERSATIONS_FILE, 'w', encoding='utf-8') as f:
31
+ json.dump(conversations, f, ensure_ascii=False, indent=2)
32
+ except Exception as e:
33
+ print(f"Error saving conversations: {e}")
34
+
35
+ def add_conversation(user_message, bot_response):
36
+ """Add a new conversation to history"""
37
+ conversations = load_conversations()
38
+
39
+ conversation = {
40
+ "timestamp": datetime.now().isoformat(),
41
+ "user": user_message,
42
+ "bot": bot_response
43
+ }
44
+
45
+ conversations.append(conversation)
46
+ save_conversations(conversations)
47
+
48
+ return conversation
49
+
50
+ def generate_html_report():
51
+ """Generate HTML report of all conversations"""
52
+ conversations = load_conversations()
53
+
54
+ html_template = """<!DOCTYPE html>
55
+ <html lang="tr">
56
+ <head>
57
+ <meta charset="UTF-8">
58
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
59
+ <title>Trek BF - Konuşma Geçmişi</title>
60
+ <style>
61
+ * {
62
+ margin: 0;
63
+ padding: 0;
64
+ box-sizing: border-box;
65
+ }
66
+
67
+ body {
68
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
69
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
70
+ min-height: 100vh;
71
+ padding: 10px;
72
+ }
73
+
74
+ .container {
75
+ max-width: 800px;
76
+ margin: 0 auto;
77
+ background: white;
78
+ border-radius: 20px;
79
+ box-shadow: 0 20px 60px rgba(0,0,0,0.3);
80
+ overflow: hidden;
81
+ }
82
+
83
+ .header {
84
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
85
+ color: white;
86
+ padding: 20px;
87
+ text-align: center;
88
+ }
89
+
90
+ .header h1 {
91
+ font-size: 24px;
92
+ margin-bottom: 5px;
93
+ }
94
+
95
+ .header .subtitle {
96
+ font-size: 14px;
97
+ opacity: 0.9;
98
+ }
99
+
100
+ .stats {
101
+ display: flex;
102
+ justify-content: space-around;
103
+ margin-top: 15px;
104
+ padding-top: 15px;
105
+ border-top: 1px solid rgba(255,255,255,0.3);
106
+ }
107
+
108
+ .stat-item {
109
+ text-align: center;
110
+ }
111
+
112
+ .stat-value {
113
+ font-size: 24px;
114
+ font-weight: bold;
115
+ }
116
+
117
+ .stat-label {
118
+ font-size: 12px;
119
+ opacity: 0.8;
120
+ }
121
+
122
+ .chat-container {
123
+ padding: 20px;
124
+ max-height: 70vh;
125
+ overflow-y: auto;
126
+ }
127
+
128
+ .chat-message {
129
+ margin-bottom: 20px;
130
+ animation: fadeIn 0.5s ease-in;
131
+ }
132
+
133
+ @keyframes fadeIn {
134
+ from { opacity: 0; transform: translateY(10px); }
135
+ to { opacity: 1; transform: translateY(0); }
136
+ }
137
+
138
+ .message-wrapper {
139
+ display: flex;
140
+ align-items: flex-start;
141
+ margin-bottom: 10px;
142
+ }
143
+
144
+ .message-wrapper.user {
145
+ justify-content: flex-end;
146
+ }
147
+
148
+ .message-bubble {
149
+ max-width: 70%;
150
+ padding: 12px 16px;
151
+ border-radius: 18px;
152
+ word-wrap: break-word;
153
+ position: relative;
154
+ }
155
+
156
+ .user .message-bubble {
157
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
158
+ color: white;
159
+ border-bottom-right-radius: 4px;
160
+ }
161
+
162
+ .bot .message-bubble {
163
+ background: #f1f3f5;
164
+ color: #333;
165
+ border-bottom-left-radius: 4px;
166
+ }
167
+
168
+ .timestamp {
169
+ font-size: 11px;
170
+ color: #999;
171
+ margin-top: 5px;
172
+ text-align: center;
173
+ }
174
+
175
+ .divider {
176
+ text-align: center;
177
+ margin: 20px 0;
178
+ position: relative;
179
+ }
180
+
181
+ .divider::before {
182
+ content: '';
183
+ position: absolute;
184
+ left: 0;
185
+ top: 50%;
186
+ width: 100%;
187
+ height: 1px;
188
+ background: #e0e0e0;
189
+ }
190
+
191
+ .divider-text {
192
+ background: white;
193
+ padding: 0 10px;
194
+ position: relative;
195
+ color: #999;
196
+ font-size: 12px;
197
+ }
198
+
199
+ .filter-container {
200
+ padding: 15px 20px;
201
+ background: #f8f9fa;
202
+ border-bottom: 1px solid #e0e0e0;
203
+ }
204
+
205
+ .search-box {
206
+ width: 100%;
207
+ padding: 10px 15px;
208
+ border: 1px solid #ddd;
209
+ border-radius: 25px;
210
+ font-size: 14px;
211
+ outline: none;
212
+ }
213
+
214
+ .search-box:focus {
215
+ border-color: #667eea;
216
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
217
+ }
218
+
219
+ .no-results {
220
+ text-align: center;
221
+ padding: 40px;
222
+ color: #999;
223
+ }
224
+
225
+ /* Product highlights */
226
+ .product-mention {
227
+ background: linear-gradient(135deg, #ffd89b 0%, #19547b 100%);
228
+ -webkit-background-clip: text;
229
+ -webkit-text-fill-color: transparent;
230
+ font-weight: bold;
231
+ }
232
+
233
+ /* Mobile responsive */
234
+ @media (max-width: 600px) {
235
+ .container {
236
+ border-radius: 0;
237
+ height: 100vh;
238
+ max-width: 100%;
239
+ }
240
+
241
+ .message-bubble {
242
+ max-width: 85%;
243
+ }
244
+
245
+ .chat-container {
246
+ max-height: calc(100vh - 200px);
247
+ }
248
+ }
249
+
250
+ /* Scrollbar styling */
251
+ .chat-container::-webkit-scrollbar {
252
+ width: 6px;
253
+ }
254
+
255
+ .chat-container::-webkit-scrollbar-track {
256
+ background: #f1f1f1;
257
+ }
258
+
259
+ .chat-container::-webkit-scrollbar-thumb {
260
+ background: #888;
261
+ border-radius: 3px;
262
+ }
263
+
264
+ .chat-container::-webkit-scrollbar-thumb:hover {
265
+ background: #555;
266
+ }
267
+ </style>
268
+ </head>
269
+ <body>
270
+ <div class="container">
271
+ <div class="header">
272
+ <h1>🚴 Trek BF Chatbot</h1>
273
+ <div class="subtitle">Konuşma Geçmişi</div>
274
+ <div class="stats">
275
+ <div class="stat-item">
276
+ <div class="stat-value" id="totalMessages">0</div>
277
+ <div class="stat-label">Toplam Mesaj</div>
278
+ </div>
279
+ <div class="stat-item">
280
+ <div class="stat-value" id="totalConversations">0</div>
281
+ <div class="stat-label">Konuşma</div>
282
+ </div>
283
+ <div class="stat-item">
284
+ <div class="stat-value" id="todayMessages">0</div>
285
+ <div class="stat-label">Bugün</div>
286
+ </div>
287
+ </div>
288
+ </div>
289
+
290
+ <div class="filter-container">
291
+ <input type="text" class="search-box" placeholder="Mesajlarda ara..." id="searchInput">
292
+ </div>
293
+
294
+ <div class="chat-container" id="chatContainer">
295
+ <!-- Messages will be inserted here -->
296
+ </div>
297
+ </div>
298
+
299
+ <script>
300
+ const conversations = {CONVERSATIONS_JSON};
301
+
302
+ function formatTime(isoString) {
303
+ const date = new Date(isoString);
304
+ const now = new Date();
305
+ const isToday = date.toDateString() === now.toDateString();
306
+
307
+ if (isToday) {
308
+ return date.toLocaleTimeString('tr-TR', {hour: '2-digit', minute: '2-digit'});
309
+ } else {
310
+ return date.toLocaleDateString('tr-TR') + ' ' + date.toLocaleTimeString('tr-TR', {hour: '2-digit', minute: '2-digit'});
311
+ }
312
+ }
313
+
314
+ function formatDate(isoString) {
315
+ const date = new Date(isoString);
316
+ const now = new Date();
317
+ const diffDays = Math.floor((now - date) / (1000 * 60 * 60 * 24));
318
+
319
+ if (diffDays === 0) return 'Bugün';
320
+ if (diffDays === 1) return 'Dün';
321
+ if (diffDays < 7) return diffDays + ' gün önce';
322
+ return date.toLocaleDateString('tr-TR');
323
+ }
324
+
325
+ function highlightProducts(text) {
326
+ const products = ['MADONE', 'MARLIN', 'DOMANE', 'CHECKPOINT', 'EMONDA', 'FX', 'RAIL', 'POWERFLY'];
327
+ let result = text;
328
+ products.forEach(product => {
329
+ const regex = new RegExp(product, 'gi');
330
+ result = result.replace(regex, '<span class="product-mention">$&</span>');
331
+ });
332
+ return result;
333
+ }
334
+
335
+ function escapeHtml(text) {
336
+ const div = document.createElement('div');
337
+ div.textContent = text;
338
+ return div.innerHTML;
339
+ }
340
+
341
+ function renderMessages(filteredConversations) {
342
+ const container = document.getElementById('chatContainer');
343
+ container.innerHTML = '';
344
+
345
+ if (filteredConversations.length === 0) {
346
+ container.innerHTML = '<div class="no-results">Mesaj bulunamadı</div>';
347
+ return;
348
+ }
349
+
350
+ let lastDate = null;
351
+
352
+ filteredConversations.forEach(conv => {
353
+ const messageDate = new Date(conv.timestamp).toDateString();
354
+
355
+ // Add date divider if new day
356
+ if (lastDate !== messageDate) {
357
+ const divider = document.createElement('div');
358
+ divider.className = 'divider';
359
+ divider.innerHTML = `<span class="divider-text">${formatDate(conv.timestamp)}</span>`;
360
+ container.appendChild(divider);
361
+ lastDate = messageDate;
362
+ }
363
+
364
+ const messageDiv = document.createElement('div');
365
+ messageDiv.className = 'chat-message';
366
+
367
+ // User message
368
+ const userMsg = document.createElement('div');
369
+ userMsg.className = 'message-wrapper user';
370
+ userMsg.innerHTML = `
371
+ <div class="message-bubble">
372
+ ${escapeHtml(conv.user)}
373
+ </div>
374
+ `;
375
+ messageDiv.appendChild(userMsg);
376
+
377
+ // Bot message
378
+ const botMsg = document.createElement('div');
379
+ botMsg.className = 'message-wrapper bot';
380
+ botMsg.innerHTML = `
381
+ <div class="message-bubble">
382
+ ${highlightProducts(escapeHtml(conv.bot))}
383
+ </div>
384
+ `;
385
+ messageDiv.appendChild(botMsg);
386
+
387
+ // Timestamp
388
+ const timestamp = document.createElement('div');
389
+ timestamp.className = 'timestamp';
390
+ timestamp.textContent = formatTime(conv.timestamp);
391
+ messageDiv.appendChild(timestamp);
392
+
393
+ container.appendChild(messageDiv);
394
+ });
395
+
396
+ // Scroll to bottom
397
+ container.scrollTop = container.scrollHeight;
398
+ }
399
+
400
+ function updateStats() {
401
+ const today = new Date().toDateString();
402
+ const todayCount = conversations.filter(c =>
403
+ new Date(c.timestamp).toDateString() === today
404
+ ).length;
405
+
406
+ document.getElementById('totalMessages').textContent = conversations.length * 2;
407
+ document.getElementById('totalConversations').textContent = conversations.length;
408
+ document.getElementById('todayMessages').textContent = todayCount;
409
+ }
410
+
411
+ // Search functionality
412
+ document.getElementById('searchInput').addEventListener('input', (e) => {
413
+ const searchTerm = e.target.value.toLowerCase();
414
+
415
+ if (!searchTerm) {
416
+ renderMessages(conversations);
417
+ return;
418
+ }
419
+
420
+ const filtered = conversations.filter(conv =>
421
+ conv.user.toLowerCase().includes(searchTerm) ||
422
+ conv.bot.toLowerCase().includes(searchTerm)
423
+ );
424
+
425
+ renderMessages(filtered);
426
+ });
427
+
428
+ // Initial render
429
+ updateStats();
430
+ renderMessages(conversations);
431
+ </script>
432
+ </body>
433
+ </html>"""
434
+
435
+ # Replace conversations JSON
436
+ html_content = html_template.replace(
437
+ '{CONVERSATIONS_JSON}',
438
+ json.dumps(conversations, ensure_ascii=False)
439
+ )
440
+
441
+ return html_content
442
+
443
+ def export_html_file():
444
+ """Export conversations to HTML file"""
445
+ html_content = generate_html_report()
446
+
447
+ filename = f"trek_bf_conversations_{datetime.now().strftime('%Y%m%d_%H%M%S')}.html"
448
+
449
+ with open(filename, 'w', encoding='utf-8') as f:
450
+ f.write(html_content)
451
+
452
+ return filename