Files
homeschool/generate-leaderboard-data.js
T

243 lines
7.4 KiB
JavaScript

/**
* Generate sample leaderboard data for the Spelling Practice App
* This script will populate leaderboards with realistic data for the analytics dashboard
*/
const fs = require('fs');
const path = require('path');
// Define paths
const DATA_DIR = path.join(__dirname, 'data');
const files = {
users: path.join(DATA_DIR, 'users.json'),
progress: path.join(DATA_DIR, 'progress.json'),
leaderboards: path.join(DATA_DIR, 'leaderboards.json')
};
// Helper functions
function readJsonSafe(filePath, defaultValue = {}) {
try {
if (fs.existsSync(filePath)) {
const content = fs.readFileSync(filePath, 'utf8');
return JSON.parse(content);
}
return defaultValue;
} catch (error) {
console.error(`Error reading ${filePath}:`, error);
return defaultValue;
}
}
function writeJsonSafe(filePath, data) {
try {
fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
return true;
} catch (error) {
console.error(`Error writing to ${filePath}:`, error);
return false;
}
}
// Random helpers
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
function randomFloat(min, max, decimals = 2) {
const rand = Math.random() * (max - min) + min;
return parseFloat(rand.toFixed(decimals));
}
// Generate additional fake users for the leaderboards
function generateFakeUsers(count) {
const firstNames = ['Emma', 'Liam', 'Olivia', 'Noah', 'Ava', 'William', 'Sophia', 'James', 'Isabella', 'Logan',
'Mia', 'Benjamin', 'Charlotte', 'Mason', 'Amelia', 'Elijah', 'Harper', 'Oliver', 'Evelyn', 'Jacob'];
const lastNames = ['Smith', 'Johnson', 'Williams', 'Brown', 'Jones', 'Garcia', 'Miller', 'Davis', 'Rodriguez', 'Martinez',
'Hernandez', 'Lopez', 'Gonzalez', 'Wilson', 'Anderson', 'Thomas', 'Taylor', 'Moore', 'Jackson', 'Martin'];
const users = [];
for (let i = 0; i < count; i++) {
const firstName = firstNames[randomInt(0, firstNames.length - 1)];
const lastName = lastNames[randomInt(0, lastNames.length - 1)];
const username = `${firstName.toLowerCase()}${lastName.substring(0, 1).toLowerCase()}${randomInt(1, 99)}`;
users.push(username);
}
return users;
}
// Main function to generate leaderboard data
async function generateLeaderboardData() {
console.log('Generating sample leaderboard data...');
// Load users and progress data
const users = readJsonSafe(files.users, []);
const progressData = readJsonSafe(files.progress, {});
if (users.length === 0) {
console.error('No users found. Please ensure users exist before running this script.');
return;
}
// Generate 10 additional fake users for leaderboards
const fakeUsers = generateFakeUsers(10);
const allUsers = [...users.map(u => u.username), ...fakeUsers];
// Create leaderboard data
const leaderboards = {
allTime: {
accuracy: [],
totalWords: [],
sessionsCompleted: [],
streakDays: []
},
weekly: {
accuracy: [],
totalWords: [],
sessionsCompleted: []
},
currentWeek: `${new Date().getFullYear()}-${Math.ceil((new Date().getDate() + new Date().getDay()) / 7)}`
};
// Generate data for each category
// All-time accuracy leaderboard
allUsers.forEach(username => {
// For real users, use their actual progress data if available
if (progressData[username]) {
leaderboards.allTime.accuracy.push({
username,
value: progressData[username].stats.accuracy || randomFloat(70, 98, 1),
timestamp: new Date().toISOString()
});
} else {
// For fake users, generate random data
leaderboards.allTime.accuracy.push({
username,
value: randomFloat(70, 98, 1),
timestamp: new Date().toISOString()
});
}
});
// Sort by value (descending)
leaderboards.allTime.accuracy.sort((a, b) => b.value - a.value);
// All-time totalWords leaderboard
allUsers.forEach(username => {
if (progressData[username]) {
leaderboards.allTime.totalWords.push({
username,
value: progressData[username].stats.totalWords || randomInt(100, 1000),
timestamp: new Date().toISOString()
});
} else {
leaderboards.allTime.totalWords.push({
username,
value: randomInt(100, 1000),
timestamp: new Date().toISOString()
});
}
});
leaderboards.allTime.totalWords.sort((a, b) => b.value - a.value);
// All-time sessionsCompleted leaderboard
allUsers.forEach(username => {
if (progressData[username]) {
leaderboards.allTime.sessionsCompleted.push({
username,
value: progressData[username].stats.totalSessions || randomInt(5, 50),
timestamp: new Date().toISOString()
});
} else {
leaderboards.allTime.sessionsCompleted.push({
username,
value: randomInt(5, 50),
timestamp: new Date().toISOString()
});
}
});
leaderboards.allTime.sessionsCompleted.sort((a, b) => b.value - a.value);
// All-time streakDays leaderboard
allUsers.forEach(username => {
if (progressData[username]) {
leaderboards.allTime.streakDays.push({
username,
value: progressData[username].streaks?.longest || randomInt(1, 14),
timestamp: new Date().toISOString()
});
} else {
leaderboards.allTime.streakDays.push({
username,
value: randomInt(1, 14),
timestamp: new Date().toISOString()
});
}
});
leaderboards.allTime.streakDays.sort((a, b) => b.value - a.value);
// Generate weekly leaderboards
// Copy from all-time but with different values
// Weekly accuracy
allUsers.forEach(username => {
const allTimeEntry = leaderboards.allTime.accuracy.find(entry => entry.username === username);
const baseValue = allTimeEntry ? allTimeEntry.value : randomFloat(70, 98, 1);
// Weekly value is slightly different from all-time
leaderboards.weekly.accuracy.push({
username,
value: Math.min(99.9, Math.max(50, baseValue + randomFloat(-5, 5, 1))),
timestamp: new Date().toISOString()
});
});
leaderboards.weekly.accuracy.sort((a, b) => b.value - a.value);
// Weekly totalWords
allUsers.forEach(username => {
const allTimeEntry = leaderboards.allTime.totalWords.find(entry => entry.username === username);
const baseValue = allTimeEntry ? allTimeEntry.value : randomInt(100, 1000);
// Weekly value is much smaller than all-time
leaderboards.weekly.totalWords.push({
username,
value: randomInt(10, Math.min(100, Math.floor(baseValue / 10))),
timestamp: new Date().toISOString()
});
});
leaderboards.weekly.totalWords.sort((a, b) => b.value - a.value);
// Weekly sessionsCompleted
allUsers.forEach(username => {
const allTimeEntry = leaderboards.allTime.sessionsCompleted.find(entry => entry.username === username);
const baseValue = allTimeEntry ? allTimeEntry.value : randomInt(5, 50);
// Weekly value is much smaller than all-time
leaderboards.weekly.sessionsCompleted.push({
username,
value: randomInt(1, Math.min(7, Math.floor(baseValue / 5))),
timestamp: new Date().toISOString()
});
});
leaderboards.weekly.sessionsCompleted.sort((a, b) => b.value - a.value);
// Save the leaderboard data
console.log('Saving leaderboard data...');
writeJsonSafe(files.leaderboards, leaderboards);
console.log('Leaderboard data generation complete!');
}
// Execute the script
generateLeaderboardData();