Saltar al contenido

Estimación de Costos de Construcción con IA para Revit/IFC

Extrae, clasifica y estima costos de construcción desde Revit/IFC usando IA (OpenAI, Anthropic, xAI Grok). Genera informes detallados HTML y Excel para análisis de costos y materiales.

AI 57 nodos 16 tipos conectado
Cargando workflow...

Nodos

ManualTrigger Code Set OpenAi If SplitInBatches Agent LmChatAnthropic SpreadsheetFile StickyNote ReadBinaryFile ExecuteCommand Merge WriteBinaryFile LmChatOpenAi LmChatXAiGrok

Herramientas

OpenAI Anthropic xAI Grok n8n Code Shell Command

Detalles

ID
7652
Nodos
57
Conex.
Tipos
16

Pertenece a:

¿Qué hace este workflow?

Este workflow avanzado revoluciona la estimación de costos en proyectos de construcción al integrarse directamente con modelos BIM de Revit y IFC. Utiliza la potencia de la Inteligencia Artificial, incluyendo OpenAI, Anthropic y xAI Grok, para extraer, clasificar y estimar costos de elementos constructivos y no constructivos con alta precisión. El proceso se realiza en lotes, asegurando una categorización exhaustiva y el manejo eficiente de grandes volúmenes de datos. Los resultados se presentan en informes detallados en formatos HTML y Excel, facilitando un análisis granular de costos y materiales. Esto proporciona a los equipos de ingeniería, arquitectura y finanzas una visión clara y automatizada del presupuesto del proyecto, minimizando errores manuales y acelerando significativamente el proceso de estimación. Es la solución ideal para empresas que buscan optimizar sus procesos de licitación, control presupuestario y gestión de recursos en la fase de pre-construcción, garantizando mayor eficiencia, precisión y rentabilidad.

¿Cómo funciona?

Este workflow usa 57 nodos conectados con 16 tipos diferentes: ManualTrigger, Code, Set, OpenAi, If y 11 más. La estructura está totalmente conectada — listo para importar.

¿Para quién es?

Diseñado para empresas de Industria y Manufactura y equipos de Operaciones & Finanzas. Nivel avanzado — recomendado para usuarios experimentados. Alto valor de negocio: automatiza una tarea recurrente con impacto directo.

¿Lo quieres en tu empresa?

Lo implementamos por ti end-to-end: integración, deploy, mantenimiento y soporte. Consultoría B2B con Genai Sapiens.

Hablemos de tu proyecto

¿Quieres aprender a hacerlo?

Sprints de 30 días con companion IA + comunidad. Aprende n8n, automatización y agentes IA desde cero o nivel avanzado.

Ver formación Momentum

Workflows similares

\n \n \n\n\n
\n \n
\n
\n
\n
\n
${projectName}
\n
\n
Cost Analysis
\n
\n \n

Project Cost Intelligence

\n
Building Element Cost Analysis & Strategic Insights
\n \n
\n
\n
\n
Report Date
\n
${new Date().toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}
\n
\n
\n
\n
\n
Total Elements
\n
${projectTotals.totalElements.toLocaleString()}
\n
\n
\n
\n
\n
Element Groups
\n
${items.length}
\n
\n
\n
\n
\n
Total Cost
\n
€${(projectTotals.totalCost / 1000).toFixed(1)}K
\n
\n
\n
\n
\n
Confidence
\n
${avgConfidence.toFixed(0)}%
\n
\n
\n
\n
\n
Material Types
\n
${Object.keys(projectTotals.byMaterial).length}
\n
\n
\n
\n
\n
\n \n \n
\n
\n
\n
\n
Total Project Investment
\n
€${(projectTotals.totalCost / 1000).toFixed(1)}K
\n
EUR Total Cost
\n
\n
\n
\n
Average Unit Cost
\n
€${(projectTotals.totalCost / projectTotals.totalElements).toFixed(2)}
\n
Per Element
\n
\n
\n
\n
Material Categories
\n
${Object.keys(projectTotals.byMaterial).length}
\n
Unique Types
\n
\n
\n
\n \n \n
\n
\n
1
\n

Executive Summary

\n
\n
\n
\n

The comprehensive cost analysis reveals a total project estimation of €${projectTotals.totalCost.toFixed(2)} across ${projectTotals.totalElements.toLocaleString()} building elements. This assessment encompasses ${Object.keys(projectTotals.byMaterial).length} distinct material categories, providing detailed insights into cost distribution and optimization opportunities.

\n \n

Our analysis indicates that the top ${Math.min(3, Object.keys(projectTotals.byMaterial).length)} material categories account for ${top3Percentage.toFixed(1)}% of total project costs, suggesting that focused optimization strategies in these areas could yield significant cost reductions without compromising project quality.

\n \n

The cost distribution analysis reveals concentration patterns that align with industry benchmarks, while also identifying specific areas where strategic interventions could improve cost efficiency. The high confidence level of ${avgConfidence.toFixed(0)}% in our estimations provides a solid foundation for decision-making.

\n
\n
\n

Key Strategic Insights

\n
\n
\n
Primary cost driver: ${topMaterial[0]} (${((topMaterial[1].cost / projectTotals.totalCost) * 100).toFixed(1)}%)
\n
\n
\n
\n
High-impact elements: ${highImpactItems} groups (>5% each)
\n
\n
\n
\n
Analysis confidence: ${avgConfidence.toFixed(0)}% average
\n
\n
\n
\n
Coverage rate: ${coveragePercent.toFixed(0)}% analyzed
\n
\n
\n
\n
Cost per m³: €${(projectTotals.totalCost / projectTotals.totalVolume).toFixed(2)}
\n
\n
\n
\n
\n \n \n
\n
\n
2
\n

