setu-trafficmonitor 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. setu_trafficmonitor-2.0.0.dist-info/LICENSE +21 -0
  2. setu_trafficmonitor-2.0.0.dist-info/METADATA +401 -0
  3. setu_trafficmonitor-2.0.0.dist-info/RECORD +42 -0
  4. setu_trafficmonitor-2.0.0.dist-info/WHEEL +5 -0
  5. setu_trafficmonitor-2.0.0.dist-info/top_level.txt +1 -0
  6. trafficmonitor/__init__.py +11 -0
  7. trafficmonitor/admin.py +217 -0
  8. trafficmonitor/analytics/__init__.py +0 -0
  9. trafficmonitor/analytics/enhanced_queries.py +286 -0
  10. trafficmonitor/analytics/serializers.py +238 -0
  11. trafficmonitor/analytics/tests.py +757 -0
  12. trafficmonitor/analytics/urls.py +18 -0
  13. trafficmonitor/analytics/views.py +694 -0
  14. trafficmonitor/apps.py +7 -0
  15. trafficmonitor/circuit_breaker.py +63 -0
  16. trafficmonitor/conf.py +154 -0
  17. trafficmonitor/dashboard_security.py +111 -0
  18. trafficmonitor/db_utils.py +37 -0
  19. trafficmonitor/exceptions.py +93 -0
  20. trafficmonitor/health.py +66 -0
  21. trafficmonitor/load_test.py +423 -0
  22. trafficmonitor/load_test_api.py +307 -0
  23. trafficmonitor/management/__init__.py +1 -0
  24. trafficmonitor/management/commands/__init__.py +1 -0
  25. trafficmonitor/management/commands/cleanup_request_logs.py +77 -0
  26. trafficmonitor/middleware.py +383 -0
  27. trafficmonitor/migrations/0001_initial.py +93 -0
  28. trafficmonitor/migrations/__init__.py +0 -0
  29. trafficmonitor/models.py +206 -0
  30. trafficmonitor/monitoring.py +104 -0
  31. trafficmonitor/permissions.py +64 -0
  32. trafficmonitor/security.py +180 -0
  33. trafficmonitor/settings_production.py +105 -0
  34. trafficmonitor/static/analytics/css/dashboard.css +99 -0
  35. trafficmonitor/static/analytics/js/dashboard-production.js +339 -0
  36. trafficmonitor/static/analytics/js/dashboard-v2.js +697 -0
  37. trafficmonitor/static/analytics/js/dashboard.js +693 -0
  38. trafficmonitor/tasks.py +137 -0
  39. trafficmonitor/templates/analytics/dashboard.html +500 -0
  40. trafficmonitor/tests.py +246 -0
  41. trafficmonitor/views.py +3 -0
  42. trafficmonitor/websocket_consumers.py +128 -0
