Decoo commited on
Commit
7c97cf8
·
1 Parent(s): 2863108

modified app.py

Browse files
Files changed (1) hide show
  1. app.py +104 -231
app.py CHANGED
@@ -27,7 +27,7 @@ def get_available_events():
27
  def load_event_data(event_name, format_type, view_type):
28
  """Load and render event data as HTML"""
29
  if not event_name:
30
- return '<div class="no-data"><h2>Please select an event from the dropdown above.</h2></div>'
31
 
32
  key = f"{format_type}_{view_type}"
33
  folder = FOLDER_MAP.get(key)
@@ -39,49 +39,55 @@ def load_event_data(event_name, format_type, view_type):
39
 
40
  return render_data_as_html(data, event_name, format_type, view_type)
41
  except FileNotFoundError:
42
- return f'<div class="no-data"><h2>⚠ File not found</h2><p>{filename}</p></div>'
43
  except Exception as e:
44
- return f'<div class="no-data"><h2>⚠ Error</h2><p>{str(e)}</p></div>'
45
 
46
  def render_data_as_html(data, event_name, format_type, view_type):
47
- """Render JSON data as HTML with modal support"""
48
 
 
49
  file_name = data.get('file_name', event_name)
50
  summary = data.get('summary', 'No summary available')
51
  summary_contexts = data.get('summary_contexts', {})
52
 
 
53
  clusters = process_clusters(data, format_type)
54
 
55
- # Start HTML with modal structure
56
  html_parts = ['''
57
- <div id="citationModal" class="modal">
58
- <div class="modal-content">
59
- <div class="modal-header">
60
- <h3 class="modal-title">Citation Details</h3>
61
- <span class="close">&times;</span>
 
62
  </div>
63
- <div class="modal-body">
64
- <div class="context-section">
65
- <div class="context-label">Context:</div>
66
- <div class="context-content" id="modalContext"></div>
67
  </div>
68
- <div class="context-section">
69
- <div class="context-label">Title:</div>
70
  <div id="modalTitle"></div>
71
  </div>
72
- <div class="context-section context-url">
73
- <div class="context-label">Source URL:</div>
74
- <div><a id="modalUrl" href="#" target="_blank"></a></div>
75
  </div>
76
  </div>
77
  </div>
78
  </div>
79
 
80
- <div class="file-section">
81
- <div class="file-header collapsible-header">📄 ''' + escape_html(file_name) + '''</div>
82
- <div class="file-content collapsible-content">
83
- <div>
84
- <div class="summary"><strong>Summary:</strong><br>''' + process_citations(escape_html(summary), summary_contexts) + '''</div>
 
 
 
85
  ''']
86
 
87
  # Render clusters
@@ -89,59 +95,53 @@ def render_data_as_html(data, event_name, format_type, view_type):
89
  html_parts.append(render_cluster(cluster, format_type))
90
 
91
  html_parts.append('''
92
- </div>
93
  </div>
94
  </div>
95
 
96
  <script>
97
- (function() {
98
- // Initialize modal
99
- const modal = document.getElementById('citationModal');
100
- const closeBtn = modal.querySelector('.close');
101
-
102
- closeBtn.onclick = function() {
 
 
 
 
 
 
103
  modal.style.display = 'none';
104
- };
105
-
106
- window.onclick = function(event) {
107
- if (event.target === modal) {
108
- modal.style.display = 'none';
109
- }
110
- };
111
-
112
- document.addEventListener('keydown', function(event) {
113
- if (event.key === 'Escape' && modal.style.display === 'block') {
114
- modal.style.display = 'none';
115
- }
116
- });
117
-
118
- // Handle citation clicks
119
- document.addEventListener('click', function(event) {
120
- if (event.target.classList.contains('citation')) {
121
- const contextDataStr = event.target.getAttribute('data-context');
122
- if (!contextDataStr) return;
123
-
124
- try {
125
- const contextData = JSON.parse(decodeURIComponent(contextDataStr));
126
- document.getElementById('modalContext').textContent = contextData.context || 'No context available.';
127
- document.getElementById('modalTitle').textContent = contextData.title || 'No title available.';
128
- document.getElementById('modalUrl').textContent = contextData.url || 'No URL provided.';
129
- document.getElementById('modalUrl').href = contextData.url || '#';
130
- modal.style.display = 'block';
131
- } catch (error) {
132
- console.error('Error parsing context data:', error);
133
- }
134
- }
135
- });
136
-
137
- // Handle collapsibles
138
- document.addEventListener('click', function(event) {
139
- const header = event.target.closest('.collapsible-header');
140
- if (header) {
141
- header.classList.toggle('active');
142
  }
143
- });
144
- })();
 
145
  </script>
146
  ''')
