UNPKG

bowling-analysis-system

Version:

A comprehensive system for analyzing bowling techniques using video processing and metrics calculation

247 lines (204 loc) 6.51 kB
/** * @module core/metrics/validation * @description Utilities for validating metrics data */ /** * Configuration for logging validation messages * @type {Object} */ const logConfig = { // Whether to log validation errors showErrors: false, // Whether to log validation warnings showWarnings: false, // Whether to log detailed validation info showDetails: false, // Counts of different message types (for summary reporting) counts: { errors: 0, warnings: 0, info: 0 } }; /** * Log a validation message if enabled * @param {string} type - Type of message ('error', 'warning', 'info') * @param {string} message - Message to log */ function logValidation(type, message) { // Track counts for summary reporting if (type in logConfig.counts) { logConfig.counts[type]++; } // Only log if enabled for this type if ((type === 'error' && logConfig.showErrors) || (type === 'warning' && logConfig.showWarnings) || (type === 'info' && logConfig.showDetails)) { console.log(`[Validation ${type.toUpperCase()}]: ${message}`); } } /** * Reset validation log counts */ function resetLogCounts() { Object.keys(logConfig.counts).forEach(key => { logConfig.counts[key] = 0; }); } /** * Configure validation logging * @param {Object} config - Configuration options */ function configureLogging(config = {}) { if (typeof config.showErrors === 'boolean') { logConfig.showErrors = config.showErrors; } if (typeof config.showWarnings === 'boolean') { logConfig.showWarnings = config.showWarnings; } if (typeof config.showDetails === 'boolean') { logConfig.showDetails = config.showDetails; } resetLogCounts(); } /** * Get validation log counts * @returns {Object} Log counts by type */ function getLogCounts() { return { ...logConfig.counts }; } /** * Validate time series metrics * @param {Object} metrics - Time series metrics to validate * @returns {Object} Validation results */ function validateTimeSeriesMetrics(metrics) { resetLogCounts(); const results = { allNulls: [], allZeros: [], lowVariance: [], validMetrics: [] }; if (!metrics) { logValidation('error', 'No metrics provided for validation'); return results; } // Process each category for (const category in metrics) { const categoryMetrics = metrics[category]; // Process each metric for (const metricName in categoryMetrics) { const metricData = categoryMetrics[metricName]; const metricKey = `${category}.${metricName}`; // Skip if metric data is missing if (!metricData) { logValidation('warning', `Metric ${metricKey} has no data`); continue; } // Count valid points const pointCount = countValidPoints(metricData); if (pointCount === 0) { results.allNulls.push(metricKey); logValidation('warning', `Metric ${metricKey} has no valid data points`); continue; } // Check if all values are zero const nonZeroCount = countNonZeroPoints(metricData); if (nonZeroCount === 0) { results.allZeros.push(metricKey); logValidation('warning', `Metric ${metricKey} has all zero values`); continue; } // Check variance const variance = calculateVariance(metricData); if (variance < 0.001) { results.lowVariance.push(`${metricKey} (${variance.toFixed(6)})`); logValidation('info', `Metric ${metricKey} has low variance: ${variance.toFixed(6)}`); continue; } // Valid metric results.validMetrics.push(metricKey); logValidation('info', `Metric ${metricKey} passed validation`); } } return results; } /** * Output validation results * @param {Object} metrics - Metrics data * @param {Object} results - Validation results */ function outputValidationResults(metrics, results) { if (!metrics || !results) { logValidation('error', 'Cannot output validation results: missing data'); return; } // Count metrics let totalMetrics = 0; for (const category in metrics) { totalMetrics += Object.keys(metrics[category] || {}).length; } // Log summary const validCount = results.validMetrics.length; const invalidCount = totalMetrics - validCount; logValidation('info', `Validation summary: ${validCount}/${totalMetrics} metrics valid`); if (results.allNulls.length > 0) { logValidation('warning', `${results.allNulls.length} metrics have no valid data`); } if (results.allZeros.length > 0) { logValidation('warning', `${results.allZeros.length} metrics have all zero values`); } if (results.lowVariance.length > 0) { logValidation('info', `${results.lowVariance.length} metrics have low variance`); } } /** * Count valid (non-null, non-undefined) points in data * @param {Array} data - Data to count valid points in * @returns {number} Count of valid points */ function countValidPoints(data) { if (!Array.isArray(data)) { return 0; } return data.filter(point => point !== null && point !== undefined).length; } /** * Count non-zero points in data * @param {Array} data - Data to count non-zero points in * @returns {number} Count of non-zero points */ function countNonZeroPoints(data) { if (!Array.isArray(data)) { return 0; } return data.filter(point => point !== null && point !== undefined && point !== 0).length; } /** * Calculate variance of data * @param {Array} data - Data to calculate variance for * @returns {number} Variance */ function calculateVariance(data) { if (!Array.isArray(data)) { return 0; } const validData = data.filter(point => point !== null && point !== undefined); if (validData.length === 0) { return 0; } const mean = validData.reduce((sum, value) => sum + value, 0) / validData.length; const squaredDiffs = validData.map(value => Math.pow(value - mean, 2)); const variance = squaredDiffs.reduce((sum, value) => sum + value, 0) / validData.length; return variance; } module.exports = { validateTimeSeriesMetrics, outputValidationResults, countValidPoints, configureLogging, getLogCounts, logValidation };