230 lines
7.9 KiB
JavaScript
230 lines
7.9 KiB
JavaScript
/**
|
|
* 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();
|