Cost Distribution Analytics

\n
\n
\n
\n
\n
Material Cost Distribution
\n
Top 6
\n
\n \n
\n
\n
\n
Top Cost Contributors
\n
Top 10
\n
\n \n
\n
\n
\n \n \n
\n
\n
3
\n

Cost Leadership Analysis

\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n ${sortedItems.slice(0, 10).map((item, index) => {\n const data = item.json;\n const costPercent = parseFloat(data['Cost % of Total']) || 0;\n const totalCost = parseFloat(data['Total Cost (EUR)']) || 0;\n const maxCost = parseFloat(sortedItems[0].json['Total Cost (EUR)']) || 1;\n const barWidth = (totalCost / maxCost) * 100;\n \n return `\n \n \n \n \n \n \n \n `;\n }).join('')}\n \n
RankElement GroupMaterial TypeQuantityUnit CostTotal CostImpact
${index + 1}${data['Element Name'] || 'Unknown'}${data['Material (EU Standard)'] || 'Unknown'}${data['Element Count'] || 0} units€${parseFloat(data['Price per Unit (EUR)'] || 0).toFixed(2)}\n
\n €${totalCost.toFixed(2)}\n
\n
\n
${costPercent.toFixed(1)}%
\n
\n
\n \n \n
\n
\n
4
\n

Material Cost Intelligence

\n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n ${Object.entries(projectTotals.byMaterial)\n .sort((a, b) => b[1].cost - a[1].cost)\n .slice(0, 8)\n .map(([material, data]) => {\n const costPercent = (data.cost / projectTotals.totalCost) * 100;\n const avgCost = data.cost / data.elements;\n \n return `\n \n \n \n \n \n \n `;\n }).join('')}\n \n
Material CategoryElementsVolume (m³)Total CostCost ShareAvg. Cost/Unit
${material}${data.elements.toLocaleString()}${data.volume.toFixed(2)}€${data.cost.toFixed(2)}\n
\n ${costPercent.toFixed(1)}%\n
\n
\n
€${avgCost.toFixed(2)}
\n
\n
\n \n \n
\n
\n
5
\n

Cumulative Price Impact

\n
\n

\n Pareto analysis showing concentration of costs\n

\n \n \n
\n \n
\n \n \n
\n
\n
TOP 20% OF ITEMS
\n
${(() => {\n const twentyPercentIndex = Math.floor(sortedItems.length * 0.2);\n let sum = 0;\n for(let i = 0; i < twentyPercentIndex && i < sortedItems.length; i++) {\n sum += parseFloat(sortedItems[i].json['Total Cost (EUR)'] || 0);\n }\n return ((sum / projectTotals.totalCost) * 100).toFixed(0);\n })()}%
\n
of total cost
\n
\n
\n
CONCENTRATION RATIO
\n
${(() => {\n let sum = 0;\n let count = 0;\n for(let i = 0; i < sortedItems.length; i++) {\n sum += parseFloat(sortedItems[i].json['Total Cost (EUR)'] || 0);\n count++;\n if(sum >= projectTotals.totalCost * 0.8) break;\n }\n return ((count / sortedItems.length) * 100).toFixed(0);\n })()}%
\n
items = 80% cost
\n
\n
\n
OPTIMIZATION POTENTIAL
\n
High
\n
cost concentration
\n
\n
\n
\n \n \n
\n
\n
6
\n

Analysis Methodology

\n
\n \n
\n
\n
1
\n
Data Extraction
\n
Automated extraction of building element data from CAD (BIM) projects (Revit or IFC (in certain cases DWG)), including geometric parameters, material specifications, volumes, and quantities. This step ensures comprehensive capture of all relevant project elements using specialized parsing algorithms.
\n
\n
\n
2
\n
AI Classification & Enrichment
\n
AI models classify elements into standardized EU material categories, identify potential substitutes, and enrich data with market insights. Machine learning algorithms analyze patterns to determine accurate material mappings and cost drivers.
\n
\n
\n
3
\n
Cost Estimation & Validation
\n
Integration of real-time market pricing data with quantity takeoffs to compute unit and total costs. Multi-source validation ensures accuracy, with confidence scoring based on data completeness, market volatility, and historical benchmarks.
\n
\n
\n
4
\n
Analytics & Visualization
\n
Aggregation of cost data into strategic insights, including Pareto analysis, distribution charts, and optimization recommendations. Interactive visualizations enable deep dives into cost structures for informed decision-making.
\n
\n
\n
\n \n \n
\n
\n
\n

About This Report

\n

\n \n

\n

\n Analysis Date: ${new Date().toLocaleString()}
\n Confidence Level: ${avgConfidence.toFixed(0)}%
\n Coverage: ${items.length} element groups analyzed\n

\n
\n \n
\n

Contact & Resources

\n \n
\n \n
\n

Report

\n

\n This comprehensive cost analysis report was generated using advanced AI-powered estimation algorithms developed by DataDrivenConstruction.io. Our tools combines machine learning with industry expertise to deliver accurate, actionable insights.\n

\n
\n
\n
\n

© ${new Date().getFullYear()} DataDrivenConstruction.io

