const fs = require('fs'); const path = require('path'); const { Subject, Chapter, Lesson, sequelize } = require('../models'); /** * Import MoveUp curriculum data from data/moveup folder * Structure: data/moveup/g1/unit4.json -> Subject: moveup_grade1 -> Chapter: Unit 4 -> Lessons */ async function importMoveUpData() { const transaction = await sequelize.transaction(); try { console.log('šŸš€ Starting MoveUp data import...\n'); const dataPath = path.join(__dirname, '../data/moveup'); const grades = ['g1', 'g2', 'g3', 'g4', 'g5']; const stats = { subjects: 0, chapters: 0, lessons: 0, errors: [] }; for (const grade of grades) { const gradePath = path.join(dataPath, grade); // Check if grade folder exists if (!fs.existsSync(gradePath)) { console.log(`ā­ļø Skipping ${grade} - folder not found`); continue; } console.log(`šŸ“š Processing ${grade.toUpperCase()}...`); // Create Subject for this grade const gradeNumber = grade.replace('g', ''); const subjectCode = `MOVEUP-G${gradeNumber}`; const subjectName = `MoveUp Grade ${gradeNumber}`; let subject = await Subject.findOne({ where: { subject_code: subjectCode }, transaction }); if (!subject) { subject = await Subject.create({ subject_code: subjectCode, subject_name: subjectName, subject_name_en: `MoveUp Grade ${gradeNumber}`, description: `English curriculum for Grade ${gradeNumber} - MoveUp series`, is_active: true, is_public: true, is_premium: false }, { transaction }); stats.subjects++; console.log(` āœ… Created Subject: ${subjectName} (${subject.id})`); } else { console.log(` ā­ļø Subject already exists: ${subjectName} (${subject.id})`); } // Read all JSON files in grade folder const files = fs.readdirSync(gradePath).filter(f => f.endsWith('.json')); for (const file of files) { const filePath = path.join(gradePath, file); const unitNumber = file.replace('unit', '').replace('.json', ''); console.log(` šŸ“– Processing ${file}...`); // Create Chapter for this unit const chapterTitle = `Unit ${unitNumber}`; let chapter = await Chapter.findOne({ where: { subject_id: subject.id, chapter_number: parseInt(unitNumber) }, transaction }); if (!chapter) { chapter = await Chapter.create({ subject_id: subject.id, chapter_number: parseInt(unitNumber), chapter_title: chapterTitle, chapter_description: `Unit ${unitNumber} lessons`, is_published: true, display_order: parseInt(unitNumber) }, { transaction }); stats.chapters++; console.log(` āœ… Created Chapter: ${chapterTitle} (${chapter.id})`); } else { console.log(` ā­ļø Chapter already exists: ${chapterTitle} (${chapter.id})`); } // Read lessons from JSON file let lessonsData; try { const fileContent = fs.readFileSync(filePath, 'utf8'); lessonsData = JSON.parse(fileContent); } catch (error) { stats.errors.push(`Failed to read ${file}: ${error.message}`); console.log(` āŒ Error reading ${file}: ${error.message}`); continue; } // Create lessons for (const lessonData of lessonsData) { try { // Check if lesson already exists const existingLesson = await Lesson.findOne({ where: { chapter_id: chapter.id, lesson_number: lessonData.lesson_number }, transaction }); if (existingLesson) { console.log(` ā­ļø Lesson ${lessonData.lesson_number} already exists: ${lessonData.lesson_title}`); continue; } // Normalize content_json based on lesson_content_type let normalizedContentJson = lessonData.content_json; if (lessonData.lesson_content_type === 'vocabulary' && lessonData.content_json) { // Convert vocabulary array to words array for consistency if (lessonData.content_json.vocabulary && Array.isArray(lessonData.content_json.vocabulary)) { normalizedContentJson = { ...lessonData.content_json, type: 'vocabulary', words: lessonData.content_json.vocabulary }; } } else if (lessonData.lesson_content_type === 'grammar' && lessonData.content_json) { // Normalize grammar content if (lessonData.content_json.grammar) { normalizedContentJson = { ...lessonData.content_json, type: 'grammar', sentences: Array.isArray(lessonData.content_json.grammar) ? lessonData.content_json.grammar : [lessonData.content_json.grammar] }; } } else if (lessonData.lesson_content_type === 'phonics' && lessonData.content_json) { // Normalize phonics content normalizedContentJson = { ...lessonData.content_json, type: 'phonics', phonics_rules: lessonData.content_json.letters && lessonData.content_json.sounds ? [ { letters: lessonData.content_json.letters, sounds: lessonData.content_json.sounds, words: lessonData.content_json.vocabulary || [] } ] : [] }; } // Create lesson const lesson = await Lesson.create({ chapter_id: chapter.id, lesson_number: lessonData.lesson_number, lesson_title: lessonData.lesson_title, lesson_type: lessonData.lesson_type, lesson_description: lessonData.lesson_description, lesson_content_type: lessonData.lesson_content_type, content_json: normalizedContentJson, content_url: lessonData.content_url || null, content_type: lessonData.content_type || null, is_published: true, is_free: false, display_order: lessonData.lesson_number }, { transaction }); stats.lessons++; console.log(` āœ… Created Lesson ${lessonData.lesson_number}: ${lessonData.lesson_title}`); } catch (error) { stats.errors.push(`Failed to create lesson ${lessonData.lesson_number} in ${file}: ${error.message}`); console.log(` āŒ Error creating lesson ${lessonData.lesson_number}: ${error.message}`); } } } console.log(''); // Empty line between grades } await transaction.commit(); // Print summary console.log('\n' + '='.repeat(60)); console.log('šŸ“Š IMPORT SUMMARY'); console.log('='.repeat(60)); console.log(`āœ… Subjects created: ${stats.subjects}`); console.log(`āœ… Chapters created: ${stats.chapters}`); console.log(`āœ… Lessons created: ${stats.lessons}`); if (stats.errors.length > 0) { console.log(`\nāŒ Errors encountered: ${stats.errors.length}`); stats.errors.forEach((err, i) => { console.log(` ${i + 1}. ${err}`); }); } else { console.log('\nšŸŽ‰ No errors!'); } console.log('='.repeat(60)); console.log('\n✨ Import completed successfully!\n'); } catch (error) { await transaction.rollback(); console.error('\nāŒ FATAL ERROR during import:', error); throw error; } } // Run import if called directly if (require.main === module) { importMoveUpData() .then(() => { console.log('āœ… Script completed'); process.exit(0); }) .catch(error => { console.error('āŒ Script failed:', error); process.exit(1); }); } module.exports = { importMoveUpData };