const mysql = require('mysql2/promise'); async function completeTaskCategorization() { try { const connection = await mysql.createConnection({ host: 'localhost', user: 'tilbudgivern_service', password: process.env.DB_PASSWORD, database: 'tilbudgivern' }); console.log('🎯 KOMPLET KATEGORISERING AF ALLE 1093 PROJEKTER'); console.log('='.repeat(60)); // Detaljeret kategorisering med alle mønstre const categories = { 'Tag arbejde': [ 'tag', 'roof', 'spær', 'tegl', 'eternit', 'b7', 'b6', 'taghætte', 'tagrender', 'rygning', 'tagplade', 'tagsten', 'betontegl', 'vingetegl' ], 'Køkken': [ 'køkken', 'kitchen', 'bordplade', 'køkkenø', 'køkkenmontage' ], 'Badeværelse': [ 'bad', 'bathroom', 'bruse', 'toilet', 'brusekabine', 'badekar' ], 'Gulv arbejde': [ 'gulv', 'floor', 'planke', 'flis', 'parket', 'klik', 'sildebensgulv' ], 'Vinduer og døre': [ 'vindue', 'window', 'dør', 'door', 'karm', 'vindskarm', 'opgangsdør' ], 'Terrasse og udendørs': [ 'terrasse', 'carport', 'altan', 'udendørs', 'deck', 'udekøkken', 'garage' ], 'Ombygning og renovering': [ 'ombygning', 'renovering', 'udvidelse', 'opretning', 'nedrivning' ], 'Tilbygning': [ 'tilbygning', 'nybygning', 'extension' ], 'Loft og vægge': [ 'loft', 'væg', 'gips', 'panel', 'isolering', 'dampspærre', 'facade' ], 'Service og reparation': [ 'service', 'reparation', 'udskift', 'justering', 'rep.', 'montage' ], 'Skabe og møbler': [ 'skab', 'skabe', 'hylde', 'møbler', 'reol' ], 'Kvist og ovenlys': [ 'kvist', 'ovenlys', 'tagvindue', 'velux' ] }; // Kategoriser alle projekter const [allProjects] = await connection.execute(` SELECT id, project_name, total_hours, total_quote_value, customer_name FROM project_analytics WHERE total_hours > 0 `); const categorizedProjects = {}; const uncategorized = []; // Initialiser kategorier Object.keys(categories).forEach(cat => { categorizedProjects[cat] = []; }); // Kategoriser hvert projekt allProjects.forEach(project => { const projectName = project.project_name.toLowerCase(); let categorized = false; for (const [category, keywords] of Object.entries(categories)) { for (const keyword of keywords) { if (projectName.includes(keyword)) { categorizedProjects[category].push(project); categorized = true; break; } } if (categorized) break; } if (!categorized) { uncategorized.push(project); } }); // Vis resultater console.log('\n📊 KATEGORISERINGS RESULTATER:'); console.log('='.repeat(50)); let totalCategorized = 0; Object.entries(categorizedProjects).forEach(([category, projects]) => { if (projects.length > 0) { totalCategorized += projects.length; const avgHours = projects.reduce((sum, p) => sum + parseFloat(p.total_hours), 0) / projects.length; const totalValue = projects.reduce((sum, p) => sum + parseFloat(p.total_quote_value || 0), 0); console.log(`\n🏗️ ${category.toUpperCase()}:`); console.log(` Projekter: ${projects.length}`); console.log(` Gennemsnit timer: ${avgHours.toFixed(1)}h`); console.log(` Total værdi: ${totalValue.toLocaleString()} kr`); console.log(` Timer spænd: ${Math.min(...projects.map(p => parseFloat(p.total_hours))).toFixed(1)}h - ${Math.max(...projects.map(p => parseFloat(p.total_hours))).toFixed(1)}h`); // Top 3 projekter i kategorien const top3 = projects .sort((a, b) => parseFloat(b.total_hours) - parseFloat(a.total_hours)) .slice(0, 3); console.log(' Top 3 projekter:'); top3.forEach((proj, i) => { console.log(` ${i+1}. ${proj.project_name.substring(0, 40)} (${parseFloat(proj.total_hours).toFixed(1)}h)`); }); } }); console.log(`\n❓ IKKE-KATEGORISEREDE PROJEKTER: ${uncategorized.length}`); console.log(`✅ KATEGORISEREDE: ${totalCategorized} (${(totalCategorized/allProjects.length*100).toFixed(1)}%)`); // Vis eksempler på ikke-kategoriserede if (uncategorized.length > 0) { console.log('\n❓ EKSEMPLER PÅ IKKE-KATEGORISEREDE:'); uncategorized.slice(0, 10).forEach((proj, i) => { console.log(` ${i+1}. ${proj.project_name.substring(0, 50)} (${parseFloat(proj.total_hours).toFixed(1)}h)`); }); } // Størst potentiale for forbedring console.log('\n🚀 MEST PROFITABLE KATEGORIER:'); console.log('='.repeat(40)); const profitableCategories = Object.entries(categorizedProjects) .filter(([_, projects]) => projects.length > 0) .map(([category, projects]) => ({ category, count: projects.length, avgHours: projects.reduce((sum, p) => sum + parseFloat(p.total_hours), 0) / projects.length, totalValue: projects.reduce((sum, p) => sum + parseFloat(p.total_quote_value || 0), 0) })) .sort((a, b) => b.totalValue - a.totalValue); profitableCategories.slice(0, 5).forEach((cat, i) => { console.log(`${i+1}. ${cat.category}: ${cat.count} projekter, ${cat.totalValue.toLocaleString()} kr total`); }); await connection.end(); } catch (error) { console.error('❌ Fejl:', error.message); } } completeTaskCategorization();