update
This commit is contained in:
18
scripts/add-lesson-content-type-column.js
Normal file
18
scripts/add-lesson-content-type-column.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const { sequelize } = require('../config/database');
|
||||
|
||||
async function addColumn() {
|
||||
try {
|
||||
await sequelize.query(`
|
||||
ALTER TABLE lessons
|
||||
ADD COLUMN lesson_content_type ENUM('vocabulary', 'grammar', 'phonics', 'review', 'mixed') NULL
|
||||
COMMENT 'Loại nội dung: vocabulary, grammar, phonics, review, mixed'
|
||||
`);
|
||||
console.log('✅ Column lesson_content_type added successfully');
|
||||
} catch (error) {
|
||||
console.error('❌ Error:', error.message);
|
||||
} finally {
|
||||
await sequelize.close();
|
||||
}
|
||||
}
|
||||
|
||||
addColumn();
|
||||
237
scripts/import-moveup-data.js
Normal file
237
scripts/import-moveup-data.js
Normal file
@@ -0,0 +1,237 @@
|
||||
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 };
|
||||
Reference in New Issue
Block a user