162 lines
5.6 KiB
JavaScript
162 lines
5.6 KiB
JavaScript
const mysql = require('mysql2/promise');
|
|
|
|
async function completeTaskCategorization() {
|
|
try {
|
|
const connection = await mysql.createConnection({
|
|
host: 'localhost',
|
|
user: 'tilbudgivern_service',
|
|
password: 'REDACTED_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(); |