File size: 10,275 Bytes
27762e4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
Statistics and Monitoring Display Module

This module provides a visual dashboard for viewing application statistics,
performance metrics, and system information.
"""

import panel as pn
import param
from utils.app_context import AppContext
from utils.performance_monitor import performance_monitor


class StatsDisplay(param.Parameterized):
    """
    Reactive statistics dashboard that displays real-time application metrics.

    Uses Panel's param.depends to automatically update when state changes.
    """

    # Refresh trigger
    refresh_trigger = param.Integer(default=0)

    def __init__(self, context: AppContext, **params):
        super().__init__(**params)
        self.context = context

    @param.depends('refresh_trigger')
    def _state_stats_panel(self):
        """Display state statistics (reactive to refresh trigger)."""
        stats = self.context.state.get_stats()

        html_content = f"""
        <div style="padding: 15px; background: #f8f9fa; border-radius: 8px; margin: 10px 0;">
            <h3 style="margin-top: 0; color: #2c3e50;">State Statistics</h3>
            <table style="width: 100%; border-collapse: collapse;">
                <tr style="border-bottom: 1px solid #dee2e6;">
                    <td style="padding: 8px; font-weight: bold;">Event Lists:</td>
                    <td style="padding: 8px;">{self.context.state.event_data_count}</td>
                </tr>
                <tr style="border-bottom: 1px solid #dee2e6;">
                    <td style="padding: 8px; font-weight: bold;">Light Curves:</td>
                    <td style="padding: 8px;">{self.context.state.light_curve_count}</td>
                </tr>
                <tr style="border-bottom: 1px solid #dee2e6;">
                    <td style="padding: 8px; font-weight: bold;">Timeseries:</td>
                    <td style="padding: 8px;">{self.context.state.timeseries_count}</td>
                </tr>
                <tr style="border-bottom: 1px solid #dee2e6;">
                    <td style="padding: 8px; font-weight: bold;">Total Additions:</td>
                    <td style="padding: 8px;">{stats.get('total_additions', 0)}</td>
                </tr>
                <tr style="border-bottom: 1px solid #dee2e6;">
                    <td style="padding: 8px; font-weight: bold;">Total Removals:</td>
                    <td style="padding: 8px;">{stats.get('total_removals', 0)}</td>
                </tr>
                <tr style="border-bottom: 1px solid #dee2e6;">
                    <td style="padding: 8px; font-weight: bold;">Evictions (LRU):</td>
                    <td style="padding: 8px;">{stats.get('total_evictions', 0)}</td>
                </tr>
                <tr>
                    <td style="padding: 8px; font-weight: bold;">Last Operation:</td>
                    <td style="padding: 8px; font-style: italic; color: #6c757d;">
                        {self.context.state.last_operation or 'None'}
                    </td>
                </tr>
            </table>
        </div>
        """
        return pn.pane.HTML(html_content, sizing_mode='stretch_width')

    @param.depends('refresh_trigger')
    def _memory_stats_panel(self):
        """Display memory statistics (reactive to refresh trigger)."""
        mem_info = self.context.state.get_system_memory_info()

        if not mem_info:
            return pn.pane.Markdown("*Memory information unavailable*")

        # Convert MB to GB
        total_gb = mem_info.get('total_mb', 0) / 1024
        available_gb = mem_info.get('available_mb', 0) / 1024
        limit_gb = mem_info.get('allocated_limit_mb', 0) / 1024
        app_usage_gb = self.context.state.memory_usage_mb / 1024

        html_content = f"""
        <div style="padding: 15px; background: #f8f9fa; border-radius: 8px; margin: 10px 0;">
            <h3 style="margin-top: 0; color: #2c3e50;">Memory Usage</h3>
            <table style="width: 100%; border-collapse: collapse;">
                <tr style="border-bottom: 1px solid #dee2e6;">
                    <td style="padding: 8px; font-weight: bold;">Total System RAM:</td>
                    <td style="padding: 8px;">{total_gb:.2f} GB</td>
                </tr>
                <tr style="border-bottom: 1px solid #dee2e6;">
                    <td style="padding: 8px; font-weight: bold;">Available:</td>
                    <td style="padding: 8px;">{available_gb:.2f} GB</td>
                </tr>
                <tr style="border-bottom: 1px solid #dee2e6;">
                    <td style="padding: 8px; font-weight: bold;">App Limit (80%):</td>
                    <td style="padding: 8px;">{limit_gb:.2f} GB</td>
                </tr>
                <tr>
                    <td style="padding: 8px; font-weight: bold;">Current App Usage:</td>
                    <td style="padding: 8px;">{app_usage_gb:.3f} GB</td>
                </tr>
            </table>
        </div>
        """
        return pn.pane.HTML(html_content, sizing_mode='stretch_width')

    @param.depends('refresh_trigger')
    def _performance_stats_panel(self):
        """Display performance statistics (reactive to refresh trigger)."""
        summary = performance_monitor.get_summary()

        html_content = f"""
        <div style="padding: 15px; background: #f8f9fa; border-radius: 8px; margin: 10px 0;">
            <h3 style="margin-top: 0; color: #2c3e50;">Performance Metrics</h3>
            <table style="width: 100%; border-collapse: collapse;">
                <tr style="border-bottom: 1px solid #dee2e6;">
                    <td style="padding: 8px; font-weight: bold;">Total Operations:</td>
                    <td style="padding: 8px;">{summary.get('total_operations', 0)}</td>
                </tr>
                <tr style="border-bottom: 1px solid #dee2e6;">
                    <td style="padding: 8px; font-weight: bold;">Unique Operations:</td>
                    <td style="padding: 8px;">{summary.get('unique_operations', 0)}</td>
                </tr>
                <tr style="border-bottom: 1px solid #dee2e6;">
                    <td style="padding: 8px; font-weight: bold;">Avg Duration:</td>
                    <td style="padding: 8px;">{summary.get('avg_duration_ms', 0):.2f} ms</td>
                </tr>
                <tr style="border-bottom: 1px solid #dee2e6;">
                    <td style="padding: 8px; font-weight: bold;">Success Rate:</td>
                    <td style="padding: 8px;">{summary.get('success_rate', 0):.1f}%</td>
                </tr>
                <tr style="border-bottom: 1px solid #dee2e6;">
                    <td style="padding: 8px; font-weight: bold;">Most Frequent:</td>
                    <td style="padding: 8px; font-style: italic;">{summary.get('most_frequent') or 'N/A'}</td>
                </tr>
                <tr>
                    <td style="padding: 8px; font-weight: bold;">Slowest Operation:</td>
                    <td style="padding: 8px; font-style: italic; color: #e74c3c;">
                        {summary.get('slowest') or 'N/A'}
                    </td>
                </tr>
            </table>
        </div>
        """
        return pn.pane.HTML(html_content, sizing_mode='stretch_width')

    @param.depends('refresh_trigger')
    def _recent_operations_panel(self):
        """Display recent operations list (reactive to refresh trigger)."""
        recent = performance_monitor.get_recent_operations(limit=10)

        if not recent:
            return pn.pane.Markdown("*No recent operations*")

        rows = ""
        for op in reversed(recent):  # Most recent first
            status_icon = "[OK]" if op.success else "[X]"
            status_color = "#28a745" if op.success else "#dc3545"
            time_str = op.start_time.strftime("%H:%M:%S")

            rows += f"""
            <tr style="border-bottom: 1px solid #dee2e6;">
                <td style="padding: 6px; font-size: 20px;">{status_icon}</td>
                <td style="padding: 6px;">{time_str}</td>
                <td style="padding: 6px;">{op.operation_name}</td>
                <td style="padding: 6px; text-align: right;">{op.duration_ms:.2f} ms</td>
            </tr>
            """

        html_content = f"""
        <div style="padding: 15px; background: #f8f9fa; border-radius: 8px; margin: 10px 0;">
            <h3 style="margin-top: 0; color: #2c3e50;">Recent Operations</h3>
            <table style="width: 100%; border-collapse: collapse; font-size: 14px;">
                <thead>
                    <tr style="background: #e9ecef; border-bottom: 2px solid #dee2e6;">
                        <th style="padding: 8px; text-align: left;">Status</th>
                        <th style="padding: 8px; text-align: left;">Time</th>
                        <th style="padding: 8px; text-align: left;">Operation</th>
                        <th style="padding: 8px; text-align: right;">Duration</th>
                    </tr>
                </thead>
                <tbody>
                    {rows}
                </tbody>
            </table>
        </div>
        """
        return pn.pane.HTML(html_content, sizing_mode='stretch_width')

    def view(self):
        """Create the complete stats dashboard view."""
        # Refresh button
        refresh_button = pn.widgets.Button(
            name="Refresh Stats",
            button_type="primary",
            width=200
        )

        def on_refresh(event):
            self.refresh_trigger += 1

        refresh_button.on_click(on_refresh)

        # Create layout (no duplicate header - header is set in sidebar.py)
        dashboard = pn.Column(
            pn.Row(refresh_button, align='center'),
            pn.layout.Divider(),
            self._state_stats_panel,
            self._memory_stats_panel,
            self._performance_stats_panel,
            self._recent_operations_panel,
            sizing_mode='stretch_width'
        )

        return dashboard


def create_stats_dashboard(context: AppContext):
    """
    Factory function to create the stats dashboard.

    Args:
        context: Application context

    Returns:
        Panel dashboard component
    """
    stats_display = StatsDisplay(context)
    return stats_display.view()