# 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 ) @dataclass 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" ] } } @staticmethod 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 @staticmethod 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 = """
""" 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"""

{mode_info['icon']} {mode_info['title']} - {session.dataset_name}

{time_ago}
Progress: {session.verified_count}/{session.total_messages} {progress_pct:.0f}%
✓ Correct: {session.correct_count} ✗ Incorrect: {session.incorrect_count} 📊 Accuracy: {accuracy:.1f}%
Session ID: {session.session_id[:8]}...
""" html += """

💡 Tip: Click on a session above to select it, then click "Resume Selected Session" to continue where you left off.

""" return html @staticmethod 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" @staticmethod 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"""

❌ Error Loading Sessions

Could not load incomplete sessions: {str(e)}

""" return False, [], error_html @staticmethod 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 @staticmethod 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 @staticmethod 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 @staticmethod 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) @staticmethod 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