223 lines
6.1 KiB
JavaScript
223 lines
6.1 KiB
JavaScript
/**
|
|
* Generate sample spaced repetition data for the Spelling Practice App
|
|
* This script will populate the spaced repetition system with realistic learning data
|
|
*/
|
|
|
|
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'),
|
|
wordlists: path.join(DATA_DIR, 'wordlists.json'),
|
|
spacedRepetition: path.join(DATA_DIR, 'spacedRepetition.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));
|
|
}
|
|
|
|
// Date helpers
|
|
function addDays(date, days) {
|
|
const result = new Date(date);
|
|
result.setDate(result.getDate() + days);
|
|
return result;
|
|
}
|
|
|
|
function subtractDays(date, days) {
|
|
return addDays(date, -days);
|
|
}
|
|
|
|
function formatDate(date) {
|
|
return date.toISOString().split('T')[0];
|
|
}
|
|
|
|
// Main function to generate spaced repetition data
|
|
async function generateSpacedRepetitionData() {
|
|
console.log('Generating sample spaced repetition data...');
|
|
|
|
// Load users and wordlists
|
|
const users = readJsonSafe(files.users, []);
|
|
const wordlists = readJsonSafe(files.wordlists, {});
|
|
|
|
if (users.length === 0) {
|
|
console.error('No users found. Please ensure users exist before running this script.');
|
|
return;
|
|
}
|
|
|
|
// Create spaced repetition data structure
|
|
const srData = readJsonSafe(files.spacedRepetition, {});
|
|
|
|
// Generate data for each user
|
|
const today = new Date();
|
|
|
|
for (const user of users) {
|
|
const username = user.username;
|
|
console.log(`Generating spaced repetition data for user: ${username}`);
|
|
|
|
// Get user's words
|
|
let userWords = [];
|
|
|
|
// Handle different wordlist structures
|
|
if (Array.isArray(wordlists[username])) {
|
|
userWords = wordlists[username];
|
|
} else if (wordlists[username] && wordlists[username].weeks && Array.isArray(wordlists[username].weeks)) {
|
|
// Extract all words from all weeks
|
|
wordlists[username].weeks.forEach(week => {
|
|
if (week.words && Array.isArray(week.words)) {
|
|
userWords = [...userWords, ...week.words];
|
|
}
|
|
});
|
|
}
|
|
|
|
if (userWords.length === 0) {
|
|
console.log(`No words found for user ${username}. Skipping...`);
|
|
continue;
|
|
}
|
|
|
|
// Initialize user's spaced repetition data if not exists
|
|
if (!srData[username]) {
|
|
srData[username] = {
|
|
settings: {
|
|
dailyLimit: 20,
|
|
reviewThreshold: 3,
|
|
newWordsPerDay: 5
|
|
},
|
|
stats: {
|
|
totalReviews: 0,
|
|
correctReviews: 0,
|
|
totalWords: 0,
|
|
masteredWords: 0
|
|
},
|
|
words: {}
|
|
};
|
|
}
|
|
|
|
// Add words with different mastery levels
|
|
let totalReviews = 0;
|
|
let correctReviews = 0;
|
|
let masteredWords = 0;
|
|
|
|
for (const word of userWords) {
|
|
// Determine mastery level
|
|
// 0: New - 20%
|
|
// 1-2: Learning - 30%
|
|
// 3-4: Reviewing - 30%
|
|
// 5+: Mastered - 20%
|
|
|
|
const rand = Math.random();
|
|
let repetitions = 0;
|
|
let interval = 0;
|
|
let easeFactor = 2.5;
|
|
|
|
if (rand < 0.2) {
|
|
// New word
|
|
repetitions = 0;
|
|
interval = 0;
|
|
easeFactor = randomFloat(2.3, 2.7, 1);
|
|
} else if (rand < 0.5) {
|
|
// Learning
|
|
repetitions = randomInt(1, 2);
|
|
interval = repetitions === 1 ? 1 : 3;
|
|
easeFactor = randomFloat(2.0, 2.5, 1);
|
|
totalReviews += repetitions;
|
|
correctReviews += repetitions - 1;
|
|
} else if (rand < 0.8) {
|
|
// Reviewing
|
|
repetitions = randomInt(3, 4);
|
|
interval = repetitions === 3 ? 7 : 15;
|
|
easeFactor = randomFloat(2.2, 2.8, 1);
|
|
totalReviews += repetitions;
|
|
correctReviews += repetitions - 1;
|
|
} else {
|
|
// Mastered
|
|
repetitions = randomInt(5, 8);
|
|
interval = Math.pow(2, repetitions - 4) * 10; // Exponential increase
|
|
easeFactor = randomFloat(2.6, 3.0, 1);
|
|
totalReviews += repetitions;
|
|
correctReviews += repetitions - 1;
|
|
masteredWords++;
|
|
}
|
|
|
|
// Calculate last and next review dates
|
|
let nextReview = null;
|
|
let lastReview = null;
|
|
|
|
if (repetitions > 0) {
|
|
// If the word has been reviewed before, set lastReview
|
|
lastReview = formatDate(subtractDays(today, randomInt(1, interval)));
|
|
|
|
// Next review might be due
|
|
const dueChance = 0.3; // 30% chance word is due
|
|
if (Math.random() < dueChance) {
|
|
// Word is due today or in the past
|
|
nextReview = formatDate(subtractDays(today, randomInt(0, 3)));
|
|
} else {
|
|
// Word is due in the future
|
|
nextReview = formatDate(addDays(today, randomInt(1, interval)));
|
|
}
|
|
} else {
|
|
// New word, not reviewed yet
|
|
nextReview = formatDate(today);
|
|
}
|
|
|
|
// Add word to user's SR data
|
|
srData[username].words[word] = {
|
|
easeFactor,
|
|
interval,
|
|
repetitions,
|
|
lastReview,
|
|
nextReview
|
|
};
|
|
}
|
|
|
|
// Update user stats
|
|
srData[username].stats = {
|
|
totalReviews,
|
|
correctReviews,
|
|
totalWords: userWords.length,
|
|
masteredWords
|
|
};
|
|
}
|
|
|
|
// Save spaced repetition data
|
|
console.log('Saving spaced repetition data...');
|
|
writeJsonSafe(files.spacedRepetition, srData);
|
|
|
|
console.log('Spaced repetition data generation complete!');
|
|
}
|
|
|
|
// Execute the script
|
|
generateSpacedRepetitionData();
|