File size: 13,921 Bytes
7bbd836
 
 
 
 
 
 
 
 
 
713cfc8
 
7bbd836
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
# enhanced_progress_components.py
"""
Enhanced Progress UI Components for Verification Modes.

Provides Gradio components for real-time progress tracking, statistics display,
and session management across all verification modes.

Requirements: 9.1, 9.2, 9.3, 9.4, 9.5
"""

from __future__ import annotations

import gradio as gr
from typing import Tuple, Dict, Any, Optional
from datetime import datetime, timedelta

from src.core.enhanced_progress_tracker import (
    EnhancedProgressTracker,
    VerificationMode,
    ProgressDisplayFormatter
)


class EnhancedProgressComponents:
    """Enhanced progress tracking UI components."""
    
    @staticmethod
    def create_progress_panel() -> Tuple[gr.Component, gr.Component, gr.Component, gr.Component, gr.Component]:
        """
        Create comprehensive progress tracking panel.
        
        Returns:
            Tuple of (progress_display, accuracy_display, speed_display, error_display, time_display)
        """
        # Main progress display with bar and position
        progress_display = gr.HTML(
            value=EnhancedProgressComponents._get_initial_progress_html(),
            label="Progress Tracking"
        )
        
        # Running accuracy display
        accuracy_display = gr.Markdown(
            value="🎯 Current Accuracy: No verifications yet",
            label="Accuracy"
        )
        
        # Processing speed display (for batch mode)
        speed_display = gr.Markdown(
            value="",
            label="Processing Speed",
            visible=False
        )
        
        # Error count and status display
        error_display = gr.Markdown(
            value="",
            label="Error Status",
            visible=False
        )
        
        # Time tracking display
        time_display = gr.Markdown(
            value="⏱️ Time: Ready to start",
            label="Session Time"
        )
        
        return progress_display, accuracy_display, speed_display, error_display, time_display
    
    @staticmethod
    def create_compact_progress_panel() -> gr.Component:
        """
        Create compact progress panel for smaller interfaces.
        
        Returns:
            Single HTML component with comprehensive progress info
        """
        return gr.HTML(
            value=EnhancedProgressComponents._get_initial_progress_html(),
            label="Session Progress"
        )
    
    @staticmethod
    def create_session_controls() -> Tuple[gr.Component, gr.Component, gr.Component]:
        """
        Create session control buttons.
        
        Returns:
            Tuple of (pause_btn, resume_btn, reset_btn)
        """
        pause_btn = gr.Button(
            "⏸️ Pause Session",
            variant="secondary",
            size="sm",
            visible=False
        )
        
        resume_btn = gr.Button(
            "▶️ Resume Session", 
            variant="primary",
            size="sm",
            visible=False
        )
        
        reset_btn = gr.Button(
            "🔄 Reset Progress",
            variant="stop",
            size="sm"
        )
        
        return pause_btn, resume_btn, reset_btn
    
    @staticmethod
    def update_progress_displays(
        tracker: EnhancedProgressTracker,
        use_compact: bool = False
    ) -> Tuple[str, str, str, str, str, bool, bool]:
        """
        Update all progress displays based on tracker state.
        
        Args:
            tracker: Progress tracker instance
            use_compact: Whether to use compact display format
            
        Returns:
            Tuple of display values and visibility states
        """
        if use_compact:
            # Return single HTML component
            progress_html = ProgressDisplayFormatter.create_progress_panel_html(tracker)
            return (
                progress_html,  # progress_display
                "",             # accuracy_display (unused in compact)
                "",             # speed_display (unused in compact)
                "",             # error_display (unused in compact)
                "",             # time_display (unused in compact)
                False,          # speed_visible
                False           # error_visible
            )
        else:
            # Return individual components
            progress_html = ProgressDisplayFormatter.create_progress_panel_html(tracker)
            accuracy_display = tracker.get_accuracy_display()
            speed_display = tracker.get_processing_speed_display()
            error_display = tracker.get_error_display()
            time_display = tracker.get_time_tracking_display()
            
            # Determine visibility
            speed_visible = tracker.mode == VerificationMode.FILE_UPLOAD and tracker.stats.processing_speed > 0
            error_visible = tracker.error_tracker.error_count > 0
            
            return (
                progress_html,
                accuracy_display,
                speed_display,
                error_display,
                time_display,
                speed_visible,
                error_visible
            )
    
    @staticmethod
    def update_session_controls(
        tracker: EnhancedProgressTracker
    ) -> Tuple[bool, bool, bool]:
        """
        Update session control button visibility.
        
        Args:
            tracker: Progress tracker instance
            
        Returns:
            Tuple of (pause_visible, resume_visible, reset_visible)
        """
        session_active = tracker.stats.start_time is not None
        is_paused = tracker.is_paused
        
        pause_visible = session_active and not is_paused
        resume_visible = session_active and is_paused
        reset_visible = session_active
        
        return pause_visible, resume_visible, reset_visible
    
    @staticmethod
    def create_statistics_summary() -> gr.Component:
        """
        Create detailed statistics summary component.
        
        Returns:
            Gradio component for statistics display
        """
        return gr.Markdown(
            value=EnhancedProgressComponents._get_initial_stats_summary(),
            label="Session Statistics"
        )
    
    @staticmethod
    def update_statistics_summary(tracker: EnhancedProgressTracker) -> str:
        """
        Update statistics summary display.
        
        Args:
            tracker: Progress tracker instance
            
        Returns:
            Formatted statistics summary
        """
        stats = tracker.get_comprehensive_stats()
        
        # Calculate additional metrics
        total_verified = stats["correct_count"] + stats["incorrect_count"]
        
        summary = f"""
### 📊 Session Statistics

**Progress Overview:**
- Messages Processed: {stats['processed_messages']}/{stats['total_messages']} ({stats['completion_percentage']:.1f}%)
- Verifications Complete: {total_verified}
- Current Accuracy: {stats['accuracy']:.1f}%

**Performance Metrics:**
- Correct Classifications: {stats['correct_count']}
- Incorrect Classifications: {stats['incorrect_count']}
"""
        
        if tracker.mode == VerificationMode.FILE_UPLOAD:
            summary += f"- Processing Speed: {stats['processing_speed']:.1f} messages/min\n"
        
        if stats["average_processing_time"] > 0:
            summary += f"- Average Time per Message: {stats['average_processing_time']:.1f}s\n"
        
        summary += f"""
**Session Timing:**
- Elapsed Time: {EnhancedProgressComponents._format_duration(stats['elapsed_time'])}
"""
        
        if stats["estimated_remaining"]:
            remaining_str = EnhancedProgressComponents._format_duration(stats["estimated_remaining"])
            summary += f"- Estimated Remaining: {remaining_str}\n"
        
        if stats["is_paused"]:
            summary += "- Status: ⏸️ **Paused**\n"
        
        if stats["error_count"] > 0:
            summary += f"""
**Error Information:**
- Total Errors: {stats['error_count']}
- Can Continue: {'✅ Yes' if stats['can_continue'] else '❌ No'}
"""
        
        return summary
    
    @staticmethod
    def create_error_details_panel() -> gr.Component:
        """
        Create detailed error information panel.
        
        Returns:
            Gradio component for error details
        """
        return gr.Markdown(
            value="",
            label="Error Details",
            visible=False
        )
    
    @staticmethod
    def update_error_details(tracker: EnhancedProgressTracker) -> Tuple[str, bool]:
        """
        Update error details panel.
        
        Args:
            tracker: Progress tracker instance
            
        Returns:
            Tuple of (error_details, visible)
        """
        if tracker.error_tracker.error_count == 0:
            return "", False
        
        recent_errors = tracker.error_tracker.get_recent_errors(5)
        
        details = f"""
### ⚠️ Error Details

**Total Errors:** {tracker.error_tracker.error_count}
**Can Continue Processing:** {'✅ Yes' if tracker.error_tracker.can_continue else '❌ No'}

**Recent Errors:**
"""
        
        for i, (error_msg, timestamp) in enumerate(recent_errors, 1):
            time_str = timestamp.strftime("%H:%M:%S")
            details += f"{i}. `{time_str}` - {error_msg}\n"
        
        if tracker.error_tracker.error_count > len(recent_errors):
            details += f"\n*... and {tracker.error_tracker.error_count - len(recent_errors)} more errors*"
        
        return details, True
    
    @staticmethod
    def _get_initial_progress_html() -> str:
        """Get initial progress HTML."""
        return """
        <div style="font-family: system-ui; padding: 1rem; background: #f9fafb; border-radius: 8px; border: 1px solid #e5e7eb;">
            <div style="text-align: center; color: #6b7280;">
                <div style="font-size: 1.125rem; margin-bottom: 0.5rem;">📊 Ready to Start</div>
                <div style="width: 100%; background-color: #e5e7eb; border-radius: 4px; height: 8px;">
                    <div style="width: 0%; background-color: #3b82f6; border-radius: 4px; height: 8px;"></div>
                </div>
                <div style="margin-top: 0.5rem; font-size: 0.875rem;">Select a dataset or enter messages to begin</div>
            </div>
        </div>
        """
    
    @staticmethod
    def _get_initial_stats_summary() -> str:
        """Get initial statistics summary."""
        return """
### 📊 Session Statistics

**Progress Overview:**
- Messages Processed: 0/0 (0%)
- Verifications Complete: 0
- Current Accuracy: 0%

**Performance Metrics:**
- Correct Classifications: 0
- Incorrect Classifications: 0

**Session Timing:**
- Elapsed Time: 0s
- Status: Ready to start
"""
    
    @staticmethod
    def _format_duration(seconds: float) -> str:
        """Format duration in seconds to human-readable string."""
        if seconds is None or seconds <= 0:
            return "0s"
        
        total_seconds = int(seconds)
        
        if total_seconds < 60:
            return f"{total_seconds}s"
        elif total_seconds < 3600:
            minutes = total_seconds // 60
            seconds = total_seconds % 60
            return f"{minutes}m {seconds}s"
        else:
            hours = total_seconds // 3600
            minutes = (total_seconds % 3600) // 60
            return f"{hours}h {minutes}m"