147
 
@@ -189,20 +189,22 @@ def render_cluster(cluster, format_type):
189
  header = cluster.get('cluster_headline', 'Cluster')
190
 
191
  html = f'''
192
- <div class="cluster">
193
- <div class="cluster-header collapsible-header">🎯 {escape_html(header)}</div>
194
- <div class="cluster-content collapsible-content">
195
- <div>
 
 
196
  '''
197
 
198
  if format_type == 'summary':
199
  summary_text = cluster.get('cluster_summary', 'No summary')
200
  contexts = cluster.get('used_contexts', {})
201
- html += f'<div class="cluster-summary">{process_citations(escape_html(summary_text), contexts)}</div>'
202
  else:
203
  qas = cluster.get('questions_and_answers', [])
204
  if not qas:
205
- html += '<div class="no-data">No Q&A available</div>'
206
  else:
207
  for qa in qas:
208
  question = qa.get('question') or qa.get('Question', 'No question')
@@ -210,21 +212,21 @@ def render_cluster(cluster, format_type):
210
  contexts = qa.get('used_contexts') or qa.get('summary_contexts', {})
211
 
212
  html += f'''
213
- <div class="qa-item">
214
- <div class="question">❓ {escape_html(question)}</div>
215
- <div class="answer">{process_citations(escape_html(answer), contexts)}</div>
 
 
 
 
216
  </div>
217
  '''
218
 
219
- html += '''
220
- </div>
221
- </div>
222
- </div>
223
- '''
224
  return html
225
 
226
  def process_citations(text, contexts):
227
- """Add citation spans with data for modal popup"""
228
  import re
229
 
230
  def replace_citation(match):
@@ -239,9 +241,12 @@ def process_citations(text, contexts):
239
  'title': ctx.get('title', ''),
240
  'url': ctx.get('url', '')
241
  }
242
- encoded = escape_html(json.dumps(context_data))
243
 
244
- return f'<span class="citation" data-context="{encoded}">[{cit_id}]</span>'
 
 
 
 
245
 
246
  return re.sub(r'\[(\d+)\]', replace_citation, text)
247
 
@@ -283,151 +288,19 @@ def save_feedback(event, format_type, view_type, rating, comment):
283
  # Get events
284
  events = get_available_events()
285
 
