/** * Script: Dump và Sync All Teachers * Mục đích: Kiểm tra và tạo user_profile cho TẤT CẢ teachers trong hệ thống * * Usage: * node src/scripts/dump-and-sync-teachers.js * node src/scripts/dump-and-sync-teachers.js --auto-fix */ require('dotenv').config(); const { sequelize, TeacherDetail, UserProfile, UsersAuth } = require('../models'); const teacherProfileService = require('../services/teacherProfileService'); async function dumpAllTeachers() { console.log('📊 Dumping All Teachers Information...\n'); console.log('='.repeat(80)); try { // Get all teachers (without join to avoid association issues) const teachers = await TeacherDetail.findAll({ order: [['created_at', 'ASC']], }); console.log(`\n📈 Total Teachers: ${teachers.length}\n`); const withProfile = []; const withoutProfile = []; const invalidUserId = []; for (const teacher of teachers) { const teacherInfo = { id: teacher.id, user_id: teacher.user_id, teacher_code: teacher.teacher_code, teacher_type: teacher.teacher_type, status: teacher.status, created_at: teacher.created_at, }; // Check if user_id exists in users_auth const userAuth = await UsersAuth.findByPk(teacher.user_id, { attributes: ['id', 'username', 'email', 'is_active'], }); if (!userAuth) { invalidUserId.push({ ...teacherInfo, issue: 'user_id not found in users_auth', }); continue; } teacherInfo.username = userAuth.username; teacherInfo.email = userAuth.email; teacherInfo.is_active = userAuth.is_active; // Check if profile exists const userProfile = await UserProfile.findOne({ where: { user_id: teacher.user_id }, }); if (userProfile) { teacherInfo.profile = { id: userProfile.id, full_name: userProfile.full_name, phone: userProfile.phone, needs_update: userProfile.etc?.needs_profile_update, }; withProfile.push(teacherInfo); } else { withoutProfile.push(teacherInfo); } } // Display results console.log('✅ Teachers WITH Profile:', withProfile.length); if (withProfile.length > 0) { console.log('\nSample (first 5):'); withProfile.slice(0, 5).forEach((t, i) => { console.log(` ${i + 1}. ${t.teacher_code} (${t.teacher_type}) - ${t.username}`); console.log(` Profile: ${t.profile.full_name} | Phone: ${t.profile.phone || 'N/A'}`); }); if (withProfile.length > 5) { console.log(` ... and ${withProfile.length - 5} more\n`); } } console.log('\n❌ Teachers WITHOUT Profile:', withoutProfile.length); if (withoutProfile.length > 0) { console.log('\nList:'); withoutProfile.forEach((t, i) => { console.log(` ${i + 1}. ${t.teacher_code} (${t.teacher_type}) - ${t.username} - user_id: ${t.user_id}`); }); } console.log('\n⚠️ Teachers with Invalid user_id:', invalidUserId.length); if (invalidUserId.length > 0) { console.log('\nList:'); invalidUserId.forEach((t, i) => { console.log(` ${i + 1}. ${t.teacher_code} - ${t.issue} - user_id: ${t.user_id}`); }); } console.log('\n' + '='.repeat(80)); return { total: teachers.length, withProfile: withProfile.length, withoutProfile: withoutProfile.length, invalidUserId: invalidUserId.length, teachers: { withProfile, withoutProfile, invalidUserId, } }; } catch (error) { console.error('❌ Dump failed:', error.message); throw error; } } async function autoFixProfiles(teachersData) { console.log('\n🔧 Starting Auto-Fix for Teachers without Profile...\n'); const { withoutProfile } = teachersData.teachers; if (withoutProfile.length === 0) { console.log('✅ No teachers need fixing. All have profiles!\n'); return { success: 0, failed: 0 }; } console.log(`📋 Processing ${withoutProfile.length} teachers...\n`); const results = { success: [], failed: [], }; for (const teacher of withoutProfile) { try { console.log(`Processing: ${teacher.teacher_code} (${teacher.username})...`); // Create profile using service const userProfile = await UserProfile.create({ user_id: teacher.user_id, full_name: teacherProfileService._generateFullNameFromCode(teacher.teacher_code), first_name: '', last_name: '', phone: '', date_of_birth: null, gender: null, address: '', school_id: null, city: '', district: '', avatar_url: null, etc: { teacher_code: teacher.teacher_code, teacher_type: teacher.teacher_type, username: teacher.username, synced_from: 'dump-and-sync-script', needs_profile_update: true, synced_at: new Date().toISOString(), } }); results.success.push({ teacher_code: teacher.teacher_code, username: teacher.username, user_id: teacher.user_id, profile_id: userProfile.id, full_name: userProfile.full_name, }); console.log(` ✅ Created profile: ${userProfile.full_name}\n`); } catch (error) { results.failed.push({ teacher_code: teacher.teacher_code, username: teacher.username, error: error.message, }); console.log(` ❌ Failed: ${error.message}\n`); } } // Summary console.log('\n' + '='.repeat(80)); console.log('📊 AUTO-FIX SUMMARY'); console.log('='.repeat(80)); console.log(`✅ Success: ${results.success.length}`); console.log(`❌ Failed: ${results.failed.length}`); if (results.success.length > 0) { console.log('\n✅ Successfully Created Profiles:'); results.success.forEach((item, i) => { console.log(` ${i + 1}. ${item.teacher_code} → ${item.full_name}`); console.log(` user_id: ${item.user_id} | profile_id: ${item.profile_id}`); }); } if (results.failed.length > 0) { console.log('\n❌ Failed:'); results.failed.forEach((item, i) => { console.log(` ${i + 1}. ${item.teacher_code} - ${item.error}`); }); } console.log('\n' + '='.repeat(80)); return { success: results.success.length, failed: results.failed.length, details: results, }; } async function main() { console.log('\n🚀 Teacher Profile Dump & Sync Tool\n'); try { // Parse arguments const args = process.argv.slice(2); const autoFix = args.includes('--auto-fix'); // Connect to database await sequelize.authenticate(); console.log('✅ Database connected\n'); // Step 1: Dump all teachers const dumpResults = await dumpAllTeachers(); // Step 2: Ask for auto-fix if not specified if (!autoFix && dumpResults.withoutProfile > 0) { console.log('\n⚠️ Found teachers without profile!'); console.log('💡 Run with --auto-fix to create profiles automatically:'); console.log(' node src/scripts/dump-and-sync-teachers.js --auto-fix\n'); process.exit(0); } // Step 3: Auto-fix if requested if (autoFix && dumpResults.withoutProfile > 0) { const fixResults = await autoFixProfiles(dumpResults); console.log('\n🎉 COMPLETED!'); console.log(` Total: ${dumpResults.total} teachers`); console.log(` Fixed: ${fixResults.success} profiles`); console.log(` Already had: ${dumpResults.withProfile} profiles`); console.log(` Failed: ${fixResults.failed} profiles\n`); } else if (autoFix) { console.log('\n✅ All teachers already have profiles! Nothing to fix.\n'); } process.exit(0); } catch (error) { console.error('\n❌ Error:', error.message); console.error(error.stack); process.exit(1); } } // Run main();