238 lines
8.2 KiB
JavaScript
238 lines
8.2 KiB
JavaScript
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 };
|