286
- # CSS completo dal tuo HTML originale
287
  custom_css = """
288
- * { margin: 0; padding: 0; box-sizing: border-box; }
289
- body {
290
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
291
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
292
- min-height: 100vh;
293
- color: #333;
294
  }
295
  .gradio-container {
296
  max-width: 1200px !important;
297
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
298
- }
299
- .file-section { margin-bottom: 40px; border: 2px solid #e1e5e9; border-radius: 12px; overflow: hidden; }
300
- .file-header { background: linear-gradient(135deg, #667eea, #764ba2); color: white; padding: 20px; font-size: 1.3em; font-weight: 600; }
301
- .file-content { padding: 25px; }
302
- .summary { background: #f8f9fa; border-left: 4px solid #667eea; padding: 20px; margin: 20px 0; border-radius: 0 8px 8px 0; line-height: 1.6; white-space: pre-wrap; }
303
- .cluster { margin: 30px 0; border: 1px solid #dee2e6; border-radius: 10px; overflow: hidden; }
304
- .cluster-header { background: linear-gradient(45deg, #28a745, #20c997); color: white; padding: 15px 20px; font-weight: 600; font-size: 1.1em; }
305
- .cluster-content { padding: 20px; }
306
- .cluster-summary { background: #e8f5e8; border-radius: 8px; padding: 15px; line-height: 1.6; white-space: pre-wrap; }
307
- .qa-item { margin-bottom: 20px; padding: 15px; background: #f8f9fa; border-radius: 8px; border-left: 4px solid #007bff; }
308
- .question { font-weight: 600; color: #495057; margin-bottom: 10px; font-size: 1.05em; }
309
- .answer { color: #6c757d; line-height: 1.5; white-space: pre-wrap; }
310
- .no-data { text-align: center; color: #6c757d; font-style: italic; padding: 40px; }
311
-
312
- .citation {
313
- display: inline;
314
- background: #007bff;
315
- color: white;
316
- padding: 2px 6px;
317
- border-radius: 4px;
318
- font-size: 0.85em;
319
- font-weight: 600;
320
- cursor: pointer;
321
- margin: 0 2px;
322
- transition: all 0.3s ease;
323
- position: relative;
324
- }
325
- .citation:hover {
326
- background: #0056b3;
327
- transform: translateY(-1px);
328
- box-shadow: 0 2px 8px rgba(0, 123, 255, 0.3);
329
- }
330
-
331
- .modal {
332
- display: none;
333
- position: fixed;
334
- z-index: 1000;
335
- left: 0;
336
- top: 0;
337
- width: 100%;
338
- height: 100%;
339
- background-color: rgba(0, 0, 0, 0.5);
340
- backdrop-filter: blur(3px);
341
- }
342
- .modal-content {
343
- background: white;
344
- margin: 5% auto;
345
- padding: 0;
346
- border-radius: 15px;
347
- width: 90%;
348
- max-width: 700px;
349
- max-height: 80vh;
350
- overflow-y: auto;
351
- box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
352
- animation: modalSlideIn 0.3s ease-out;
353
- }
354
- @keyframes modalSlideIn {
355
- from { transform: translateY(-50px); opacity: 0; }
356
- to { transform: translateY(0); opacity: 1; }
357
- }
358
- .modal-header {
359
- background: linear-gradient(135deg, #667eea, #764ba2);
360
- color: white;
361
- padding: 20px;
362
- border-radius: 15px 15px 0 0;
363
- display: flex;
364
- justify-content: space-between;
365
- align-items: center;
366
- }
367
- .modal-title { font-size: 1.2em; font-weight: 600; margin: 0; }
368
- .close {
369
- color: white;
370
- font-size: 28px;
371
- font-weight: bold;
372
- cursor: pointer;
373
- line-height: 1;
374
- transition: opacity 0.3s;
375
- }
376
- .close:hover { opacity: 0.7; }
377
- .modal-body { padding: 25px; }
378
- .context-section { margin-bottom: 20px; }
379
- .context-label { font-weight: 600; color: #495057; margin-bottom: 8px; font-size: 1.05em; }
380
- .context-content {
381
- background: #f8f9fa;
382
- padding: 15px;
383
- border-radius: 8px;
384
- line-height: 1.6;
385
- border-left: 4px solid #007bff;
386
  }
387
- .context-url { margin-top: 15px; }
388
- .context-url a {
389
- color: #007bff;
390
- text-decoration: none;
391
- word-break: break-all;
392
- font-size: 0.95em;
393
- }
394
- .context-url a:hover { text-decoration: underline; }
395
-
396
- .collapsible-header {
397
- cursor: pointer;
398
- position: relative;
399
- transition: background-color 0.2s ease;
400
- }
401
- .collapsible-header:hover { filter: brightness(1.1); }
402
- .collapsible-header::after {
403
- content: '+';
404
- font-family: monospace;
405
- font-weight: bold;
406
- position: absolute;
407
- right: 20px;
408
- top: 50%;
409
- transform: translateY(-50%);
410
- font-size: 1.6em;
411
- line-height: 1;
412
- transition: transform 0.3s ease;
413
- }
414
- .collapsible-header.active::after {
415
- content: '−';
416
- transform: translateY(-50%) rotate(180deg);
417
- }
418
-
419
- .collapsible-content {
420
- display: grid;
421
- grid-template-rows: 0fr;
422
- transition: grid-template-rows 0.4s ease-out;
423
- }
424
-
425
- .collapsible-header.active + .collapsible-content {
426
- grid-template-rows: 1fr;
427
- }
428
-
429
- .collapsible-content > div {
430
- overflow: hidden;
431
  }
432
  """
