Files
tilbudgivern/complete-task-analyzer.js
2025-10-15 09:57:16 +00:00

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();