import gradio as gr import json import os from datetime import datetime import urllib.parse # Data folder paths DATA_BASE = "Viewer/Data/Our_system" FOLDER_MAP = { "qa_subtopics": f"{DATA_BASE}/QA+Topics", "qa_sdgs": f"{DATA_BASE}/QA+SDGs", "summary_subtopics": f"{DATA_BASE}/Summary+Topics", "summary_sdgs": f"{DATA_BASE}/Summary+SDG" } def get_available_events(): """Get all available events from JSON files""" events = set() for folder in FOLDER_MAP.values(): if os.path.exists(folder): for file in os.listdir(folder): if file.endswith('_combined_data.json'): event_name = file.replace('_combined_data.json', '') if not event_name.isdigit(): events.add(event_name) return sorted(list(events)) def load_event_data(event_name, format_type, view_type): """Load and render event data as HTML""" if not event_name: return '

Please select an event from the dropdown above.

' key = f"{format_type}_{view_type}" folder = FOLDER_MAP.get(key) filename = f"{folder}/{event_name}_combined_data.json" try: with open(filename, 'r', encoding='utf-8') as f: data = json.load(f) return render_data_as_html(data, event_name, format_type, view_type) except FileNotFoundError: return f'

⚠ File not found

{filename}

' except Exception as e: return f'

⚠ Error

{str(e)}

' def render_data_as_html(data, event_name, format_type, view_type): """Render JSON data as HTML""" # Estrai informazioni file_name = data.get('file_name', event_name) summary = data.get('summary', 'No summary available') summary_contexts = data.get('summary_contexts', {}) # Process clusters clusters = process_clusters(data, format_type) # Genera un ID unico per questa istanza unique_id = f"viewer_{abs(hash(event_name + format_type + view_type)) % 100000}" # Render HTML con modal html_parts = [f'''
📄 {escape_html(file_name)}
Summary:
{process_citations(escape_html(summary), summary_contexts, unique_id)}
'''] # Render clusters for cluster in clusters: html_parts.append(render_cluster(cluster, format_type, unique_id)) html_parts.append(f'''
''') return ''.join(html_parts) def process_clusters(data, format_type): """Extract clusters from data""" if 'clusters' in data and isinstance(data['clusters'], list): return data['clusters'] ignore_keys = ['file_name', 'summary', 'summary_contexts'] clusters = [] for key, value in data.items(): if key in ignore_keys: continue if format_type == 'qa': clusters.append({ 'cluster_headline': key, 'questions_and_answers': value if isinstance(value, list) else [] }) else: if isinstance(value, list): summary_text = '\n\n'.join([ v.get('cluster_summary') or v.get('summary', '') for v in value if isinstance(v, dict) ]) clusters.append({ 'cluster_headline': key, 'cluster_summary': summary_text or 'No summary', 'used_contexts': value[0].get('used_contexts', {}) if value else {} }) elif isinstance(value, dict): clusters.append({ 'cluster_headline': key, 'cluster_summary': value.get('cluster_summary') or value.get('summary', 'No summary'), 'used_contexts': value.get('used_contexts', {}) }) return clusters def render_cluster(cluster, format_type, unique_id): """Render a single cluster as HTML""" header = cluster.get('cluster_headline', 'Cluster') html = f'''
🎯 {escape_html(header)}
''' if format_type == 'summary': summary_text = cluster.get('cluster_summary', 'No summary') contexts = cluster.get('used_contexts', {}) html += f'
{process_citations(escape_html(summary_text), contexts, unique_id)}
' else: qas = cluster.get('questions_and_answers', []) if not qas: html += '
No Q&A available
' else: for qa in qas: question = qa.get('question') or qa.get('Question', 'No question') answer = qa.get('updated_retrieved_answer') or qa.get('retrieved_answer') or qa.get('answer', 'No answer') contexts = qa.get('used_contexts') or qa.get('summary_contexts', {}) html += f'''
❓ {escape_html(question)}
{process_citations(escape_html(answer), contexts, unique_id)}
''' html += '
' return html def process_citations(text, contexts, unique_id): """Add citation spans with popup data""" import re def replace_citation(match): cit_id = match.group(1) ctx = contexts.get(cit_id, {}) if not ctx: return match.group(0) context_text = urllib.parse.quote(ctx.get('context', '')[:500]) title_text = urllib.parse.quote(ctx.get('title', '')) url_text = urllib.parse.quote(ctx.get('url', '')) return f'[{cit_id}]' return re.sub(r'\[(\d+)\]', replace_citation, text) def escape_html(text): """Escape HTML special characters""" if not text: return '' return str(text).replace('&', '&').replace('<', '<').replace('>', '>').replace('"', '"').replace("'", ''') def save_feedback(event, format_type, view_type, rating, comment): """Save feedback to JSON file""" if not event: return "⚠️ Please select an event first" feedback_dir = "feedback" os.makedirs(feedback_dir, exist_ok=True) feedback_file = os.path.join(feedback_dir, "feedback.json") if os.path.exists(feedback_file): with open(feedback_file, 'r', encoding='utf-8') as f: feedbacks = json.load(f) else: feedbacks = [] feedbacks.append({ "timestamp": datetime.now().isoformat(), "event": event, "format": format_type, "view": view_type, "rating": rating, "comment": comment }) with open(feedback_file, 'w', encoding='utf-8') as f: json.dump(feedbacks, f, indent=2, ensure_ascii=False) return "✅ Thank you for your feedback!" # Get events events = get_available_events() # CSS personalizzato con font originale custom_css = """ * { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important; } body { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); } .gradio-container { max-width: 1200px !important; } """ # Create Gradio interface with gr.Blocks(title="Report Viewer", theme=gr.themes.Soft(), css=custom_css) as demo: gr.Markdown("""

📊 Our Report Viewer

Explore events organized by topics or SDGs

""") with gr.Tab("📖 Viewer"): with gr.Row(): with gr.Column(scale=2): event_dropdown = gr.Dropdown( choices=events, label="Select Event", value=None ) with gr.Column(scale=1): view_radio = gr.Radio( choices=["subtopics", "sdgs"], label="Arranged by", value="subtopics" ) with gr.Column(scale=1): format_radio = gr.Radio( choices=["qa", "summary"], label="Presented as", value="qa" ) content_html = gr.HTML( '

Please select an event from the dropdown above.

' ) # Update on any change event_dropdown.change( load_event_data, inputs=[event_dropdown, format_radio, view_radio], outputs=content_html ) format_radio.change( load_event_data, inputs=[event_dropdown, format_radio, view_radio], outputs=content_html ) view_radio.change( load_event_data, inputs=[event_dropdown, format_radio, view_radio], outputs=content_html ) with gr.Tab("💬 Feedback"): gr.Markdown("### Leave your feedback on the results") with gr.Row(): fb_event = gr.Dropdown(choices=events, label="Event") fb_format = gr.Radio(choices=["qa", "summary"], label="Format", value="qa") fb_view = gr.Radio(choices=["subtopics", "sdgs"], label="View", value="subtopics") fb_rating = gr.Slider(minimum=1, maximum=5, step=1, label="Rating (1-5 stars)", value=3) fb_comment = gr.Textbox(label="Comment (optional)", lines=5, placeholder="Write your feedback here...") fb_submit = gr.Button("Submit Feedback", variant="primary") fb_output = gr.Textbox(label="Status", interactive=False) fb_submit.click( save_feedback, inputs=[fb_event, fb_format, fb_view, fb_rating, fb_comment], outputs=fb_output ) if __name__ == "__main__": demo.launch()