\n
\n
\n
\n \n \n\n`;\n\nreturn [{\n json: {\n html: html,\n timestamp: new Date().toISOString(),\n reportType: 'Executive Cost Analysis',\n totalItems: items.length,\n totalCost: projectTotals.totalCost\n }\n}];"},"typeVersion":2},{"id":"e99d0ff0-79ea-4ed1-9878-a23986efe41b","name":"Convert to Binary","type":"n8n-nodes-base.code","position":[1776,1568],"parameters":{"jsCode":"const html = $json.html;\n\nreturn [{\n binary: {\n data: {\n data: Buffer.from(html).toString('base64'),\n mimeType: 'text/html',\n fileName: 'price_estimation_report.html'\n }\n }\n}];"},"typeVersion":2},{"id":"80908a5f-3838-4f47-b579-cadb189cea31","name":"Sticky Note","type":"n8n-nodes-base.stickyNote","position":[224,432],"parameters":{"width":340,"height":116,"content":"⭐ **If you find our tools helpful**, please consider **starring our repository** on [GitHub](https://github.com/datadrivenconstruction/cad2data-Revit-IFC-DWG-DGN-pipeline-with-conversion-validation-qto). \n\nYour support helps us improve and continue developing open solutions for the community!\n"},"typeVersion":1},{"id":"478155e4-eb87-4684-9546-871dfb3577f0","name":"Group Data with AI Rules1","type":"n8n-nodes-base.code","position":[2256,544],"parameters":{"jsCode":"\n const input = $input.first().json;\n const aggregationRules = input.aggregationRules;\n const headerMapping = input.headerMapping;\n const rawData = input.rawData;\n const groupByParamOriginal = input.groupByParam;\n\n\n const groupByParam = headerMapping[groupByParamOriginal] || groupByParamOriginal;\n\n console.log(`Grouping ${rawData.length} items by: ${groupByParam}`);\n\n\n const cleanedData = rawData.map(item => {\n const cleaned = {};\n Object.entries(item).forEach(([key, value]) => {\n const newKey = headerMapping[key] || key;\n cleaned[newKey] = value;\n });\n return cleaned;\n });\n\n\n const grouped = {};\n\n\n cleanedData.forEach(item => {\n const groupKey = item[groupByParam];\n \n if (!groupKey || groupKey === '' || groupKey === null) return;\n \n if (!grouped[groupKey]) {\n grouped[groupKey] = {\n _count: 0,\n _values: {}\n };\n \n \n Object.keys(aggregationRules).forEach(param => {\n if (param !== groupByParam) {\n grouped[groupKey]._values[param] = [];\n }\n });\n }\n \n grouped[groupKey]._count++;\n \n \n Object.entries(item).forEach(([key, value]) => {\n if (key === groupByParam) return;\n \n if (value !== null && value !== undefined && value !== '' && grouped[groupKey]._values[key]) {\n grouped[groupKey]._values[key].push(value);\n }\n });\n });\n\n\n const result = [];\n\n Object.entries(grouped).forEach(([groupKey, groupData]) => {\n const aggregated = {\n [groupByParam]: groupKey,\n 'Element Count': groupData._count\n };\n \n \n Object.entries(groupData._values).forEach(([param, values]) => {\n const rule = aggregationRules[param] || 'first';\n \n if (values.length === 0) {\n aggregated[param] = null;\n return;\n }\n \n switch(rule) {\n case 'sum':\n const numericValues = values.map(v => {\n const num = parseFloat(String(v).replace(',', '.'));\n return isNaN(num) ? 0 : num;\n });\n aggregated[param] = numericValues.reduce((a, b) => a + b, 0);\n \n if (aggregated[param] % 1 !== 0) {\n aggregated[param] = Math.round(aggregated[param] * 100) / 100;\n }\n break;\n \n case 'mean':\n case 'average':\n const avgValues = values.map(v => {\n const num = parseFloat(String(v).replace(',', '.'));\n return isNaN(num) ? null : num;\n }).filter(v => v !== null);\n \n if (avgValues.length > 0) {\n const avg = avgValues.reduce((a, b) => a + b, 0) / avgValues.length;\n aggregated[param] = Math.round(avg * 100) / 100;\n } else {\n aggregated[param] = values[0];\n }\n break;\n \n case 'first':\n default:\n aggregated[param] = values[0];\n break;\n }\n });\n \n result.push({ json: aggregated });\n });\n\n\n result.sort((a, b) => {\n const aVal = a.json[groupByParam];\n const bVal = b.json[groupByParam];\n if (aVal < bVal) return -1;\n if (aVal > bVal) return 1;\n return 0;\n });\n\n console.log(`\\nGrouping complete:`);\n console.log(`- Input items: ${cleanedData.length}`);\n console.log(`- Output groups: ${result.length}`);\n console.log(`- Parameters processed: ${Object.keys(aggregationRules).length}`);\n\n\n const rulesSummary = { sum: [], mean: [], first: [] };\n Object.entries(aggregationRules).forEach(([param, rule]) => {\n if (rulesSummary[rule]) rulesSummary[rule].push(param);\n });\n\n console.log('\\nAggregation summary:');\n if (rulesSummary.sum.length > 0) {\n console.log(`- SUM (${rulesSummary.sum.length}): ${rulesSummary.sum.slice(0, 5).join(', ')}${rulesSummary.sum.length > 5 ? '...' : ''}`);\n }\n if (rulesSummary.mean.length > 0) {\n console.log(`- MEAN (${rulesSummary.mean.length}): ${rulesSummary.mean.slice(0, 5).join(', ')}${rulesSummary.mean.length > 5 ? '...' : ''}`);\n }\n if (rulesSummary.first.length > 0) {\n console.log(`- FIRST (${rulesSummary.first.length}): ${rulesSummary.first.slice(0, 5).join(', ')}${rulesSummary.first.length > 5 ? '...' : ''}`);\n }\n\n return result;"},"typeVersion":2},{"id":"45d8e0e5-591b-4469-9766-501806e5f7a1","name":"Extract Headers and Data","type":"n8n-nodes-base.code","position":[1584,544],"parameters":{"jsCode":"\n const items = $input.all();\n if (items.length === 0) {\n throw new Error('No data found in Excel file');\n }\n\n\n const allHeaders = new Set();\n items.forEach(item => {\n Object.keys(item.json).forEach(key => allHeaders.add(key));\n });\n\n\n const headers = Array.from(allHeaders);\n const cleanedHeaders = headers.map(header => {\n return header.replace(/:\\s*(string|double|int|float|boolean|number)\\s*$/i, '').trim();\n });\n\n\n const headerMapping = {};\n headers.forEach((oldHeader, index) => {\n headerMapping[oldHeader] = cleanedHeaders[index];\n });\n\n\n const sampleValues = {};\n cleanedHeaders.forEach((header, index) => {\n const originalHeader = headers[index];\n for (const item of items) {\n const value = item.json[originalHeader];\n if (value !== null && value !== undefined && value !== '') {\n sampleValues[header] = value;\n break;\n }\n }\n if (!sampleValues[header]) {\n sampleValues[header] = null;\n }\n });\n\n console.log(`Found ${headers.length} unique headers across ${items.length} items`);\n\n\n return [{\n json: {\n headers: cleanedHeaders,\n originalHeaders: headers,\n headerMapping: headerMapping,\n sampleValues: sampleValues,\n totalRows: items.length,\n totalHeaders: headers.length,\n \n rawData: items.map(item => item.json)\n }\n }];"},"typeVersion":2},{"id":"d501723a-bf02-4e1b-8f24-5811500868f4","name":"AI Analyze All Headers","type":"@n8n/n8n-nodes-langchain.openAi","position":[1728,544],"parameters":{"modelId":{"__rl":true,"mode":"list","value":"chatgpt-4o-latest","cachedResultName":"CHATGPT-4O-LATEST"},"options":{"temperature":0.1},"messages":{"values":[{"role":"system","content":"You are an expert in construction classification systems. Analyze building element groups and assign aggregation methods for grouping data.\n\nRules:\n1. 'sum' - for quantities that should be totaled:\n - Volume, Area, Length, Width, Height, Depth, Size\n - Count, Quantity, Number, Amount, Total\n - Thickness, Perimeter, Dimension\n - Weight, Mass, Load\n - Any measurable physical property that accumulates\n\n2. 'mean' (average) - for rates and unit values:\n - Price, Cost, Rate (per unit)\n - Coefficient, Factor, Ratio\n - Percentage, Percent\n - Efficiency, Performance metrics\n - Any per-unit or normalized values\n\n3. 'first' - for descriptive/categorical data:\n - ID, Code, Number (when used as identifier)\n - Name, Title, Description\n - Type, Category, Class, Group\n - Material, Component, Element\n - Project, Building, Location\n - Status, Phase, Stage\n - Any text or categorical field\n\nIMPORTANT: \n- Analyze each header carefully\n- Consider both the header name AND sample value\n- Return aggregation rule for EVERY header provided\n- Use exact header names from input\n\nReturn ONLY valid JSON in this exact format:\n{\n \"aggregation_rules\": {\n \"Header1\": \"sum\",\n \"Header2\": \"first\",\n \"Header3\": \"mean\"\n }\n}"},{"content":"Analyze these {{ $json.totalHeaders }} headers and determine aggregation method for each:\n\nHeaders with sample values:\n{{ JSON.stringify($json.sampleValues, null, 2) }}\n\nProvide aggregation rule for EACH header listed above."}]}},"credentials":{"openAiApi":{"id":"5SwKOx6OOukR6C0w","name":"OpenAi account n8n"}},"typeVersion":1.3},{"id":"03b8267f-6ff1-4793-8a03-1c8ba934dbe9","name":"Read Excel File1","type":"n8n-nodes-base.readBinaryFile","position":[1216,544],"parameters":{"filePath":"={{ $json.path_to_file }}"},"typeVersion":1},{"id":"b265c8fd-6d6b-49a1-802f-e25598569273","name":"Parse Excel1","type":"n8n-nodes-base.spreadsheetFile","position":[1408,544],"parameters":{"options":{"headerRow":true,"sheetName":"={{ $node['Set Parameters1'].json.sheet_name }}","includeEmptyCells":false},"fileFormat":"xlsx"},"typeVersion":2},{"id":"4430c041-3a5d-4235-b092-fcd6c7b4a0ba","name":"Create - Excel filename1","type":"n8n-nodes-base.set","position":[1376,-64],"parameters":{"options":{},"assignments":{"assignments":[{"id":"xlsx-filename-id","name":"xlsx_filename","type":"string","value":"={{ $json[\"project_file\"].slice(0, -4) + \"_rvt.xlsx\" }}"},{"id":"path-to-converter-pass","name":"path_to_converter","type":"string","value":"={{ $json[\"path_to_converter\"] }}"},{"id":"project-file-pass","name":"project_file","type":"string","value":"={{ $json[\"project_file\"] }}"}]}},"typeVersion":3.4},{"id":"7ff93cb0-8c5a-4dfb-97d0-3caaa1940bc8","name":"Check - Does Excel file exist?1","type":"n8n-nodes-base.readBinaryFile","position":[1568,-64],"parameters":{"filePath":"={{ $json[\"xlsx_filename\"] }}"},"typeVersion":1,"continueOnFail":true,"alwaysOutputData":true},{"id":"93cc0ed5-4eac-4907-8638-65010834605a","name":"If - File exists?1","type":"n8n-nodes-base.if","position":[1744,-64],"parameters":{"options":{},"conditions":{"options":{"version":1,"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"e7fb1577-e753-43f5-9f5a-4d5285aeb96e","operator":{"type":"boolean","operation":"equals"},"leftValue":"={{ $binary.data ? true : false }}","rightValue":"={{ true }}"}]}},"typeVersion":2},{"id":"85ead84d-193e-46c0-9d9f-9ccc07f2518b","name":"Extract - Run converter1","type":"n8n-nodes-base.executeCommand","position":[1504,144],"parameters":{"command":"=\"{{$node[\"Setup - Define file paths\"].json[\"path_to_converter\"]}}\" \"{{$node[\"Setup - Define file paths\"].json[\"project_file\"]}}\""},"typeVersion":1,"continueOnFail":true},{"id":"8b6b3e88-90b9-4dbc-81fd-9aa8378f8981","name":"Info - Skip conversion1","type":"n8n-nodes-base.set","position":[1936,-80],"parameters":{"options":{},"assignments":{"assignments":[{"id":"status-id","name":"status","type":"string","value":"File already exists - skipping conversion"},{"id":"xlsx-filename-id","name":"xlsx_filename","type":"string","value":"={{ $node[\"Create - Excel filename1\"].json[\"xlsx_filename\"] }}"}]}},"typeVersion":3.4},{"id":"5b2c4e88-5254-4682-a1d2-8f200952fba4","name":"Check - Did extraction succeed?1","type":"n8n-nodes-base.if","position":[1712,144],"parameters":{"options":{},"conditions":{"options":{"leftValue":"","caseSensitive":true,"typeValidation":"strict"},"combinator":"and","conditions":[{"id":"condition1","operator":{"type":"object","operation":"exists","rightType":"any"},"leftValue":"={{ $node[\"Extract - Run converter1\"].json.error }}","rightValue":""}]}},"typeVersion":2},{"id":"672e73cc-d8a8-4033-8b7a-347ef46ba89a","name":"Error - Show what went wrong1","type":"n8n-nodes-base.set","position":[1936,80],"parameters":{"options":{},"assignments":{"assignments":[{"id":"error-message-id","name":"error_message","type":"string","value":"=Extraction failed: {{ $node[\"Extract - Run converter1\"].json.error || \"Unknown error\" }}"},{"id":"error-code-id","name":"error_code","type":"number","value":"={{ $node[\"Extract - Run converter1\"].json.code || -1 }}"},{"id":"xlsx-filename-error","name":"xlsx_filename","type":"string","value":"={{ $node[\"Create - Excel filename1\"].json[\"xlsx_filename\"] }}"}]}},"typeVersion":3.4},{"id":"a99bdd3b-76e0-4517-8258-c78f009d0653","name":"Set xlsx_filename after success1","type":"n8n-nodes-base.set","position":[1936,256],"parameters":{"options":{},"assignments":{"assignments":[{"id":"xlsx-filename-success","name":"xlsx_filename","type":"string","value":"={{ $node[\"Create - Excel filename1\"].json[\"xlsx_filename\"] }}"}]}},"typeVersion":3.4},{"id":"c60ea846-191c-43d1-a883-131524e2f152","name":"Merge - Continue workflow1","type":"n8n-nodes-base.merge","position":[2128,-16],"parameters":{},"typeVersion":3},{"id":"642d6337-d6a4-4079-ba9d-f42b7c69286f","name":"Set Parameters1","type":"n8n-nodes-base.set","position":[2272,272],"parameters":{"options":{},"assignments":{"assignments":[{"id":"path-id","name":"path_to_file","type":"string","value":"={{ $json.xlsx_filename }}"}]}},"typeVersion":3.4},{"id":"52e54e39-139e-4985-a654-fce750204100","name":"Process AI Response1","type":"n8n-nodes-base.code","position":[2064,544],"parameters":{"jsCode":"\n const aiResponse = $input.first().json;\n const headerData = $node['Extract Headers and Data'].json;\n\n\n let aiRules = {};\n try {\n const content = aiResponse.content || aiResponse.message || aiResponse.response || '';\n console.log('AI Response received, length:', content.length);\n \n \n const jsonMatch = content.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n const parsed = JSON.parse(jsonMatch[0]);\n aiRules = parsed.aggregation_rules || parsed.parameter_aggregation || {};\n console.log(`AI provided ${Object.keys(aiRules).length} rules`);\n } else {\n console.warn('No JSON found in AI response');\n }\n } catch (error) {\n console.error('Error parsing AI response:', error.message);\n }\n\n\n const finalRules = {};\n headerData.headers.forEach(header => {\n if (aiRules[header]) {\n finalRules[header] = aiRules[header];\n } else {\n \n const lowerHeader = header.toLowerCase();\n \n if (lowerHeader.match(/volume|area|length|width|height|count|quantity|thickness|perimeter|depth|size|dimension|weight|mass|total|amount|number/)) {\n finalRules[header] = 'sum';\n } else if (lowerHeader.match(/price|rate|cost|coefficient|factor|percent|ratio|efficiency|avg|average|mean/)) {\n finalRules[header] = 'mean';\n } else {\n finalRules[header] = 'first';\n }\n }\n });\n\n const groupByParam = $node['Setup - Define file paths'].json.group_by;\n\n console.log(`\\nAggregation rules summary:`);\n console.log(`- Total headers: ${headerData.headers.length}`);\n console.log(`- AI rules: ${Object.keys(aiRules).length}`);\n console.log(`- Default rules: ${headerData.headers.length - Object.keys(aiRules).length}`);\n console.log(`- Group by: ${groupByParam}`);\n\n\n return [{\n json: {\n aggregationRules: finalRules,\n headerMapping: headerData.headerMapping,\n headers: headerData.headers,\n originalHeaders: headerData.originalHeaders,\n rawData: headerData.rawData,\n groupByParam: groupByParam,\n totalRows: headerData.totalRows\n }\n }];"},"typeVersion":2},{"id":"0cb35fda-f4ee-4428-9bfa-a6630519908e","name":"Price Analysis and Reporting Block1","type":"n8n-nodes-base.stickyNote","position":[576,1504],"parameters":{"color":5,"width":1920,"height":448,"content":"## Block 4: Price Calculation & Reporting\nThis block:\n- Estimates costs using material types, volumes/areas, and online price searches\n- Creates charts: pie for material distribution, bar for top costs\n- Calculates summary stats like total cost and elements\n- Generates a professional HTML report with visuals\n- Exports detailed data to Excel with multiple sheets (Summary, Details, etc.)\n\nKey nodes:\n- Calculate Project Totals1: Aggregates costs and stats\n- Generate HTML Report: Creates visual report\n- Create Excel File: Builds multi-sheet Excel export"},"typeVersion":1},{"id":"77e86578-5835-4b05-9ed0-0cf4789f9e73","name":"Element Classification Block","type":"n8n-nodes-base.stickyNote","position":[576,752],"parameters":{"color":6,"width":1920,"height":320,"content":"## Block 2: Element Classification\nThis block:\n- Detects category fields (e.g., Category, IFC Type)\n- Uses AI to classify as building elements (e.g., walls, doors) \nor non-building (e.g., annotations, texts)\n- Splits data: building elements go to analysis, \nnon-building are noted separately\n\nKey nodes:\n- Find Category Fields1: Identifies categories\n- AI Classify Categories1: AI classification\n- Is Building Element1: Splits the flow"},"typeVersion":1},{"id":"a96c6ebe-b70a-444e-bbff-10db7b42be76","name":"Material Analysis Block","type":"n8n-nodes-base.stickyNote","position":[576,1088],"parameters":{"color":6,"width":1920,"height":400,"content":"## Block 3: Material Analysis\nThis block:\n- Processes building elements in batches\n- Classifies materials by EU/DE/US standards\n- Determines units (m³, m², etc.) and densities\n- Uses AI (Anthropic) for detailed analysis and price estimation\n- Accumulates results and calculates distributions\n\nKey nodes:\n- Process in Batches1: Handles data in chunks\n- AI Agent Enhanced: AI analysis with tools\n- Accumulate Results: Collects all data"},"typeVersion":1},{"id":"20b27e18-a7bd-491d-9972-6a096633b6ed","name":"Conversion Block","type":"n8n-nodes-base.stickyNote","position":[576,-272],"parameters":{"color":5,"width":1912,"height":708,"content":"## Conversion Block\nThis block:\n- Checks if Excel file exists from Revit project\n- If not, runs converter to extract data\n- If yes, skips to save time\n\nSimply: Converts Revit file to Excel for analysis.\n\nKey nodes:\n- Check - Does Excel file exist?1: Verifies file\n- Extract - Run converter1: Converts if needed"},"typeVersion":1},{"id":"3bd50143-1a76-4ae4-8e4a-904375a45201","name":"Data Loading Block1","type":"n8n-nodes-base.stickyNote","position":[576,464],"parameters":{"color":6,"width":1920,"height":272,"content":"## Block 1: Data Loading & Grouping\nThis block:\n- Loads Excel data\n- Cleans headers\n- Uses AI to decide aggregation (sum for quantities, mean for rates, first for texts)\n- Groups data by parameter (e.g., Type Name)\nSimply: Prepares raw data for classification."},"typeVersion":1},{"id":"95340b29-d6b6-4aa4-b850-4166f1ff186e","name":"Important Notes","type":"n8n-nodes-base.stickyNote","position":[224,128],"parameters":{"width":336,"height":288,"content":"## ⚠️ Important Information\n\n- Pipeline uses AI (OpenAI, Grok, Anthropic) - check credits and limits.\n- Revit converter requires downloaded DDC_Converter_Revit\n- Data is aggregated by groups, volumes are already summed - do not multiply by element count\n- Reports are generated in HTML and in Excel wfor detailed analysis"},"typeVersion":1},{"id":"9887f607-85f0-41d5-92d8-9c6e655f6581","name":"Prepare HTML Path","type":"n8n-nodes-base.code","position":[1936,1568],"parameters":{"jsCode":"// Get the project file path from the original setup\nconst projectFile = $node['Setup - Define file paths'].json.project_file;\nconst htmlContent = $node['Convert to Binary'].json.html || $input.first().binary.data;\n\n// Extract directory path from project file\nconst path = projectFile.substring(0, projectFile.lastIndexOf('\\\\'));\n\n// Create filename with timestamp\nconst timestamp = new Date().toISOString().slice(0,10);\nconst htmlFileName = `CO2_Analysis_Report_${timestamp}.html`;\nconst fullPath = `${path}\\\\${htmlFileName}`;\n\nconsole.log('Project file:', projectFile);\nconsole.log('Directory:', path);\nconsole.log('HTML file will be saved to:', fullPath);\n\nreturn [{\n json: {\n html_filename: htmlFileName,\n full_path: fullPath,\n directory: path,\n project_file: projectFile\n },\n binary: $input.first().binary\n}];"},"typeVersion":2},{"id":"24be1fea-bdcb-491b-afb8-170431f8f198","name":"Write HTML to Project Folder","type":"n8n-nodes-base.writeBinaryFile","position":[2112,1568],"parameters":{"options":{},"fileName":"={{ $json.full_path }}"},"typeVersion":1},{"id":"c4d015d4-b040-4aa7-a4c5-cc22d3ab7eb4","name":"Open HTML in Browser","type":"n8n-nodes-base.executeCommand","position":[2288,1568],"parameters":{"command":"=start \"\" \"{{ $json.full_path }}\""},"typeVersion":1},{"id":"01bcb7a4-534b-46ee-9462-b8f1aaf813e8","name":"Prepare Excel Path1","type":"n8n-nodes-base.code","position":[2112,1744],"parameters":{"jsCode":"// Similar logic for Excel file\nconst projectFile = $node['Setup - Define file paths'].json.project_file;\n\n// Extract directory path from project file\nconst path = projectFile.substring(0, projectFile.lastIndexOf('\\\\'));\n\n// Create filename with timestamp\nconst timestamp = new Date().toISOString().slice(0,10);\nconst excelFileName = `CO2_Analysis_Professional_Report_${timestamp}.xlsx`;\nconst fullPath = `${path}\\\\${excelFileName}`;\n\nconsole.log('Excel file will be saved to:', fullPath);\n\nreturn [{\n json: {\n excel_filename: excelFileName,\n full_path: fullPath,\n directory: path,\n project_file: projectFile\n },\n binary: $input.first().binary\n}];"},"typeVersion":2},{"id":"694efc44-ce97-44d1-a34d-84fce917a0e9","name":"Write Excel to Project Folder1","type":"n8n-nodes-base.writeBinaryFile","position":[2288,1744],"parameters":{"options":{},"fileName":"={{ $json.full_path }}"},"typeVersion":1},{"id":"297ac011-ea69-4975-b9e5-e14a1b6730b9","name":"Setup - Define file paths","type":"n8n-nodes-base.set","position":[1168,-64],"parameters":{"options":{},"assignments":{"assignments":[{"id":"9cbd4ec9-df24-41e8-b47a-720a4cdb733b","name":"path_to_converter","type":"string","value":"C:\\Users\\Artem Boiko\\Desktop\\n8n pipelines\\DDC_Converter_Revit\\datadrivenlibs\\RvtExporter.exe"},{"id":"aa834467-80fb-476a-bac1-6728478834f0","name":"project_file","type":"string","value":"C:\\Users\\Artem Boiko\\Desktop\\n8n\\cad2data-Revit-IFC-DWG-DGN-pipeline-with-conversion-validation-qto-main\\cad2data-Revit-IFC-DWG-DGN-pipeline-with-conversion-validation-qto-main\\Sample_Projects\\ra_basic_sample_revit_somegroups.rvt"},{"id":"4e4f5e6f-7a8b-4c5d-9e0f-1a2b3c4d5e6f","name":"group_by","type":"string","value":"Type Name"},{"id":"5f6a7b8c-9d0e-4f1a-2b3c-4d5e6f7a8b9c","name":"country","type":"string","value":"Germany"}]}},"typeVersion":3.4},{"id":"a2363cf4-bd62-410c-a52b-e5d2c4760940","name":"Conversion Block1","type":"n8n-nodes-base.stickyNote","position":[576,-400],"parameters":{"color":5,"width":952,"height":112,"content":"# Project Cost Calculation for Revit and IFC with AI (LLM) \nDataDrivenConstruction [GitHub](https://github.com/datadrivenconstruction/cad2data-Revit-IFC-DWG-DGN-pipeline-with-conversion-validation-qto)"},"typeVersion":1},{"id":"0af0bac5-4f11-4a99-bc74-0d99827754cb","name":"Setup Instructions","type":"n8n-nodes-base.stickyNote","position":[224,-272],"parameters":{"width":336,"height":384,"content":"## 📝 Setup Instructions\n\n1. In the 'Setup - Define file paths' node, specify:\n - Path to converter (RvtExporter.exe)\n - Path to project file (.rvt)\n - Grouping parameter (group_by, e.g. 'Type Name', 'IfcType' for IFC or other)\n - Country (country for which the values will be calculated, e.g. 'Germany'or 'Brazil')\n\n2. Ensure API keys for OpenAI and Anthropic are set in credentials or just connect other models that you use in your work (of course, these can be open source LLMs)"},"typeVersion":1},{"id":"b2243495-e0c0-4bd7-8118-701d33d20c6f","name":"OpenAI Chat Model","type":"@n8n/n8n-nodes-langchain.lmChatOpenAi","position":[1616,1312],"parameters":{"model":{"__rl":true,"mode":"list","value":"gpt-3.5-turbo","cachedResultName":"gpt-3.5-turbo"},"options":{}},"credentials":{"openAiApi":{"id":"5SwKOx6OOukR6C0w","name":"OpenAi account n8n"}},"typeVersion":1.2},{"id":"2c6de4d3-cf23-4748-aff3-2bdc1f602ae5","name":"xAI Grok Chat Model","type":"@n8n/n8n-nodes-langchain.lmChatXAiGrok","position":[1776,1312],"parameters":{"options":{}},"credentials":{"xAiApi":{"id":"JKhw9fFrSig9QNQB","name":"xAi account"}},"typeVersion":1},{"id":"80f0dd52-efa5-45ce-9812-d75ec19d9fb0","name":"Sticky Note2","type":"n8n-nodes-base.stickyNote","position":[1104,-240],"parameters":{"color":4,"width":224,"height":368,"content":"## ⬇️ Only modify the variables here \n— everything else works automatically"},"typeVersion":1},{"id":"9d8c252c-4dd6-434e-a3c6-18f7a0b1d2e5","name":"On the standard 3D View","type":"n8n-nodes-base.if","position":[1184,816],"parameters":{"conditions":{"boolean":[{"value1":"={{ $json['On the standard 3D View'] }}","value2":true}]}},"typeVersion":1},{"id":"549c23ba-0839-4221-9401-fc4b63011211","name":"Non-3D View Elements Output","type":"n8n-nodes-base.set","position":[1360,912],"parameters":{"options":{},"assignments":{"assignments":[{"id":"message","name":"message","type":"string","value":"Elements not visible in standard 3D view"},{"id":"filtered_count","name":"filtered_count","type":"number","value":"={{ $input.all().length }}"},{"id":"reason","name":"reason","type":"string","value":"Parameter 'On the standard 3D View' is not True"}]}},"typeVersion":3.4}],"active":false,"pinData":{},"settings":{"executionOrder":"v1"},"versionId":"df99202c-1ae9-46a0-9d9f-1b46aa6273ea","connections":{"Parse Excel1":{"main":[[{"node":"Extract Headers and Data","type":"main","index":0}]]},"Set Parameters1":{"main":[[{"node":"Read Excel File1","type":"main","index":0}]]},"Read Excel File1":{"main":[[{"node":"Parse Excel1","type":"main","index":0}]]},"AI Agent Enhanced":{"main":[[{"node":"Parse Enhanced Response","type":"main","index":0}]]},"Convert to Binary":{"main":[[{"node":"Prepare HTML Path","type":"main","index":0}]]},"Create Excel File":{"main":[[{"node":"Enhance Excel Output","type":"main","index":0}]]},"Prepare HTML Path":{"main":[[{"node":"Write HTML to Project Folder","type":"main","index":0}]]},"Accumulate Results":{"main":[[{"node":"Check If All Batches Done","type":"main","index":0}]]},"If - File exists?1":{"main":[[{"node":"Info - Skip conversion1","type":"main","index":0}],[{"node":"Extract - Run converter1","type":"main","index":0}]]},"Prepare Excel Data":{"main":[[{"node":"Create Excel File","type":"main","index":0}]]},"Clean Empty Values1":{"main":[[{"node":"Prepare Enhanced Prompts","type":"main","index":0}]]},"Collect All Results":{"main":[[{"node":"Calculate Project Totals1","type":"main","index":0}]]},"Prepare Excel Path1":{"main":[[{"node":"Write Excel to Project Folder1","type":"main","index":0}]]},"Process in Batches1":{"main":[[{"node":"Clean Empty Values1","type":"main","index":0}]]},"Enhance Excel Output":{"main":[[{"node":"Prepare Excel Path1","type":"main","index":0}]]},"Generate HTML Report":{"main":[[{"node":"Convert to Binary","type":"main","index":0}]]},"Is Building Element1":{"main":[[{"node":"Process in Batches1","type":"main","index":0}],[{"node":"Non-Building Elements Output1","type":"main","index":0}]]},"Process AI Response1":{"main":[[{"node":"Group Data with AI Rules1","type":"main","index":0}]]},"Anthropic Chat Model1":{"ai_languageModel":[[{"node":"AI Agent Enhanced","type":"ai_languageModel","index":0}]]},"Find Category Fields1":{"main":[[{"node":"AI Classify Categories1","type":"main","index":0}]]},"AI Analyze All Headers":{"main":[[{"node":"Process AI Response1","type":"main","index":0}]]},"AI Classify Categories1":{"main":[[{"node":"Apply Classification to Groups1","type":"main","index":0}]]},"Info - Skip conversion1":{"main":[[{"node":"Merge - Continue workflow1","type":"main","index":0}]]},"On the standard 3D View":{"main":[[{"node":"Find Category Fields1","type":"main","index":0}],[{"node":"Non-3D View Elements Output","type":"main","index":0}]]},"Parse Enhanced Response":{"main":[[{"node":"Accumulate Results","type":"main","index":0}]]},"Create - Excel filename1":{"main":[[{"node":"Check - Does Excel file exist?1","type":"main","index":0}]]},"Extract - Run converter1":{"main":[[{"node":"Check - Did extraction succeed?1","type":"main","index":0}]]},"Extract Headers and Data":{"main":[[{"node":"AI Analyze All Headers","type":"main","index":0}]]},"Prepare Enhanced Prompts":{"main":[[{"node":"AI Agent Enhanced","type":"main","index":0}]]},"Calculate Project Totals1":{"main":[[{"node":"Prepare Excel Data","type":"main","index":0},{"node":"Generate HTML Report","type":"main","index":0}]]},"Check If All Batches Done":{"main":[[{"node":"Collect All Results","type":"main","index":0}],[{"node":"Process in Batches1","type":"main","index":0}]]},"Group Data with AI Rules1":{"main":[[{"node":"On the standard 3D View","type":"main","index":0}]]},"Setup - Define file paths":{"main":[[{"node":"Create - Excel filename1","type":"main","index":0}]]},"Merge - Continue workflow1":{"main":[[{"node":"Set Parameters1","type":"main","index":0}]]},"Write HTML to Project Folder":{"main":[[{"node":"Open HTML in Browser","type":"main","index":0}]]},"Error - Show what went wrong1":{"main":[[{"node":"Merge - Continue workflow1","type":"main","index":1}]]},"Apply Classification to Groups1":{"main":[[{"node":"Is Building Element1","type":"main","index":0}]]},"Check - Does Excel file exist?1":{"main":[[{"node":"If - File exists?1","type":"main","index":0}]]},"Check - Did extraction succeed?1":{"main":[[{"node":"Error - Show what went wrong1","type":"main","index":0}],[{"node":"Set xlsx_filename after success1","type":"main","index":0}]]},"Set xlsx_filename after success1":{"main":[[{"node":"Merge - Continue workflow1","type":"main","index":1}]]},"When clicking ‘Execute workflow’":{"main":[[{"node":"Setup - Define file paths","type":"main","index":0}]]}}}