243 lines
7.4 KiB
JavaScript
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();
|