class ProgressTrackingMixin:
    """Mixin class for adding progress tracking to verification interfaces."""
    
    def __init__(self, mode: VerificationMode):
        """Initialize progress tracking."""
        self.progress_tracker = EnhancedProgressTracker(mode)
        self.progress_components = None
    
    def setup_progress_tracking(self, total_messages: int = 0) -> None:
        """
        Setup progress tracking for a session.
        
        Args:
            total_messages: Total number of messages to process
        """
        self.progress_tracker = EnhancedProgressTracker(self.progress_tracker.mode, total_messages)
        self.progress_tracker.start_session()
    
    def record_verification_with_timing(self, is_correct: bool, start_time: datetime = None) -> None:
        """
        Record verification with automatic timing.
        
        Args:
            is_correct: Whether verification was correct
            start_time: When processing started (for timing calculation)
        """
        processing_time = None
        if start_time:
            processing_time = (datetime.now() - start_time).total_seconds()
        
        self.progress_tracker.record_verification(is_correct, processing_time)
    
    def get_progress_updates(self, use_compact: bool = False) -> Tuple:
        """
        Get all progress display updates.
        
        Args:
            use_compact: Whether to use compact display
            
        Returns:
            Tuple of display updates
        """
        return EnhancedProgressComponents.update_progress_displays(
            self.progress_tracker, use_compact
        )
    
    def handle_session_pause(self) -> Tuple[bool, bool, bool]:
        """
        Handle session pause and return control states.
        
        Returns:
            Tuple of control button visibility states
        """
        self.progress_tracker.pause_session()
        return EnhancedProgressComponents.update_session_controls(self.progress_tracker)
    
    def handle_session_resume(self) -> Tuple[bool, bool, bool]:
        """
        Handle session resume and return control states.
        
        Returns:
            Tuple of control button visibility states
        """
        self.progress_tracker.resume_session()
        return EnhancedProgressComponents.update_session_controls(self.progress_tracker)