Implement version 3.0.0 with Analytics Dashboard, Gamification Expansion, and Spaced Repetition System
This commit is contained in:
1 parent
27af564a41
commit
7d8470df84
20 files changed
+22999
-23
No files matched your search
@@ -0,0 +1,222 @@
|
||||
/**
|
||||
* 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();
|
||||
Reference in new issue
Block a user