bowling-analysis-system
Version:
A comprehensive system for analyzing bowling techniques using video processing and metrics calculation
215 lines (185 loc) • 6.85 kB
JavaScript
/**
* Report Generator Processor
*
* Generates reports from metrics and analysis data
* @module processors/ReportGeneratorProcessor
*/
const { BaseProcessor } = require('../core/BaseProcessor');
const { defaultFileService } = require('../utils/FileService');
/**
* @class ReportGeneratorProcessor
* @description Processor for generating reports from metrics and analysis data
* @extends BaseProcessor
*/
class ReportGeneratorProcessor extends BaseProcessor {
/**
* Create a new report generator processor
* @param {Object} config - Processor configuration
*/
constructor(config = {}) {
super('reportGenerator', config);
this.formatters = new Map();
// Register default formatters
this.registerFormatter('html', this._formatHtml.bind(this));
this.registerFormatter('md', this._formatMarkdown.bind(this));
this.registerFormatter('json', this._formatJson.bind(this));
}
/**
* Register a report formatter
* @param {string} format - Format name
* @param {Function} formatter - Formatter function
* @returns {ReportGeneratorProcessor} Processor instance for chaining
*/
registerFormatter(format, formatter) {
this.formatters.set(format, formatter);
return this;
}
/**
* Process input by generating a report
* @param {Object} input - Input metrics and analysis data
* @param {Object} context - Processing context
* @returns {Promise<string>} Generated report
* @protected
*/
async _process(input, context) {
const format = this.config.format || 'html';
const template = this.config.template;
// Get formatter
const formatter = this.formatters.get(format);
if (!formatter) {
throw new Error(`Unknown report format: ${format}`);
}
// Load custom template if specified
let customTemplate = null;
if (template) {
try {
customTemplate = await defaultFileService.readFile(template, { encoding: 'utf8' });
} catch (error) {
throw new Error(`Failed to load template ${template}: ${error.message}`);
}
}
// Apply formatter
return formatter(input, { template: customTemplate, ...context });
}
/**
* Format report as HTML
* @param {Object} input - Input data
* @param {Object} options - Formatting options
* @returns {string} HTML report
* @private
*/
_formatHtml(input, options) {
const { template } = options;
// Use custom template if provided
if (template) {
// Placeholder for template processing logic
return template.replace('{{content}}', JSON.stringify(input, null, 2));
}
// Generate default HTML report
return `<!DOCTYPE html>
<html>
<head>
<title>Bowling Analysis Report</title>
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
h1 { color: #333; }
.section { margin-bottom: 20px; }
.metric { margin-bottom: 10px; }
.recommendation { background-color: #f0f0f0; padding: 10px; margin-bottom: 10px; border-left: 4px solid #007bff; }
.high { border-left-color: #dc3545; }
.medium { border-left-color: #fd7e14; }
.low { border-left-color: #28a745; }
</style>
</head>
<body>
<h1>Bowling Analysis Report</h1>
<div class="section">
<h2>Summary</h2>
<p>Generated on ${new Date().toLocaleString()}</p>
<p>Analysis contains ${input.analysis?.insights?.length || 0} insights and ${input.analysis?.recommendations?.length || 0} recommendations.</p>
</div>
<div class="section">
<h2>Recommendations</h2>
${input.analysis?.recommendations?.map(rec => `
<div class="recommendation ${rec.priority === 1 ? 'high' : rec.priority === 2 ? 'medium' : 'low'}">
<h3>${rec.recommendation}</h3>
<p><strong>Category:</strong> ${rec.category}</p>
<p><strong>Priority:</strong> ${rec.priority}</p>
<p><strong>Suggested Drills:</strong> ${rec.drills?.join(', ') || 'None'}</p>
</div>
`).join('') || '<p>No recommendations available.</p>'}
</div>
<div class="section">
<h2>Insights</h2>
${input.analysis?.insights?.map(insight => `
<div class="metric">
<h3>${insight.aspect}</h3>
<p><strong>Finding:</strong> ${insight.finding}</p>
<p><strong>Category:</strong> ${insight.category}</p>
<p><strong>Impact:</strong> ${insight.impact}</p>
<p><strong>Confidence:</strong> ${insight.confidence ? (insight.confidence * 100).toFixed(1) : 'N/A'}%</p>
</div>
`).join('') || '<p>No insights available.</p>'}
</div>
</body>
</html>`;
}
/**
* Format report as Markdown
* @param {Object} input - Input data
* @param {Object} options - Formatting options
* @returns {string} Markdown report
* @private
*/
_formatMarkdown(input, options) {
const { template } = options;
// Use custom template if provided
if (template) {
// Placeholder for template processing logic
return template.replace('{{content}}', JSON.stringify(input, null, 2));
}
// Generate default Markdown report
return `# Bowling Analysis Report
Generated on ${new Date().toLocaleString()}
## Summary
Analysis contains ${input.analysis?.insights?.length || 0} insights and ${input.analysis?.recommendations?.length || 0} recommendations.
## Recommendations
${input.analysis?.recommendations?.map(rec => `
### ${rec.recommendation}
- **Category:** ${rec.category}
- **Priority:** ${rec.priority}
- **Suggested Drills:** ${rec.drills?.join(', ') || 'None'}
`).join('') || 'No recommendations available.'}
## Insights
${input.analysis?.insights?.map(insight => `
### ${insight.aspect}
- **Finding:** ${insight.finding}
- **Category:** ${insight.category}
- **Impact:** ${insight.impact}
- **Confidence:** ${insight.confidence ? (insight.confidence * 100).toFixed(1) : 'N/A'}%
`).join('') || 'No insights available.'}
`;
}
/**
* Format report as JSON
* @param {Object} input - Input data
* @param {Object} options - Formatting options
* @returns {string} JSON report
* @private
*/
_formatJson(input, options) {
// Create report structure
const report = {
generated: new Date().toISOString(),
summary: {
insightCount: input.analysis?.insights?.length || 0,
recommendationCount: input.analysis?.recommendations?.length || 0
},
recommendations: input.analysis?.recommendations || [],
insights: input.analysis?.insights || [],
metrics: input.metrics || {}
};
return JSON.stringify(report, null, 2);
}
}
module.exports = ReportGeneratorProcessor;