433
 
@@ -436,8 +309,8 @@ with gr.Blocks(title="Report Viewer", theme=gr.themes.Soft(), css=custom_css) as
436
 
437
  gr.Markdown("""
438
  <div style="text-align: center; color: white; margin-bottom: 30px;">
439
- <h1 style="font-size: 2.5em; margin-bottom: 10px; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);">Our Report</h1>
440
- <p style="font-size: 1.2em; opacity: 0.9;">Select an event and view to visualize the data</p>
441
  </div>
442
  """)
443
 
@@ -463,7 +336,7 @@ with gr.Blocks(title="Report Viewer", theme=gr.themes.Soft(), css=custom_css) as
463
  )
464
 
465
  content_html = gr.HTML(
466
- '<div class="no-data"><h2>Please select an event from the dropdown above.</h2></div>'
467
  )
468
 
469
  # Update on any change
 
27
  def load_event_data(event_name, format_type, view_type):
28
  """Load and render event data as HTML"""
29
  if not event_name:
30
+ return '<div style="text-align: center; padding: 40px; color: #6c757d;"><h2>Please select an event from the dropdown above.</h2></div>'
31
 
32
  key = f"{format_type}_{view_type}"
33
  folder = FOLDER_MAP.get(key)
 
39
 
40
  return render_data_as_html(data, event_name, format_type, view_type)
41
  except FileNotFoundError:
42
+ return f'<div style="text-align: center; padding: 40px;"><h2>⚠ File not found</h2><p>{filename}</p></div>'
43
  except Exception as e:
44
+ return f'<div style="text-align: center; padding: 40px;"><h2>⚠ Error</h2><p>{str(e)}</p></div>'
45
 
46
  def render_data_as_html(data, event_name, format_type, view_type):
47
+ """Render JSON data as HTML"""
48
 
49
+ # Estrai informazioni
50
  file_name = data.get('file_name', event_name)
51
  summary = data.get('summary', 'No summary available')
52
  summary_contexts = data.get('summary_contexts', {})
53
 
54
+ # Process clusters
55
  clusters = process_clusters(data, format_type)
56
 
