/** * Test script to verify sleep data processing * This script loads the metrics.csv file directly and processes it * to check if sleep data is being correctly identified and processed. */ // First, load the required modules const fs = require('fs'); const path = require('path'); // Create a simplified version of the DataProcessor class for testing class TestDataProcessor { constructor() { this.metricsData = null; } parseCSV(csvString) { // Split the CSV string into lines const lines = csvString.split('\n'); // Extract headers (first line) and remove quotes and any extra characters const headers = lines[0].split(',').map(header => { // Clean up header - remove quotes, carriage returns, etc. return header.replace(/^"(.*)"$/, '$1').replace(/\r$/, ''); }); console.log('CSV Headers:', headers); // Process each data line const results = []; for (let i = 1; i < lines.length; i++) { // Skip empty lines if (!lines[i].trim()) continue; // Split the line into values, handling quoted values with commas const values = this.splitCSVLine(lines[i]); // Create an object with headers as keys and values const entry = {}; headers.forEach((header, index) => { // Remove quotes if present and convert to appropriate type let value = values[index] ? values[index].replace(/^"(.*)"$/, '$1').replace(/\r$/, '') : ''; // Try to convert to number if it looks like one if (!isNaN(value) && value.trim() !== '') { value = parseFloat(value); } entry[header] = value; }); results.push(entry); } return results; } splitCSVLine(line) { const values = []; let inQuotes = false; let currentValue = ''; for (let i = 0; i < line.length; i++) { const char = line[i]; if (char === '"') { inQuotes = !inQuotes; } else if (char === ',' && !inQuotes) { values.push(currentValue); currentValue = ''; } else { currentValue += char; } } // Add the last value values.push(currentValue); return values; } processMetricsData(csvString) { const rawData = this.parseCSV(csvString); console.log('First few entries from metrics CSV:', rawData.slice(0, 5)); // Group metrics by date const metricsByDate = {}; rawData.forEach(entry => { // Extract date from timestamp (format: "2025-04-14 00:00:00") const date = entry.Timestamp.split(' ')[0]; if (!metricsByDate[date]) { metricsByDate[date] = {}; } // Add metric to the date group // Ensure Type is trimmed to handle any extra spaces const metricType = entry.Type.trim(); // Get the value from the correct column (might be '"Value"' instead of 'Value') let metricValue = null; if (entry.Value !== undefined) { metricValue = entry.Value; } else if (entry['"Value"'] !== undefined) { metricValue = entry['"Value"']; } // Convert Value to number if it's numeric if (metricValue !== null && !isNaN(metricValue) && metricValue.toString().trim() !== '') { metricValue = parseFloat(metricValue); } metricsByDate[date][metricType] = metricValue; }); console.log('Sample of metrics by date:', Object.keys(metricsByDate).slice(0, 3).map(date => ({ date, metrics: metricsByDate[date] }))); // Convert to array and sort by date const processedData = Object.keys(metricsByDate).map(date => { return { date, metrics: metricsByDate[date] }; }).sort((a, b) => new Date(a.date) - new Date(b.date)); this.metricsData = processedData; return processedData; } generateDataSummary() { if (!this.metricsData) { return null; } // Log all available metric types from the first few days to help diagnose issues console.log('Available metric types in first few days:'); this.metricsData.slice(0, 3).forEach(day => { console.log(`Day ${day.date} metrics:`, Object.keys(day.metrics)); }); // Find sleep hours data with case-insensitive matching const sleepData = []; const sleepHoursKey = 'Sleep Hours'; // The expected key this.metricsData.forEach(day => { // Try to find the sleep hours key (case-insensitive) let foundSleepHours = false; // First try the exact key if (day.metrics[sleepHoursKey] !== undefined) { sleepData.push(parseFloat(day.metrics[sleepHoursKey])); foundSleepHours = true; } else { // If not found, try case-insensitive search for (const key in day.metrics) { if (key.toLowerCase() === sleepHoursKey.toLowerCase()) { sleepData.push(parseFloat(day.metrics[key])); foundSleepHours = true; console.log(`Found sleep hours with different case: "${key}"`); break; } } } if (!foundSleepHours) { console.log(`No sleep hours found for day ${day.date}`); } }); console.log(`Found ${sleepData.length} days with sleep data out of ${this.metricsData.length} total days`); const sleepStats = sleepData.length > 0 ? { average: this.calculateAverage(sleepData), min: Math.min(...sleepData), max: Math.max(...sleepData) } : null; console.log('Sleep stats:', sleepStats); return { sleep: sleepStats }; } calculateAverage(values) { if (!values || values.length === 0) return 0; return values.reduce((sum, val) => sum + val, 0) / values.length; } } // Main test function async function testSleepDataProcessing() { try { // Read the metrics.csv file const metricsFilePath = path.join(__dirname, 'metrics.csv'); const metricsCSV = fs.readFileSync(metricsFilePath, 'utf8'); console.log('Loaded metrics.csv file'); // Create a test processor and process the data const processor = new TestDataProcessor(); processor.processMetricsData(metricsCSV); // Generate summary to check sleep data const summary = processor.generateDataSummary(); console.log('Summary:', summary); // Check if sleep data was found if (summary.sleep) { console.log('SUCCESS: Sleep data was found and processed correctly'); console.log(`Average sleep: ${summary.sleep.average.toFixed(2)} hours`); console.log(`Min sleep: ${summary.sleep.min.toFixed(2)} hours`); console.log(`Max sleep: ${summary.sleep.max.toFixed(2)} hours`); } else { console.log('ERROR: No sleep data was found'); } } catch (error) { console.error('Error testing sleep data processing:', error); } } // Run the test testSleepDataProcessing();