@arela/uploader 0.2.13 → 1.0.0
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.
- package/.env.template +66 -0
- package/README.md +263 -62
- package/docs/API_ENDPOINTS_FOR_DETECTION.md +647 -0
- package/docs/QUICK_REFERENCE_API_DETECTION.md +264 -0
- package/docs/REFACTORING_SUMMARY_DETECT_PEDIMENTOS.md +200 -0
- package/package.json +3 -2
- package/scripts/cleanup-ds-store.js +109 -0
- package/scripts/cleanup-system-files.js +69 -0
- package/scripts/tests/phase-7-features.test.js +415 -0
- package/scripts/tests/signal-handling.test.js +275 -0
- package/scripts/tests/smart-watch-integration.test.js +554 -0
- package/scripts/tests/watch-service-integration.test.js +584 -0
- package/src/commands/UploadCommand.js +31 -4
- package/src/commands/WatchCommand.js +1342 -0
- package/src/config/config.js +270 -2
- package/src/document-type-shared.js +2 -0
- package/src/document-types/support-document.js +200 -0
- package/src/file-detection.js +9 -1
- package/src/index.js +163 -4
- package/src/services/AdvancedFilterService.js +505 -0
- package/src/services/AutoProcessingService.js +749 -0
- package/src/services/BenchmarkingService.js +381 -0
- package/src/services/DatabaseService.js +1019 -539
- package/src/services/ErrorMonitor.js +275 -0
- package/src/services/LoggingService.js +419 -1
- package/src/services/MonitoringService.js +401 -0
- package/src/services/PerformanceOptimizer.js +511 -0
- package/src/services/ReportingService.js +511 -0
- package/src/services/SignalHandler.js +255 -0
- package/src/services/SmartWatchDatabaseService.js +527 -0
- package/src/services/WatchService.js +783 -0
- package/src/services/upload/ApiUploadService.js +447 -3
- package/src/services/upload/MultiApiUploadService.js +233 -0
- package/src/services/upload/SupabaseUploadService.js +12 -5
- package/src/services/upload/UploadServiceFactory.js +24 -0
- package/src/utils/CleanupManager.js +262 -0
- package/src/utils/FileOperations.js +44 -0
- package/src/utils/WatchEventHandler.js +522 -0
- package/supabase/migrations/001_create_initial_schema.sql +366 -0
- package/supabase/migrations/002_align_with_arela_api_schema.sql +145 -0
- package/.envbackup +0 -37
- package/SUPABASE_UPLOAD_FIX.md +0 -157
- package/commands.md +0 -14
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ReportingService.js
|
|
3
|
+
* Phase 7 - Task 2: Reporting & Analytics
|
|
4
|
+
*
|
|
5
|
+
* Provides comprehensive reporting and analytics including:
|
|
6
|
+
* - Session reports
|
|
7
|
+
* - Batch reports
|
|
8
|
+
* - Error analysis reports
|
|
9
|
+
* - Export functionality (JSON, CSV, PDF-ready)
|
|
10
|
+
* - Statistics aggregation
|
|
11
|
+
*/
|
|
12
|
+
import fs from 'fs/promises';
|
|
13
|
+
import path from 'path';
|
|
14
|
+
|
|
15
|
+
class ReportingService {
|
|
16
|
+
constructor(logger) {
|
|
17
|
+
this.logger = logger;
|
|
18
|
+
this.reports = {};
|
|
19
|
+
this.sessions = [];
|
|
20
|
+
|
|
21
|
+
this.reportConfig = {
|
|
22
|
+
includeTimestamps: true,
|
|
23
|
+
includeMetrics: true,
|
|
24
|
+
includeErrors: true,
|
|
25
|
+
includeSummary: true,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Generate session report
|
|
31
|
+
*/
|
|
32
|
+
generateSessionReport(session) {
|
|
33
|
+
const report = {
|
|
34
|
+
type: 'session_report',
|
|
35
|
+
timestamp: new Date().toISOString(),
|
|
36
|
+
sessionId: session.sessionId,
|
|
37
|
+
duration: session.endTime
|
|
38
|
+
? session.endTime - session.startTime
|
|
39
|
+
: Date.now() - session.startTime,
|
|
40
|
+
startTime: new Date(session.startTime).toISOString(),
|
|
41
|
+
endTime: session.endTime ? new Date(session.endTime).toISOString() : null,
|
|
42
|
+
|
|
43
|
+
summary: {
|
|
44
|
+
totalFiles: session.fileCount || 0,
|
|
45
|
+
processedFiles: session.processedCount || 0,
|
|
46
|
+
successCount: session.successCount || 0,
|
|
47
|
+
errorCount: session.errorCount || 0,
|
|
48
|
+
skippedCount: session.skippedCount || 0,
|
|
49
|
+
successRate: this._calculateSuccessRate(session),
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
metrics: {
|
|
53
|
+
averageProcessingTime: this._calculateAvgTime(session),
|
|
54
|
+
filesPerSecond: this._calculateFilesPerSecond(session),
|
|
55
|
+
successRate: this._calculateSuccessRate(session),
|
|
56
|
+
errorRate: this._calculateErrorRate(session),
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
details: {
|
|
60
|
+
uploadedFiles: session.uploadedFiles || [],
|
|
61
|
+
failedFiles: session.failedFiles || [],
|
|
62
|
+
skippedFiles: session.skippedFiles || [],
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
errors: this._extractErrors(session),
|
|
66
|
+
|
|
67
|
+
tags: session.tags || [],
|
|
68
|
+
notes: session.notes || '',
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
this.reports[session.sessionId] = report;
|
|
72
|
+
this.sessions.push(report);
|
|
73
|
+
|
|
74
|
+
return report;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Generate batch report
|
|
79
|
+
*/
|
|
80
|
+
generateBatchReport(sessions) {
|
|
81
|
+
const report = {
|
|
82
|
+
type: 'batch_report',
|
|
83
|
+
timestamp: new Date().toISOString(),
|
|
84
|
+
batchId: `batch_${Date.now()}`,
|
|
85
|
+
|
|
86
|
+
summary: {
|
|
87
|
+
totalSessions: sessions.length,
|
|
88
|
+
totalFiles: 0,
|
|
89
|
+
totalProcessed: 0,
|
|
90
|
+
totalSuccess: 0,
|
|
91
|
+
totalErrors: 0,
|
|
92
|
+
totalSkipped: 0,
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
sessionMetrics: [],
|
|
96
|
+
aggregatedMetrics: {},
|
|
97
|
+
timelineData: [],
|
|
98
|
+
errorSummary: {},
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// Aggregate data from all sessions
|
|
102
|
+
for (const session of sessions) {
|
|
103
|
+
const sessionReport = this.generateSessionReport(session);
|
|
104
|
+
|
|
105
|
+
report.summary.totalFiles += sessionReport.summary.totalFiles;
|
|
106
|
+
report.summary.totalProcessed += sessionReport.summary.processedFiles;
|
|
107
|
+
report.summary.totalSuccess += sessionReport.summary.successCount;
|
|
108
|
+
report.summary.totalErrors += sessionReport.summary.errorCount;
|
|
109
|
+
report.summary.totalSkipped += sessionReport.summary.skippedCount;
|
|
110
|
+
|
|
111
|
+
report.sessionMetrics.push({
|
|
112
|
+
sessionId: session.sessionId,
|
|
113
|
+
duration: sessionReport.duration,
|
|
114
|
+
fileCount: sessionReport.summary.totalFiles,
|
|
115
|
+
successRate: sessionReport.metrics.successRate,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
report.timelineData.push({
|
|
119
|
+
timestamp: sessionReport.startTime,
|
|
120
|
+
filesProcessed: sessionReport.summary.processedFiles,
|
|
121
|
+
duration: sessionReport.duration,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// Aggregate errors by type
|
|
125
|
+
for (const error of sessionReport.errors) {
|
|
126
|
+
const errorType = error.type || 'unknown';
|
|
127
|
+
if (!report.errorSummary[errorType]) {
|
|
128
|
+
report.errorSummary[errorType] = { count: 0, examples: [] };
|
|
129
|
+
}
|
|
130
|
+
report.errorSummary[errorType].count++;
|
|
131
|
+
if (report.errorSummary[errorType].examples.length < 3) {
|
|
132
|
+
report.errorSummary[errorType].examples.push(error.message);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Calculate aggregated metrics
|
|
138
|
+
report.aggregatedMetrics = {
|
|
139
|
+
totalDuration: report.sessionMetrics.reduce(
|
|
140
|
+
(sum, s) => sum + s.duration,
|
|
141
|
+
0,
|
|
142
|
+
),
|
|
143
|
+
averageSessionDuration:
|
|
144
|
+
report.sessionMetrics.length > 0
|
|
145
|
+
? report.sessionMetrics.reduce((sum, s) => sum + s.duration, 0) /
|
|
146
|
+
report.sessionMetrics.length
|
|
147
|
+
: 0,
|
|
148
|
+
overallSuccessRate:
|
|
149
|
+
report.summary.totalProcessed > 0
|
|
150
|
+
? (
|
|
151
|
+
(report.summary.totalSuccess / report.summary.totalProcessed) *
|
|
152
|
+
100
|
|
153
|
+
).toFixed(2) + '%'
|
|
154
|
+
: 'N/A',
|
|
155
|
+
overallErrorRate:
|
|
156
|
+
report.summary.totalProcessed > 0
|
|
157
|
+
? (
|
|
158
|
+
(report.summary.totalErrors / report.summary.totalProcessed) *
|
|
159
|
+
100
|
|
160
|
+
).toFixed(2) + '%'
|
|
161
|
+
: 'N/A',
|
|
162
|
+
averageFilesPerSession:
|
|
163
|
+
report.summary.totalFiles / Math.max(report.summary.totalSessions, 1),
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
return report;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Generate error analysis report
|
|
171
|
+
*/
|
|
172
|
+
generateErrorAnalysisReport(sessions) {
|
|
173
|
+
const report = {
|
|
174
|
+
type: 'error_analysis_report',
|
|
175
|
+
timestamp: new Date().toISOString(),
|
|
176
|
+
|
|
177
|
+
summary: {
|
|
178
|
+
totalErrors: 0,
|
|
179
|
+
uniqueErrorTypes: new Set(),
|
|
180
|
+
affectedSessions: new Set(),
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
errorBreakdown: {},
|
|
184
|
+
errorPatterns: [],
|
|
185
|
+
recommendations: [],
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
for (const session of sessions) {
|
|
189
|
+
const sessionReport = this.generateSessionReport(session);
|
|
190
|
+
|
|
191
|
+
for (const error of sessionReport.errors) {
|
|
192
|
+
report.summary.totalErrors++;
|
|
193
|
+
report.summary.uniqueErrorTypes.add(error.type);
|
|
194
|
+
report.summary.affectedSessions.add(session.sessionId);
|
|
195
|
+
|
|
196
|
+
// Breakdown by type
|
|
197
|
+
const errorType = error.type || 'unknown';
|
|
198
|
+
if (!report.errorBreakdown[errorType]) {
|
|
199
|
+
report.errorBreakdown[errorType] = {
|
|
200
|
+
count: 0,
|
|
201
|
+
severity: error.severity || 'unknown',
|
|
202
|
+
examples: [],
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
report.errorBreakdown[errorType].count++;
|
|
206
|
+
|
|
207
|
+
if (report.errorBreakdown[errorType].examples.length < 5) {
|
|
208
|
+
report.errorBreakdown[errorType].examples.push({
|
|
209
|
+
message: error.message,
|
|
210
|
+
file: error.file,
|
|
211
|
+
timestamp: error.timestamp,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Convert sets to arrays for JSON serialization
|
|
218
|
+
report.summary.uniqueErrorTypes = Array.from(
|
|
219
|
+
report.summary.uniqueErrorTypes,
|
|
220
|
+
);
|
|
221
|
+
report.summary.affectedSessions = Array.from(
|
|
222
|
+
report.summary.affectedSessions,
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
// Identify patterns
|
|
226
|
+
const errorTypes = Object.keys(report.errorBreakdown);
|
|
227
|
+
if (errorTypes.length > 0) {
|
|
228
|
+
const topErrors = errorTypes
|
|
229
|
+
.sort(
|
|
230
|
+
(a, b) =>
|
|
231
|
+
report.errorBreakdown[b].count - report.errorBreakdown[a].count,
|
|
232
|
+
)
|
|
233
|
+
.slice(0, 3);
|
|
234
|
+
|
|
235
|
+
for (const errorType of topErrors) {
|
|
236
|
+
report.errorPatterns.push({
|
|
237
|
+
type: errorType,
|
|
238
|
+
frequency: report.errorBreakdown[errorType].count,
|
|
239
|
+
severity: report.errorBreakdown[errorType].severity,
|
|
240
|
+
occurrenceRate:
|
|
241
|
+
(
|
|
242
|
+
(report.errorBreakdown[errorType].count /
|
|
243
|
+
report.summary.totalErrors) *
|
|
244
|
+
100
|
|
245
|
+
).toFixed(2) + '%',
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Generate recommendations
|
|
251
|
+
if (report.summary.totalErrors > 0) {
|
|
252
|
+
for (const pattern of report.errorPatterns) {
|
|
253
|
+
report.recommendations.push(
|
|
254
|
+
`${pattern.type}: Occurs in ${pattern.occurrenceRate} of errors. ` +
|
|
255
|
+
`Consider implementing retry logic or improving validation.`,
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return report;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Export report to JSON
|
|
265
|
+
*/
|
|
266
|
+
async exportReportJson(report, outputPath) {
|
|
267
|
+
try {
|
|
268
|
+
const jsonContent = JSON.stringify(report, null, 2);
|
|
269
|
+
await fs.writeFile(outputPath, jsonContent, 'utf-8');
|
|
270
|
+
|
|
271
|
+
this.logger.info(`Report exported to JSON: ${outputPath}`);
|
|
272
|
+
return { success: true, path: outputPath };
|
|
273
|
+
} catch (error) {
|
|
274
|
+
this.logger.error(`Failed to export report to JSON:`, error);
|
|
275
|
+
return { success: false, error: error.message };
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Export report to CSV
|
|
281
|
+
*/
|
|
282
|
+
async exportReportCsv(report, outputPath) {
|
|
283
|
+
try {
|
|
284
|
+
let csvContent = '';
|
|
285
|
+
|
|
286
|
+
if (report.type === 'session_report') {
|
|
287
|
+
csvContent = this._generateSessionCsv(report);
|
|
288
|
+
} else if (report.type === 'batch_report') {
|
|
289
|
+
csvContent = this._generateBatchCsv(report);
|
|
290
|
+
} else if (report.type === 'error_analysis_report') {
|
|
291
|
+
csvContent = this._generateErrorAnalysisCsv(report);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
await fs.writeFile(outputPath, csvContent, 'utf-8');
|
|
295
|
+
|
|
296
|
+
this.logger.info(`Report exported to CSV: ${outputPath}`);
|
|
297
|
+
return { success: true, path: outputPath };
|
|
298
|
+
} catch (error) {
|
|
299
|
+
this.logger.error(`Failed to export report to CSV:`, error);
|
|
300
|
+
return { success: false, error: error.message };
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Generate summary statistics
|
|
306
|
+
*/
|
|
307
|
+
generateSummaryStatistics(sessions) {
|
|
308
|
+
const stats = {
|
|
309
|
+
timestamp: new Date().toISOString(),
|
|
310
|
+
periodAnalyzed: {
|
|
311
|
+
start: sessions.length > 0 ? sessions[0].startTime : null,
|
|
312
|
+
end: sessions.length > 0 ? sessions[sessions.length - 1].endTime : null,
|
|
313
|
+
},
|
|
314
|
+
|
|
315
|
+
totals: {
|
|
316
|
+
sessions: sessions.length,
|
|
317
|
+
files: 0,
|
|
318
|
+
processed: 0,
|
|
319
|
+
successful: 0,
|
|
320
|
+
failed: 0,
|
|
321
|
+
skipped: 0,
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
rates: {},
|
|
325
|
+
performance: {},
|
|
326
|
+
trending: {},
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
for (const session of sessions) {
|
|
330
|
+
stats.totals.files += session.fileCount || 0;
|
|
331
|
+
stats.totals.processed += session.processedCount || 0;
|
|
332
|
+
stats.totals.successful += session.successCount || 0;
|
|
333
|
+
stats.totals.failed += session.errorCount || 0;
|
|
334
|
+
stats.totals.skipped += session.skippedCount || 0;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Calculate rates
|
|
338
|
+
if (stats.totals.processed > 0) {
|
|
339
|
+
stats.rates.successRate =
|
|
340
|
+
((stats.totals.successful / stats.totals.processed) * 100).toFixed(2) +
|
|
341
|
+
'%';
|
|
342
|
+
stats.rates.failureRate =
|
|
343
|
+
((stats.totals.failed / stats.totals.processed) * 100).toFixed(2) + '%';
|
|
344
|
+
stats.rates.skipRate =
|
|
345
|
+
((stats.totals.skipped / stats.totals.processed) * 100).toFixed(2) +
|
|
346
|
+
'%';
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Performance metrics
|
|
350
|
+
if (sessions.length > 0) {
|
|
351
|
+
const totalDuration = sessions.reduce(
|
|
352
|
+
(sum, s) => sum + ((s.endTime || Date.now()) - s.startTime),
|
|
353
|
+
0,
|
|
354
|
+
);
|
|
355
|
+
stats.performance.averageSessionDuration =
|
|
356
|
+
(totalDuration / sessions.length).toFixed(0) + 'ms';
|
|
357
|
+
stats.performance.totalDuration = totalDuration + 'ms';
|
|
358
|
+
stats.performance.filesPerSecond = (
|
|
359
|
+
stats.totals.processed /
|
|
360
|
+
(totalDuration / 1000)
|
|
361
|
+
).toFixed(2);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return stats;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Get report history
|
|
369
|
+
*/
|
|
370
|
+
getReportHistory(limit = 10) {
|
|
371
|
+
return this.sessions
|
|
372
|
+
.slice(-limit)
|
|
373
|
+
.reverse()
|
|
374
|
+
.map((report) => ({
|
|
375
|
+
reportId: report.sessionId,
|
|
376
|
+
type: report.type,
|
|
377
|
+
timestamp: report.timestamp,
|
|
378
|
+
summary: report.summary,
|
|
379
|
+
successRate: report.metrics?.successRate,
|
|
380
|
+
}));
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Clear old reports
|
|
385
|
+
*/
|
|
386
|
+
clearOldReports(daysOld = 30) {
|
|
387
|
+
const cutoffTime = Date.now() - daysOld * 24 * 60 * 60 * 1000;
|
|
388
|
+
const beforeCount = this.sessions.length;
|
|
389
|
+
|
|
390
|
+
this.sessions = this.sessions.filter(
|
|
391
|
+
(report) => new Date(report.timestamp).getTime() > cutoffTime,
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
const removed = beforeCount - this.sessions.length;
|
|
395
|
+
this.logger.info(`Cleared ${removed} reports older than ${daysOld} days`);
|
|
396
|
+
|
|
397
|
+
return { removed, remaining: this.sessions.length };
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Private helper methods
|
|
402
|
+
*/
|
|
403
|
+
|
|
404
|
+
_calculateSuccessRate(session) {
|
|
405
|
+
if (!session.processedCount || session.processedCount === 0) return '0%';
|
|
406
|
+
return (
|
|
407
|
+
(((session.successCount || 0) / session.processedCount) * 100).toFixed(
|
|
408
|
+
2,
|
|
409
|
+
) + '%'
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
_calculateErrorRate(session) {
|
|
414
|
+
if (!session.processedCount || session.processedCount === 0) return '0%';
|
|
415
|
+
return (
|
|
416
|
+
(((session.errorCount || 0) / session.processedCount) * 100).toFixed(2) +
|
|
417
|
+
'%'
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
_calculateAvgTime(session) {
|
|
422
|
+
if (!session.processedCount || session.processedCount === 0) return 0;
|
|
423
|
+
const duration = session.endTime
|
|
424
|
+
? session.endTime - session.startTime
|
|
425
|
+
: Date.now() - session.startTime;
|
|
426
|
+
return Math.round(duration / session.processedCount) + 'ms';
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
_calculateFilesPerSecond(session) {
|
|
430
|
+
if (!session.processedCount) return 0;
|
|
431
|
+
const duration = session.endTime
|
|
432
|
+
? session.endTime - session.startTime
|
|
433
|
+
: Date.now() - session.startTime;
|
|
434
|
+
return (session.processedCount / (duration / 1000)).toFixed(2);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
_extractErrors(session) {
|
|
438
|
+
return (session.errors || []).map((error) => ({
|
|
439
|
+
type: error.type || 'unknown',
|
|
440
|
+
message: error.message,
|
|
441
|
+
file: error.file,
|
|
442
|
+
severity: error.severity || 'error',
|
|
443
|
+
timestamp: error.timestamp || new Date().toISOString(),
|
|
444
|
+
context: error.context,
|
|
445
|
+
}));
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
_generateSessionCsv(report) {
|
|
449
|
+
const lines = [
|
|
450
|
+
'Session Report',
|
|
451
|
+
`Generated: ${report.timestamp}`,
|
|
452
|
+
`Session ID: ${report.sessionId}`,
|
|
453
|
+
'',
|
|
454
|
+
'Summary',
|
|
455
|
+
'Total Files,Processed,Success,Errors,Skipped',
|
|
456
|
+
`${report.summary.totalFiles},${report.summary.processedFiles},${report.summary.successCount},${report.summary.errorCount},${report.summary.skippedCount}`,
|
|
457
|
+
'',
|
|
458
|
+
'Metrics',
|
|
459
|
+
'Metric,Value',
|
|
460
|
+
`Success Rate,${report.metrics.successRate}`,
|
|
461
|
+
`Error Rate,${report.metrics.errorRate}`,
|
|
462
|
+
`Files Per Second,${report.metrics.filesPerSecond}`,
|
|
463
|
+
`Avg Processing Time,${report.metrics.averageProcessingTime}`,
|
|
464
|
+
];
|
|
465
|
+
|
|
466
|
+
return lines.join('\n');
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
_generateBatchCsv(report) {
|
|
470
|
+
const lines = [
|
|
471
|
+
'Batch Report',
|
|
472
|
+
`Generated: ${report.timestamp}`,
|
|
473
|
+
'',
|
|
474
|
+
'Session Metrics',
|
|
475
|
+
'Session ID,Duration (ms),File Count,Success Rate',
|
|
476
|
+
];
|
|
477
|
+
|
|
478
|
+
for (const metric of report.sessionMetrics) {
|
|
479
|
+
lines.push(
|
|
480
|
+
`${metric.sessionId},${metric.duration},${metric.fileCount},${metric.successRate}`,
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
lines.push('', 'Summary', 'Metric,Value');
|
|
485
|
+
for (const [key, value] of Object.entries(report.aggregatedMetrics)) {
|
|
486
|
+
lines.push(`${key},${value}`);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return lines.join('\n');
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
_generateErrorAnalysisCsv(report) {
|
|
493
|
+
const lines = [
|
|
494
|
+
'Error Analysis Report',
|
|
495
|
+
`Generated: ${report.timestamp}`,
|
|
496
|
+
'',
|
|
497
|
+
'Error Summary',
|
|
498
|
+
'Error Type,Count,Severity,Occurrence Rate',
|
|
499
|
+
];
|
|
500
|
+
|
|
501
|
+
for (const pattern of report.errorPatterns) {
|
|
502
|
+
lines.push(
|
|
503
|
+
`${pattern.type},${pattern.frequency},${pattern.severity},${pattern.occurrenceRate}`,
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
return lines.join('\n');
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
export default ReportingService;
|