57
+ # Render HTML con modal
58
  html_parts = ['''
59
+ <!-- Modal for citations -->
60
+ <div id="citationModal" style="display: none; position: fixed; z-index: 1000; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); backdrop-filter: blur(3px);">
61
+ <div style="background: white; margin: 5% auto; padding: 0; border-radius: 15px; width: 90%; max-width: 700px; max-height: 80vh; overflow-y: auto; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);">
62
+ <div style="background: linear-gradient(135deg, #667eea, #764ba2); color: white; padding: 20px; border-radius: 15px 15px 0 0; display: flex; justify-content: space-between; align-items: center;">
63
+ <h3 style="font-size: 1.2em; font-weight: 600; margin: 0;">Citation Details</h3>
64
+ <span id="closeModal" style="color: white; font-size: 28px; font-weight: bold; cursor: pointer; line-height: 1;">&times;</span>
65
  </div>
66
+ <div style="padding: 25px;">
67
+ <div style="margin-bottom: 20px;">
68
+ <div style="font-weight: 600; color: #495057; margin-bottom: 8px; font-size: 1.05em;">Context:</div>
69
+ <div id="modalContext" style="background: #f8f9fa; padding: 15px; border-radius: 8px; line-height: 1.6; border-left: 4px solid #007bff;"></div>
70
  </div>
71
+ <div style="margin-bottom: 20px;">
72
+ <div style="font-weight: 600; color: #495057; margin-bottom: 8px; font-size: 1.05em;">Title:</div>
73
  <div id="modalTitle"></div>
74
  </div>
75
+ <div style="margin-top: 15px;">
76
+ <div style="font-weight: 600; color: #495057; margin-bottom: 8px; font-size: 1.05em;">Source URL:</div>
77
+ <div><a id="modalUrl" href="#" target="_blank" style="color: #007bff; text-decoration: none; word-break: break-all; font-size: 0.95em;"></a></div>
78
  </div>
79
  </div>
80
  </div>
81
  </div>
82
 
83
+ <div style="margin-bottom: 40px; border: 2px solid #e1e5e9; border-radius: 12px; overflow: hidden;">
84
+ <div style="background: linear-gradient(135deg, #667eea, #764ba2); color: white; padding: 20px; font-size: 1.3em; font-weight: 600;">
85
+ 📄 ''' + escape_html(file_name) + '''
86
+ </div>
87
+ <div style="padding: 25px;">
88
+ <div style="background: #f8f9fa; border-left: 4px solid #667eea; padding: 20px; margin: 20px 0; border-radius: 0 8px 8px 0; line-height: 1.6;">
89
+ <strong>Summary:</strong><br>''' + process_citations(escape_html(summary), summary_contexts) + '''
90
+ </div>
91
  ''']
92
 
93
  # Render clusters
 
95
  html_parts.append(render_cluster(cluster, format_type))
96
 
97
  html_parts.append('''
 
98
  </div>
99
  </div>
100
 
101
  <script>
102
+ (function() {
103
+ const modal = document.getElementById('citationModal');
104
+ const closeBtn = document.getElementById('closeModal');
105
+
106
+ // Close modal on click X
107
+ closeBtn.onclick = function() {
108
+ modal.style.display = 'none';
109
+ };
110
+
111
+ // Close modal on click outside
112
+ window.onclick = function(event) {
113
+ if (event.target === modal) {
114
  modal.style.display = 'none';
115
+ }
116
+ };
117
+
118
+ // Close modal on ESC key
119
+ document.addEventListener('keydown', function(event) {
120
+ if (event.key === 'Escape' && modal.style.display === 'block') {
121
+ modal.style.display = 'none';
122
+ }
123
+ });
124
+
125
+ // Handle citation clicks
126
+ document.addEventListener('click', function(event) {
127
+ if (event.target && event.target.classList.contains('citation')) {
128
+ const contextDataStr = event.target.getAttribute('data-context');
129
+ if (!contextDataStr) return;
130
+
131
+ try {
132
+ const contextData = JSON.parse(decodeURIComponent(contextDataStr));
133
+ document.getElementById('modalContext').textContent = contextData.context || 'No context available.';
134
+ document.getElementById('modalTitle').textContent = contextData.title || 'No title available.';
135
+ const urlElement = document.getElementById('modalUrl');
136
+ urlElement.textContent = contextData.url || 'No URL provided.';
137
+ urlElement.href = contextData.url || '#';
138
+ modal.style.display = 'block';
139
+ } catch (error) {
140
+ console.error('Error parsing context data:', error);
 
 
 
 
 
 
 
 
 
 
 
 
141
  }
142
+ }
143
+ });
144
+ })();
145
  </script>
146
  ''')
147
 
 
189
  header = cluster.get('cluster_headline', 'Cluster')
190
 