@@ -0,0 +1,339 @@
1
+ /**
2
+ * Production-ready Analytics Dashboard JavaScript
3
+ * Features: Error handling, retry logic, performance optimization, accessibility
4
+ */
5
+
6
+ class DashboardManager {
7
+ constructor() {
8
+ this.charts = new Map();
9
+ this.retryCount = 0;
10
+ this.maxRetries = 3;
11
+ this.csrfToken = this.getCSRFToken();
12
+ this.abortController = null;
13
+
14
+ // Debounced filter function
15
+ this.applyFiltersDebounced = this.debounce(this.applyFilters.bind(this), 300);
16
+
17
+ this.init();
18
+ }
19
+
20
+ init() {
21
+ this.setupEventListeners();
22
+ this.loadDashboard();
23
+ this.setupPerformanceMonitoring();
24
+ }
25
+
26
+ setupEventListeners() {
27
+ // Filter change events
28
+ document.getElementById('rangeFilter')?.addEventListener('change', () => {
29
+ this.handleRangeChange();
30
+ });
31
+
32
+ // Keyboard accessibility
33
+ document.addEventListener('keydown', (e) => {
34
+ if (e.key === 'r' && e.ctrlKey) {
35
+ e.preventDefault();
36
+ this.refreshDashboard();
37
+ }
38
+ });
39
+
40
+ // Window resize handling
41
+ window.addEventListener('resize', this.debounce(() => {
42
+ this.resizeCharts();
43
+ }, 250));
44
+
45
+ // Visibility change (pause updates when tab not visible)
46
+ document.addEventListener('visibilitychange', () => {
47
+ if (document.hidden) {
48
+ this.pauseUpdates();
49
+ } else {
50
+ this.resumeUpdates();
51
+ }
52
+ });
53
+ }
54
+
55
+ async loadDashboard() {
56
+ try {
57
+ this.showLoading();
58
+ const data = await this.fetchAnalyticsData();
59
+ this.renderDashboard(data);
60
+ this.hideLoading();
61
+ } catch (error) {
62
+ this.handleError(error);
63
+ }
64
+ }
65
+
66
+ async fetchAnalyticsData(filters = {}) {
67
+ // Cancel previous request
68
+ if (this.abortController) {
69
+ this.abortController.abort();
70
+ }
71
+
72
+ this.abortController = new AbortController();
73
+
74
+ const params = new URLSearchParams(filters);
75
+ const url = `/api/analytics/overview/?${params}`;
76
+
77
+ try {
78
+ const response = await fetch(url, {
79
+ method: 'GET',
80
+ headers: {
81
+ 'X-CSRFToken': this.csrfToken,
82
+ 'Content-Type': 'application/json',
83
+ },
84
+ signal: this.abortController.signal,
85
+ timeout: 30000, // 30 second timeout
86
+ });
87
+
88
+ if (!response.ok) {
89
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
90
+ }
91
+
92
+ const data = await response.json();
93
+ this.retryCount = 0; // Reset on success
94
+ return data;
95
+
96
+ } catch (error) {
97
+ if (error.name === 'AbortError') {
98
+ throw new Error('Request was cancelled');
99
+ }
100
+
101
+ // Retry logic
102
+ if (this.retryCount < this.maxRetries) {
103
+ this.retryCount++;
104
+ console.warn(`Request failed, retrying (${this.retryCount}/${this.maxRetries}):`, error);
105
+ await this.delay(1000 * this.retryCount); // Exponential backoff
106
+ return this.fetchAnalyticsData(filters);
107
+ }
108
+
109
+ throw error;
110
+ }
111
+ }
112
+
113
+ renderDashboard(data) {
114
+ try {
115
+ this.updateMetrics(data.metrics);
116
+ this.renderCharts(data.charts);
117
+ this.updateLastRefresh();
118
+ } catch (error) {
119
+ console.error('Error rendering dashboard:', error);
120
+ this.showError('Failed to render dashboard data');
121
+ }
122
+ }
123
+
124
+ updateMetrics(metrics) {
125
+ const elements = {
126
+ 'totalRequests': metrics.total_requests,
127
+ 'readOps': metrics.read_operations,
128
+ 'writeOps': metrics.write_operations,
129
+ 'deleteOps': metrics.delete_operations,
130
+ 'errorRate': `${metrics.error_rate}%`,
131
+ 'avgResponseTime': `${metrics.avg_response_time}ms`,
132
+ };
133
+
134
+ Object.entries(elements).forEach(([id, value]) => {
135
+ const element = document.getElementById(id);
136
+ if (element) {
137
+ this.animateValue(element, value);
138
+ }
139
+ });
140
+ }
141
+
142
+ renderCharts(chartData) {
143
+ // Destroy existing charts to prevent memory leaks
144
+ this.destroyCharts();
145
+
146
+ // Render new charts with error handling
147
+ this.renderChart('requestsOverTime', chartData.requests_over_time, 'line');
148
+ this.renderChart('statusCodes', chartData.status_codes, 'doughnut');
149
+ this.renderChart('methods', chartData.methods, 'bar');
150
+ this.renderChart('errorTrend', chartData.error_trend, 'line');
151
+ this.renderChart('slowestEndpoints', chartData.slowest_endpoints, 'horizontalBar');
152
+ }
153
+
154
+ renderChart(canvasId, data, type) {
155
+ try {
156
+ const canvas = document.getElementById(canvasId);
157
+ if (!canvas) {
158
+ console.warn(`Canvas element ${canvasId} not found`);
159
+ return;
160
+ }
161
+
162
+ const ctx = canvas.getContext('2d');
163
+ const config = this.getChartConfig(type, data);
164
+
165
+ const chart = new Chart(ctx, config);
166
+ this.charts.set(canvasId, chart);
167
+
168
+ } catch (error) {
169
+ console.error(`Error rendering chart ${canvasId}:`, error);
170
+ this.showChartError(canvasId);
171
+ }
172
+ }
173
+
174
+ getChartConfig(type, data) {
175
+ const baseConfig = {
176
+ responsive: true,
177
+ maintainAspectRatio: false,
178
+ plugins: {
179
+ legend: {
180
+ display: true,
181
+ position: 'top',
182
+ },
183
+ tooltip: {
184
+ mode: 'index',
185
+ intersect: false,
186
+ },
187
+ },
188
+ interaction: {
189
+ mode: 'nearest',
190
+ axis: 'x',
191
+ intersect: false,
192
+ },
193
+ };
194
+
195
+ // Chart-specific configurations
196
+ switch (type) {
197
+ case 'line':
198
+ return {
199
+ type: 'line',
200
+ data: data,
201
+ options: {
202
+ ...baseConfig,
203
+ scales: {
204
+ x: {
205
+ type: 'time',
206
+ time: {
207
+ displayFormats: {
208
+ hour: 'MMM dd, HH:mm',
209
+ day: 'MMM dd',
210
+ },
211
+ },
212
+ },
213
+ y: {
214
+ beginAtZero: true,
215
+ },
216
+ },
217
+ },
218
+ };
219
+
220
+ case 'doughnut':
221
+ return {
222
+ type: 'doughnut',
223
+ data: data,
224
+ options: {
225
+ ...baseConfig,
226
+ cutout: '60%',
227
+ },
228
+ };
229
+
230
+ default:
231
+ return {
232
+ type: type,
233
+ data: data,
234
+ options: baseConfig,
235
+ };
236
+ }
237
+ }
238
+
239
+ // Utility methods
240
+ getCSRFToken() {
241
+ const token = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
242
+ if (!token) {
243
+ console.warn('CSRF token not found');
244
+ }
245
+ return token;
246
+ }
247
+
248
+ debounce(func, wait) {
249
+ let timeout;
250
+ return function executedFunction(...args) {
251
+ const later = () => {
252
+ clearTimeout(timeout);
253
+ func(...args);
254
+ };
255
+ clearTimeout(timeout);
256
+ timeout = setTimeout(later, wait);
257
+ };
258
+ }
259
+
260
+ delay(ms) {
261
+ return new Promise(resolve => setTimeout(resolve, ms));
262
+ }
263
+
264
+ animateValue(element, newValue) {
265
+ element.style.opacity = '0.5';
266
+ setTimeout(() => {
267
+ element.textContent = newValue;
268
+ element.style.opacity = '1';
269
+ }, 150);
270
+ }
271
+
272
+ showLoading() {
273
+ document.getElementById('loadingIndicator')?.classList.remove('hidden');
274
+ document.getElementById('dashboardContent')?.classList.add('hidden');
275
+ }
276
+
277
+ hideLoading() {
278
+ document.getElementById('loadingIndicator')?.classList.add('hidden');
279
+ document.getElementById('dashboardContent')?.classList.remove('hidden');
280
+ }
281
+
282
+ showError(message) {
283
+ const errorDiv = document.createElement('div');
284
+ errorDiv.className = 'error-state';
285
+ errorDiv.innerHTML = `
286
+ <i class="fas fa-exclamation-triangle text-4xl mb-4"></i>
287
+ <h3 class="text-xl font-semibold mb-2">Error Loading Dashboard</h3>
288
+ <p>${message}</p>
289
+ <button onclick="dashboard.refreshDashboard()" class="mt-4 px-4 py-2 bg-blue-600 text-white rounded">
290
+ Retry
291
+ </button>
292
+ `;
293
+
294
+ document.getElementById('dashboardContent').innerHTML = '';
295
+ document.getElementById('dashboardContent').appendChild(errorDiv);
296
+ this.hideLoading();
297
+ }
298
+
299
+ destroyCharts() {
300
+ this.charts.forEach(chart => {
301
+ chart.destroy();
302
+ });
303
+ this.charts.clear();
304
+ }
305
+
306
+ resizeCharts() {
307
+ this.charts.forEach(chart => {
308
+ chart.resize();
309
+ });
310
+ }
311
+
312
+ refreshDashboard() {
313
+ this.loadDashboard();
314
+ }
315
+
316
+ setupPerformanceMonitoring() {
317
+ // Monitor performance
318
+ if ('performance' in window) {
319
+ window.addEventListener('load', () => {
320
+ const loadTime = performance.now();
321
+ console.log(`Dashboard loaded in ${loadTime.toFixed(2)}ms`);
322
+ });
323
+ }
324
+ }
325
+ }
326
+
327
+ // Initialize dashboard when DOM is ready
328
+ document.addEventListener('DOMContentLoaded', () => {
329
+ window.dashboard = new DashboardManager();
330
+ });
331
+
332
+ // Global functions for backward compatibility
333
+ function applyFilters() {
334
+ window.dashboard?.applyFiltersDebounced();
335
+ }
336
+
337
+ function refreshDashboard() {
338
+ window.dashboard?.refreshDashboard();
339
+ }