Spaces:
Sleeping
Sleeping
| # enhanced_verification_ui.py | |
| """ | |
| Enhanced Verification UI Components for Multi-Mode Verification. | |
| Provides interface components for mode selection, session resumption, | |
| and enhanced verification workflows across different modes. | |
| Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 12.1, 12.2, 12.3, 12.4, 12.5 | |
| """ | |
| import gradio as gr | |
| from typing import List, Dict, Tuple, Optional, Any | |
| from dataclasses import dataclass | |
| from datetime import datetime | |
| import uuid | |
| from src.core.verification_models import ( | |
| EnhancedVerificationSession, | |
| VerificationRecord, | |
| TestMessage, | |
| TestDataset, | |
| ) | |
| from src.core.verification_store import JSONVerificationStore | |
| from src.core.test_datasets import TestDatasetManager | |
| from src.interface.enhanced_dataset_interface import EnhancedDatasetInterfaceController | |
| from src.interface.ui_consistency_components import ( | |
| StandardizedComponents, | |
| ClassificationDisplay, | |
| ProgressDisplay, | |
| ErrorDisplay, | |
| SessionDisplay, | |
| HelpDisplay, | |
| UITheme | |
| ) | |
| class ModeSelectionState: | |
| """State container for mode selection interface.""" | |
| current_mode: Optional[str] = None | |
| incomplete_sessions: List[EnhancedVerificationSession] = None | |
| selected_session: Optional[EnhancedVerificationSession] = None | |
| def __post_init__(self): | |
| if self.incomplete_sessions is None: | |
| self.incomplete_sessions = [] | |
| class EnhancedVerificationUIComponents: | |
| """Enhanced UI components for multi-mode verification.""" | |
| # Mode definitions with descriptions | |
| MODE_OPTIONS = { | |
| "enhanced_dataset": { | |
| "icon": "📊", | |
| "title": "Enhanced Datasets", | |
| "description": "Use existing test datasets with editing capabilities. Add, modify, or delete test cases to customize datasets for specific testing scenarios.", | |
| "features": [ | |
| "Edit existing datasets", | |
| "Add new test cases", | |
| "Modify message text and classifications", | |
| "Delete test cases with confirmation", | |
| "Dataset versioning and backup" | |
| ] | |
| }, | |
| "manual_input": { | |
| "icon": "✏️", | |
| "title": "Manual Input", | |
| "description": "Manually enter individual messages for immediate testing. Perfect for exploring edge cases or testing specific scenarios in real-time.", | |
| "features": [ | |
| "Real-time message classification", | |
| "Immediate feedback collection", | |
| "Session results accumulation", | |
| "Quick testing of specific cases", | |
| "Export manual input results" | |
| ] | |
| }, | |
| "file_upload": { | |
| "icon": "📁", | |
| "title": "File Upload", | |
| "description": "Upload CSV or XLSX files containing test messages for batch processing. Ideal for large-scale testing with pre-prepared datasets.", | |
| "features": [ | |
| "CSV and XLSX file support", | |
| "Batch processing with progress tracking", | |
| "Automated verification against expected results", | |
| "File format validation and error reporting", | |
| "Comprehensive export options" | |
| ] | |
| } | |
| } | |
| def create_mode_selection_interface() -> gr.Blocks: | |
| """ | |
| Create the main mode selection interface. | |
| Returns: | |
| Gradio Blocks component for mode selection | |
| """ | |
| with gr.Blocks() as mode_selection: | |
| # Header | |
| gr.Markdown("# 🔍 Enhanced Verification Modes") | |
| gr.Markdown("Choose your verification approach based on your testing needs and data source.") | |
| # Incomplete sessions section | |
| incomplete_sessions_section = gr.Row(visible=False) | |
| with incomplete_sessions_section: | |
| with gr.Column(): | |
| gr.Markdown("## 📋 Resume Previous Sessions") | |
| gr.Markdown("You have incomplete verification sessions. You can resume where you left off or start a new session.") | |
| incomplete_sessions_display = gr.HTML( | |
| value="", | |
| label="Incomplete Sessions" | |
| ) | |
| with gr.Row(): | |
| resume_session_btn = StandardizedComponents.create_primary_button( | |
| "Resume Selected Session", | |
| "▶️", | |
| "lg" | |
| ) | |
| resume_session_btn.scale = 2 | |
| clear_sessions_btn = StandardizedComponents.create_secondary_button( | |
| "Clear All Sessions", | |
| "🗑️", | |
| "lg" | |
| ) | |
| clear_sessions_btn.scale = 1 | |
| # Mode selection cards | |
| gr.Markdown("## 🎯 Select Verification Mode") | |
| with gr.Row(): | |
| # Enhanced Dataset Mode | |
| with gr.Column(scale=1): | |
| mode_info = EnhancedVerificationUIComponents.MODE_OPTIONS["enhanced_dataset"] | |
| gr.Markdown(f"### {mode_info['icon']} {mode_info['title']}") | |
| gr.Markdown(mode_info["description"]) | |
| gr.Markdown("**Features:**") | |
| for feature in mode_info["features"]: | |
| gr.Markdown(f"• {feature}") | |
| enhanced_dataset_btn = StandardizedComponents.create_primary_button( | |
| "Start Enhanced Dataset Mode", | |
| mode_info['icon'], | |
| "lg" | |
| ) | |
| # Manual Input Mode | |
| with gr.Column(scale=1): | |
| mode_info = EnhancedVerificationUIComponents.MODE_OPTIONS["manual_input"] | |
| gr.Markdown(f"### {mode_info['icon']} {mode_info['title']}") | |
| gr.Markdown(mode_info["description"]) | |
| gr.Markdown("**Features:**") | |
| for feature in mode_info["features"]: | |
| gr.Markdown(f"• {feature}") | |
| manual_input_btn = StandardizedComponents.create_primary_button( | |
| "Start Manual Input Mode", | |
| mode_info['icon'], | |
| "lg" | |
| ) | |
| # File Upload Mode | |
| with gr.Column(scale=1): | |
| mode_info = EnhancedVerificationUIComponents.MODE_OPTIONS["file_upload"] | |
| gr.Markdown(f"### {mode_info['icon']} {mode_info['title']}") | |
| gr.Markdown(mode_info["description"]) | |
| gr.Markdown("**Features:**") | |
| for feature in mode_info["features"]: | |
| gr.Markdown(f"• {feature}") | |
| file_upload_btn = StandardizedComponents.create_primary_button( | |
| "Start File Upload Mode", | |
| mode_info['icon'], | |
| "lg" | |
| ) | |
| # Status message | |
| status_message = gr.Markdown( | |
| "", | |
| visible=True, | |
| label="Status" | |
| ) | |
| return mode_selection | |
| def render_incomplete_sessions_display(sessions: List[EnhancedVerificationSession]) -> str: | |
| """ | |
| Render HTML display for incomplete sessions. | |
| Args: | |
| sessions: List of incomplete verification sessions | |
| Returns: | |
| HTML string for displaying incomplete sessions | |
| """ | |
| if not sessions: | |
| return "" | |
| html = """ | |
| <div style="font-family: system-ui; padding: 1em; background-color: #f9fafb; border-radius: 8px; border: 1px solid #e5e7eb;"> | |
| """ | |
| for session in sessions: | |
| mode_info = EnhancedVerificationUIComponents.MODE_OPTIONS.get( | |
| session.mode_type, | |
| {"icon": "❓", "title": "Unknown Mode"} | |
| ) | |
| progress_pct = (session.verified_count / session.total_messages * 100) if session.total_messages > 0 else 0 | |
| accuracy = (session.correct_count / session.verified_count * 100) if session.verified_count > 0 else 0 | |
| # Format creation time | |
| time_ago = EnhancedVerificationUIComponents._format_time_ago(session.created_at) | |
| html += f""" | |
| <div style="margin-bottom: 1em; padding: 1em; background-color: white; border-radius: 6px; border: 1px solid #d1d5db; cursor: pointer;" | |
| onclick="this.style.backgroundColor='#eff6ff'; this.style.borderColor='#3b82f6';"> | |
| <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5em;"> | |
| <h4 style="margin: 0; color: #1f2937;"> | |
| {mode_info['icon']} {mode_info['title']} - {session.dataset_name} | |
| </h4> | |
| <span style="font-size: 0.875em; color: #6b7280;">{time_ago}</span> | |
| </div> | |
| <div style="margin-bottom: 0.5em;"> | |
| <div style="display: flex; justify-content: space-between; margin-bottom: 0.25em;"> | |
| <span style="font-size: 0.875em; color: #374151;">Progress: {session.verified_count}/{session.total_messages}</span> | |
| <span style="font-size: 0.875em; color: #374151;">{progress_pct:.0f}%</span> | |
| </div> | |
| <div style="width: 100%; background-color: #e5e7eb; border-radius: 4px; height: 8px;"> | |
| <div style="width: {progress_pct}%; background-color: #3b82f6; border-radius: 4px; height: 8px;"></div> | |
| </div> | |
| </div> | |
| <div style="display: flex; gap: 1em; font-size: 0.875em; color: #6b7280;"> | |
| <span>✓ Correct: {session.correct_count}</span> | |
| <span>✗ Incorrect: {session.incorrect_count}</span> | |
| <span>📊 Accuracy: {accuracy:.1f}%</span> | |
| </div> | |
| <div style="margin-top: 0.5em; font-size: 0.75em; color: #9ca3af;"> | |
| Session ID: {session.session_id[:8]}... | |
| </div> | |
| </div> | |
| """ | |
| html += """ | |
| </div> | |
| <p style="font-size: 0.875em; color: #6b7280; margin-top: 0.5em;"> | |
| 💡 <strong>Tip:</strong> Click on a session above to select it, then click "Resume Selected Session" to continue where you left off. | |
| </p> | |
| """ | |
| return html | |
| def _format_time_ago(timestamp: datetime) -> str: | |
| """ | |
| Format timestamp as time ago string. | |
| Args: | |
| timestamp: Datetime to format | |
| Returns: | |
| Human-readable time ago string | |
| """ | |
| now = datetime.now() | |
| diff = now - timestamp | |
| if diff.days > 0: | |
| return f"{diff.days} day{'s' if diff.days != 1 else ''} ago" | |
| elif diff.seconds > 3600: | |
| hours = diff.seconds // 3600 | |
| return f"{hours} hour{'s' if hours != 1 else ''} ago" | |
| elif diff.seconds > 60: | |
| minutes = diff.seconds // 60 | |
| return f"{minutes} minute{'s' if minutes != 1 else ''} ago" | |
| else: | |
| return "Just now" | |
| def check_for_incomplete_sessions(store: JSONVerificationStore) -> Tuple[bool, List[EnhancedVerificationSession], str]: | |
| """ | |
| Check for incomplete sessions and return display information. | |
| Args: | |
| store: Verification data store | |
| Returns: | |
| Tuple of (has_incomplete, sessions_list, display_html) | |
| """ | |
| try: | |
| incomplete_sessions = store.get_incomplete_sessions() | |
| # Filter to only enhanced sessions for this interface | |
| enhanced_sessions = [ | |
| s for s in incomplete_sessions | |
| if isinstance(s, EnhancedVerificationSession) | |
| ] | |
| if enhanced_sessions: | |
| display_html = EnhancedVerificationUIComponents.render_incomplete_sessions_display(enhanced_sessions) | |
| return True, enhanced_sessions, display_html | |
| else: | |
| return False, [], "" | |
| except Exception as e: | |
| error_html = f""" | |
| <div style="padding: 1em; background-color: #fef2f2; border-left: 4px solid #dc2626; border-radius: 4px;"> | |
| <h4 style="color: #dc2626; margin-top: 0;">❌ Error Loading Sessions</h4> | |
| <p style="margin-bottom: 0;">Could not load incomplete sessions: {str(e)}</p> | |
| </div> | |
| """ | |
| return False, [], error_html | |
| def create_mode_switch_confirmation(current_mode: str, target_mode: str, has_progress: bool) -> Tuple[str, bool]: | |
| """ | |
| Create mode switch confirmation message. | |
| Args: | |
| current_mode: Current verification mode | |
| target_mode: Target verification mode | |
| has_progress: Whether there is unsaved progress | |
| Returns: | |
| Tuple of (warning_message, show_dialog) | |
| """ | |
| if not has_progress: | |
| return "", False | |
| current_info = EnhancedVerificationUIComponents.MODE_OPTIONS.get(current_mode, {"title": "Unknown"}) | |
| target_info = EnhancedVerificationUIComponents.MODE_OPTIONS.get(target_mode, {"title": "Unknown"}) | |
| warning_message = f""" | |
| You are currently in **{current_info['title']}** mode and have unsaved progress. | |
| Switching to **{target_info['title']}** mode will: | |
| - Save your current progress automatically | |
| - Switch to the new verification mode | |
| - Allow you to resume the current session later | |
| **What would you like to do?** | |
| """ | |
| return warning_message, True | |
| def create_enhanced_dataset_interface() -> gr.Blocks: | |
| """ | |
| Create enhanced dataset mode interface (basic version). | |
| Returns: | |
| Gradio Blocks component for enhanced dataset mode | |
| """ | |
| with gr.Blocks() as enhanced_dataset_interface: | |
| gr.Markdown("# 📊 Enhanced Dataset Mode") | |
| gr.Markdown("Select and customize test datasets for verification. You can edit existing datasets or create new test cases.") | |
| # Back to mode selection | |
| back_to_modes_btn = StandardizedComponents.create_navigation_button("Back to Mode Selection") | |
| # Status and error messages | |
| status_message = gr.Markdown("", visible=True) | |
| return enhanced_dataset_interface | |
| def create_enhanced_dataset_interface_with_handlers() -> gr.Blocks: | |
| """ | |
| Create enhanced dataset mode interface with complete event handlers. | |
| Returns: | |
| Gradio Blocks component for enhanced dataset mode with functionality | |
| """ | |
| # Initialize controller | |
| controller = EnhancedDatasetInterfaceController() | |
| with gr.Blocks() as enhanced_dataset_interface: | |
| # Application state (headers and back button are in parent interface) | |
| current_dataset_state = gr.State(value=None) | |
| verification_session_state = gr.State(value=None) | |
| # Dataset selection interface | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| gr.Markdown("## 📋 Select Dataset") | |
| # Dataset selector | |
| dataset_selector = gr.Dropdown( | |
| choices=[], | |
| label="Available Datasets", | |
| info="Choose a dataset to verify or edit", | |
| interactive=True | |
| ) | |
| with gr.Row(): | |
| load_dataset_btn = StandardizedComponents.create_primary_button("Load Dataset", "📥") | |
| load_dataset_btn.scale = 2 | |
| edit_dataset_btn = StandardizedComponents.create_secondary_button("Edit Dataset", "✏️") | |
| edit_dataset_btn.scale = 1 | |
| with gr.Column(scale=1): | |
| gr.Markdown("## 📊 Dataset Information") | |
| dataset_info_display = gr.Markdown( | |
| "Select a dataset to view details", | |
| label="Dataset Details" | |
| ) | |
| # Verification interface (initially hidden) | |
| verification_section = gr.Row(visible=False) | |
| with verification_section: | |
| with gr.Column(): | |
| gr.Markdown("## 🔍 Dataset Verification") | |
| # Verification controls | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| verifier_name_input = gr.Textbox( | |
| label="Verifier Name", | |
| placeholder="Enter your name...", | |
| interactive=True | |
| ) | |
| with gr.Column(scale=1): | |
| start_verification_btn = StandardizedComponents.create_primary_button( | |
| "Start Verification", | |
| "🚀", | |
| "lg" | |
| ) | |
| # Progress display | |
| verification_progress = gr.Markdown( | |
| "Ready to start verification", | |
| label="Progress" | |
| ) | |
| # Message review area (initially hidden) | |
| message_review_area = gr.Row(visible=False) | |
| with message_review_area: | |
| with gr.Column(scale=2): | |
| # Current message display | |
| current_message_display = gr.Textbox( | |
| label="📝 Patient Message", | |
| interactive=False, | |
| lines=4 | |
| ) | |
| # Classification results | |
| classifier_decision_display = gr.Markdown( | |
| "🔄 Loading...", | |
| label="🎯 Classifier Decision" | |
| ) | |
| classifier_confidence_display = gr.Markdown( | |
| "Loading...", | |
| label="📊 Confidence Level" | |
| ) | |
| classifier_indicators_display = gr.Markdown( | |
| "Loading...", | |
| label="🔍 Detected Indicators" | |
| ) | |
| # Verification buttons | |
| with gr.Row(): | |
| correct_classification_btn = StandardizedComponents.create_primary_button( | |
| "Correct", | |
| "✓" | |
| ) | |
| correct_classification_btn.scale = 1 | |
| incorrect_classification_btn = StandardizedComponents.create_stop_button( | |
| "Incorrect", | |
| "✗" | |
| ) | |
| incorrect_classification_btn.scale = 1 | |
| # Correction section (initially hidden) | |
| correction_section = gr.Row(visible=False) | |
| with correction_section: | |
| correction_selector = ClassificationDisplay.create_classification_radio() | |
| correction_notes = gr.Textbox( | |
| label="Notes (Optional)", | |
| placeholder="Why is this incorrect?", | |
| lines=2, | |
| interactive=True | |
| ) | |
| submit_correction_btn = StandardizedComponents.create_primary_button("Submit", "✓") | |
| with gr.Column(scale=1): | |
| # Session statistics | |
| gr.Markdown("### 📊 Session Statistics") | |
| session_stats_display = gr.Markdown( | |
| """ | |
| **Messages Processed:** 0 | |
| **Correct Classifications:** 0 | |
| **Incorrect Classifications:** 0 | |
| **Accuracy:** 0% | |
| """, | |
| label="Statistics" | |
| ) | |
| # Export options | |
| gr.Markdown("### 💾 Export Options") | |
| with gr.Column(): | |
| export_csv_btn = StandardizedComponents.create_export_button("csv") | |
| export_json_btn = StandardizedComponents.create_export_button("json") | |
| export_xlsx_btn = StandardizedComponents.create_export_button("xlsx") | |
| # Status and error messages | |
| status_message = gr.Markdown("", visible=True) | |
| # Event handlers | |
| def initialize_interface(): | |
| """Initialize the interface with datasets and templates.""" | |
| dataset_choices, dataset_info, status_msg, templates = controller.initialize_interface() | |
| # Get first dataset info if available | |
| first_dataset = None | |
| if dataset_choices: | |
| first_info, first_dataset = controller.get_dataset_info(dataset_choices[0]) | |
| dataset_info = first_info | |
| # Use gr.update() to properly update the dropdown | |
| return ( | |
| gr.update(choices=dataset_choices, value=dataset_choices[0] if dataset_choices else None), # dataset_selector | |
| dataset_info, # dataset_info_display | |
| first_dataset, # current_dataset_state | |
| status_msg # status_message | |
| ) | |
| def on_dataset_selection_change(dataset_selection): | |
| """Handle dataset selection change.""" | |
| dataset_info, dataset_obj = controller.get_dataset_info(dataset_selection) | |
| return ( | |
| dataset_info, # dataset_info_display | |
| dataset_obj # current_dataset_state | |
| ) | |
| def on_load_dataset(current_dataset): | |
| """Handle load dataset for verification.""" | |
| if not current_dataset: | |
| return ( | |
| gr.Row(visible=False), # verification_section | |
| "❌ No dataset selected" # status_message | |
| ) | |
| return ( | |
| gr.Row(visible=True), # verification_section | |
| f"✅ Dataset '{current_dataset.name}' loaded for verification" # status_message | |
| ) | |
| def on_start_verification(current_dataset, verifier_name): | |
| """Handle starting verification session.""" | |
| if not current_dataset: | |
| return ( | |
| None, # verification_session_state | |
| gr.Row(visible=False), # message_review_area | |
| "❌ No dataset selected" # status_message | |
| ) | |
| success, message, session = controller.start_verification_session( | |
| current_dataset, verifier_name | |
| ) | |
| if success: | |
| # Load first message | |
| current_message, classification_result = controller.get_current_message_for_verification() | |
| if current_message: | |
| # Format classification results using standardized components | |
| decision_badge = ClassificationDisplay.format_classification_badge( | |
| classification_result.get('decision', 'unknown') | |
| ) | |
| confidence_text = ClassificationDisplay.format_confidence_display( | |
| classification_result.get('confidence', 0) | |
| ) | |
| indicators_text = ClassificationDisplay.format_indicators_display( | |
| classification_result.get('indicators', []) | |
| ) | |
| return ( | |
| session, # verification_session_state | |
| gr.Row(visible=True), # message_review_area | |
| current_message.text, # current_message_display | |
| decision_badge, # classifier_decision_display | |
| confidence_text, # classifier_confidence_display | |
| indicators_text, # classifier_indicators_display | |
| f"Progress: 1 of {len(current_dataset.messages)} messages", # verification_progress | |
| message # status_message | |
| ) | |
| else: | |
| return ( | |
| session, # verification_session_state | |
| gr.Row(visible=False), # message_review_area | |
| "", # current_message_display | |
| "", # classifier_decision_display | |
| "", # classifier_confidence_display | |
| "", # classifier_indicators_display | |
| "No messages to verify", # verification_progress | |
| "❌ No messages in dataset" # status_message | |
| ) | |
| else: | |
| return ( | |
| None, # verification_session_state | |
| gr.Row(visible=False), # message_review_area | |
| "", # current_message_display | |
| "", # classifier_decision_display | |
| "", # classifier_confidence_display | |
| "", # classifier_indicators_display | |
| "", # verification_progress | |
| message # status_message | |
| ) | |
| def on_correct_classification(): | |
| """Handle correct classification feedback.""" | |
| success, message, stats = controller.submit_verification_feedback(True) | |
| if success and not stats.get('is_complete', False): | |
| # Load next message | |
| current_message, classification_result = controller.get_current_message_for_verification() | |
| if current_message: | |
| decision_badge = f"🎯 {classification_result.get('decision', 'Unknown').upper()}" | |
| confidence_text = f"📊 {classification_result.get('confidence', 0) * 100:.1f}% confident" | |
| indicators_text = "🔍 " + ", ".join(classification_result.get('indicators', ['No indicators'])) | |
| stats_text = f""" | |
| **Messages Processed:** {stats['processed']} | |
| **Correct Classifications:** {stats['correct']} | |
| **Incorrect Classifications:** {stats['incorrect']} | |
| **Accuracy:** {stats['accuracy']:.1f}% | |
| """ | |
| return ( | |
| current_message.text, # current_message_display | |
| decision_badge, # classifier_decision_display | |
| confidence_text, # classifier_confidence_display | |
| indicators_text, # classifier_indicators_display | |
| f"Progress: {stats['processed'] + 1} of {stats['total']} messages", # verification_progress | |
| stats_text, # session_stats_display | |
| gr.Row(visible=False), # correction_section | |
| message # status_message | |
| ) | |
| else: | |
| # Session complete | |
| stats_text = f""" | |
| **Session Complete!** | |
| **Messages Processed:** {stats['processed']} | |
| **Correct Classifications:** {stats['correct']} | |
| **Incorrect Classifications:** {stats['incorrect']} | |
| **Final Accuracy:** {stats['accuracy']:.1f}% | |
| """ | |
| return ( | |
| "Session completed!", # current_message_display | |
| "✅ All messages verified", # classifier_decision_display | |
| "", # classifier_confidence_display | |
| "", # classifier_indicators_display | |
| "✅ Verification complete", # verification_progress | |
| stats_text, # session_stats_display | |
| gr.Row(visible=False), # correction_section | |
| message # status_message | |
| ) | |
| else: | |
| return ( | |
| gr.Textbox(value=""), # current_message_display (no change) | |
| gr.Markdown(value=""), # classifier_decision_display (no change) | |
| gr.Markdown(value=""), # classifier_confidence_display (no change) | |
| gr.Markdown(value=""), # classifier_indicators_display (no change) | |
| gr.Markdown(value=""), # verification_progress (no change) | |
| gr.Markdown(value=""), # session_stats_display (no change) | |
| gr.Row(visible=False), # correction_section | |
| message # status_message | |
| ) | |
| def on_incorrect_classification(): | |
| """Handle incorrect classification - show correction options.""" | |
| return ( | |
| gr.Row(visible=True), # correction_section | |
| "Please select the correct classification" # status_message | |
| ) | |
| def on_submit_correction(correction, notes): | |
| """Handle correction submission.""" | |
| success, message, stats = controller.submit_verification_feedback( | |
| False, correction, notes | |
| ) | |
| if success and not stats.get('is_complete', False): | |
| # Load next message | |
| current_message, classification_result = controller.get_current_message_for_verification() | |
| if current_message: | |
| decision_badge = f"🎯 {classification_result.get('decision', 'Unknown').upper()}" | |
| confidence_text = f"📊 {classification_result.get('confidence', 0) * 100:.1f}% confident" | |
| indicators_text = "🔍 " + ", ".join(classification_result.get('indicators', ['No indicators'])) | |
| stats_text = f""" | |
| **Messages Processed:** {stats['processed']} | |
| **Correct Classifications:** {stats['correct']} | |
| **Incorrect Classifications:** {stats['incorrect']} | |
| **Accuracy:** {stats['accuracy']:.1f}% | |
| """ | |
| return ( | |
| current_message.text, # current_message_display | |
| decision_badge, # classifier_decision_display | |
| confidence_text, # classifier_confidence_display | |
| indicators_text, # classifier_indicators_display | |
| f"Progress: {stats['processed'] + 1} of {stats['total']} messages", # verification_progress | |
| stats_text, # session_stats_display | |
| gr.Row(visible=False), # correction_section | |
| "", # correction_notes (clear) | |
| message # status_message | |
| ) | |
| else: | |
| # Session complete | |
| stats_text = f""" | |
| **Session Complete!** | |
| **Messages Processed:** {stats['processed']} | |
| **Correct Classifications:** {stats['correct']} | |
| **Incorrect Classifications:** {stats['incorrect']} | |
| **Final Accuracy:** {stats['accuracy']:.1f}% | |
| """ | |
| return ( | |
| "Session completed!", # current_message_display | |
| "✅ All messages verified", # classifier_decision_display | |
| "", # classifier_confidence_display | |
| "", # classifier_indicators_display | |
| "✅ Verification complete", # verification_progress | |
| stats_text, # session_stats_display | |
| gr.Row(visible=False), # correction_section | |
| "", # correction_notes (clear) | |
| message # status_message | |
| ) | |
| else: | |
| return ( | |
| gr.Textbox(value=""), # current_message_display (no change) | |
| gr.Markdown(value=""), # classifier_decision_display (no change) | |
| gr.Markdown(value=""), # classifier_confidence_display (no change) | |
| gr.Markdown(value=""), # classifier_indicators_display (no change) | |
| gr.Markdown(value=""), # verification_progress (no change) | |
| gr.Markdown(value=""), # session_stats_display (no change) | |
| gr.Row(visible=True), # correction_section (keep visible) | |
| notes, # correction_notes (keep) | |
| message # status_message | |
| ) | |
| def on_export_results(format_type): | |
| """Handle results export.""" | |
| success, message, file_path = controller.export_session_results(format_type) | |
| return message | |
| # Bind event handlers | |
| enhanced_dataset_interface.load( | |
| initialize_interface, | |
| outputs=[ | |
| dataset_selector, | |
| dataset_info_display, | |
| current_dataset_state, | |
| status_message | |
| ] | |
| ) | |
| dataset_selector.change( | |
| on_dataset_selection_change, | |
| inputs=[dataset_selector], | |
| outputs=[dataset_info_display, current_dataset_state] | |
| ) | |
| load_dataset_btn.click( | |
| on_load_dataset, | |
| inputs=[current_dataset_state], | |
| outputs=[verification_section, status_message] | |
| ) | |
| start_verification_btn.click( | |
| on_start_verification, | |
| inputs=[current_dataset_state, verifier_name_input], | |
| outputs=[ | |
| verification_session_state, | |
| message_review_area, | |
| current_message_display, | |
| classifier_decision_display, | |
| classifier_confidence_display, | |
| classifier_indicators_display, | |
| verification_progress, | |
| status_message | |
| ] | |
| ) | |
| correct_classification_btn.click( | |
| on_correct_classification, | |
| outputs=[ | |
| current_message_display, | |
| classifier_decision_display, | |
| classifier_confidence_display, | |
| classifier_indicators_display, | |
| verification_progress, | |
| session_stats_display, | |
| correction_section, | |
| status_message | |
| ] | |
| ) | |
| incorrect_classification_btn.click( | |
| on_incorrect_classification, | |
| outputs=[correction_section, status_message] | |
| ) | |
| submit_correction_btn.click( | |
| on_submit_correction, | |
| inputs=[correction_selector, correction_notes], | |
| outputs=[ | |
| current_message_display, | |
| classifier_decision_display, | |
| classifier_confidence_display, | |
| classifier_indicators_display, | |
| verification_progress, | |
| session_stats_display, | |
| correction_section, | |
| correction_notes, | |
| status_message | |
| ] | |
| ) | |
| export_csv_btn.click( | |
| lambda: on_export_results("csv"), | |
| outputs=[status_message] | |
| ) | |
| export_json_btn.click( | |
| lambda: on_export_results("json"), | |
| outputs=[status_message] | |
| ) | |
| export_xlsx_btn.click( | |
| lambda: on_export_results("xlsx"), | |
| outputs=[status_message] | |
| ) | |
| return enhanced_dataset_interface | |
| def create_manual_input_interface(model_overrides_state: Optional[gr.State] = None) -> gr.Blocks: | |
| """ | |
| Create manual input mode interface. | |
| Returns: | |
| Gradio Blocks component for manual input mode | |
| """ | |
| # Import the complete manual input interface | |
| from src.interface.manual_input_interface import create_manual_input_interface | |
| return create_manual_input_interface(model_overrides_state=model_overrides_state) | |
| def create_file_upload_interface(model_overrides_state: Optional[gr.State] = None) -> gr.Blocks: | |
| """ | |
| Create file upload mode interface. | |
| Returns: | |
| Gradio Blocks component for file upload mode | |
| """ | |
| # Import the complete file upload interface | |
| from src.interface.file_upload_interface import create_file_upload_interface | |
| return create_file_upload_interface(model_overrides_state=model_overrides_state) | |
| def create_enhanced_verification_app() -> gr.Blocks: | |
| """ | |
| Create the complete enhanced verification application. | |
| Returns: | |
| Gradio Blocks application with mode selection and all verification modes | |
| """ | |
| # Initialize store | |
| store = JSONVerificationStore() | |
| with gr.Blocks(title="Enhanced Verification Modes") as app: | |
| # Application state | |
| current_mode = gr.State(value=None) | |
| current_session = gr.State(value=None) | |
| # Mode selection interface | |
| mode_selection = EnhancedVerificationUIComponents.create_mode_selection_interface() | |
| # Individual mode interfaces (initially hidden) | |
| enhanced_dataset_interface = gr.Row(visible=False) | |
| with enhanced_dataset_interface: | |
| enhanced_dataset_ui = EnhancedVerificationUIComponents.create_enhanced_dataset_interface_with_handlers() | |
| manual_input_interface = gr.Row(visible=False) | |
| with manual_input_interface: | |
| manual_input_ui = EnhancedVerificationUIComponents.create_manual_input_interface() | |
| file_upload_interface = gr.Row(visible=False) | |
| with file_upload_interface: | |
| file_upload_ui = EnhancedVerificationUIComponents.create_file_upload_interface() | |
| return app |