191
  html = f'''
192
+ <div style="margin: 30px 0; border: 1px solid #dee2e6; border-radius: 10px; overflow: hidden;">
193
+ <details>
194
+ <summary style="background: linear-gradient(45deg, #28a745, #20c997); color: white; padding: 15px 20px; font-weight: 600; font-size: 1.1em; cursor: pointer;">
195
+ 🎯 {escape_html(header)}
196
+ </summary>
197
+ <div style="padding: 20px;">
198
  '''
199
 
200
  if format_type == 'summary':
201
  summary_text = cluster.get('cluster_summary', 'No summary')
202
  contexts = cluster.get('used_contexts', {})
203
+ html += f'<div style="background: #e8f5e8; border-radius: 8px; padding: 15px; line-height: 1.6;">{process_citations(escape_html(summary_text), contexts)}</div>'
204
  else:
205
  qas = cluster.get('questions_and_answers', [])
206
  if not qas:
207
+ html += '<div style="text-align: center; color: #6c757d; font-style: italic;">No Q&A available</div>'
208
  else:
209
  for qa in qas:
210
  question = qa.get('question') or qa.get('Question', 'No question')
 
212
  contexts = qa.get('used_contexts') or qa.get('summary_contexts', {})
213
 
214
  html += f'''
215
+ <div style="margin-bottom: 20px; padding: 15px; background: #f8f9fa; border-radius: 8px; border-left: 4px solid #007bff;">
216
+ <div style="font-weight: 600; color: #495057; margin-bottom: 10px; font-size: 1.05em;">
217
+ {escape_html(question)}
218
+ </div>
219
+ <div style="color: #6c757d; line-height: 1.5;">
220
+ {process_citations(escape_html(answer), contexts)}
221
+ </div>
222
  </div>
223
  '''
224
 
225
+ html += '</div></details></div>'
 
 
 
 
226
  return html
227
 
228
  def process_citations(text, contexts):
229
+ """Add citation spans with popup data"""
230
  import re
231
 
232
  def replace_citation(match):
 
241
  'title': ctx.get('title', ''),
242
  'url': ctx.get('url', '')
243
  }
 
244
 
245
+ # URL encode the JSON data
246
+ import urllib.parse
247
+ encoded = urllib.parse.quote(json.dumps(context_data))
248
+
249
+ return f'<span class="citation" data-context="{encoded}" style="display: inline; background: #007bff; color: white; padding: 2px 6px; border-radius: 4px; font-size: 0.85em; font-weight: 600; cursor: pointer; margin: 0 2px; transition: all 0.3s ease;">[{cit_id}]</span>'
250
 
251
  return re.sub(r'\[(\d+)\]', replace_citation, text)
252
 
 
288
  # Get events
289
  events = get_available_events()
290
 
291
+ # CSS personalizzato - solo per lo sfondo
292
  custom_css = """
293
+ body {
294
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
 
 
 
 
295
  }
296
  .gradio-container {
297
  max-width: 1200px !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
  }
299
+ /* Hover effect for citations */
300
+ .citation:hover {
301
+ background: #0056b3 !important;
302
+ transform: translateY(-1px);
303
+ box-shadow: 0 2px 8px rgba(0, 123, 255, 0.3);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
304
  }
305
  """
306
 
 
309
 
310
  gr.Markdown("""
311
  <div style="text-align: center; color: white; margin-bottom: 30px;">
312
+ <h1 style="font-size: 2.5em; margin-bottom: 10px; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);">📊 Our Report Viewer</h1>
313
+ <p style="font-size: 1.2em; opacity: 0.9;">Explore events organized by topics or SDGs</p>
314
  </div>
315
  """)
316
 
 
336
  )
337
 
338
  content_html = gr.HTML(
339
+ '<div style="text-align: center; padding: 40px; color: #6c757d;"><h2>Please select an event from the dropdown above.</h2></div>'
340
  )
341
 
342
  # Update on any change