Get the FREE Ultimate OpenClaw Setup Guide →

excel-to-rvt

Scanned
npx machina-cli add skill datadrivenconstruction/DDC_Skills_for_AI_Agents_in_Construction/excel-to-rvt --openclaw
Files (1)
SKILL.md
12.1 KB

Excel to RVT Import

Note: RVT is the file format. Examples may reference Autodesk® Revit® APIs. Autodesk and Revit are registered trademarks of Autodesk, Inc.

Business Case

Problem Statement

External data (costs, specifications, classifications) lives in Excel but needs to update Revit:

  • Cost estimates need to link to model elements
  • Classification codes need assignment
  • Custom parameters need population
  • Manual entry is slow and error-prone

Solution

Automated import of Excel data into Revit using the DDC ImportExcelToRevit tool and Dynamo workflows.

Business Value

  • Automation - Batch update thousands of parameters
  • Accuracy - Eliminate manual data entry errors
  • Sync - Keep external data in sync with model
  • Flexibility - Update any writable parameter

Technical Implementation

Methods

  1. ImportExcelToRevit CLI - Direct command-line update
  2. Dynamo Script - Visual programming approach
  3. Revit API - Full programmatic control

ImportExcelToRevit CLI

ImportExcelToRevit.exe <model.rvt> <data.xlsx> [options]
OptionDescription
-sheetExcel sheet name
-idcolElement ID column
-mappingParameter mapping file

Python Implementation

import subprocess
import pandas as pd
from pathlib import Path
from typing import Dict, Any, List, Optional, Tuple
from dataclasses import dataclass
import json


@dataclass
class ImportResult:
    """Result of Excel import to Revit."""
    elements_processed: int
    elements_updated: int
    elements_failed: int
    parameters_updated: int
    errors: List[str]


class ExcelToRevitImporter:
    """Import Excel data into Revit models."""

    def __init__(self, tool_path: str = "ImportExcelToRevit.exe"):
        self.tool_path = Path(tool_path)

    def import_data(self, revit_file: str,
                    excel_file: str,
                    sheet_name: str = "Elements",
                    id_column: str = "ElementId",
                    parameter_mapping: Dict[str, str] = None) -> ImportResult:
        """Import Excel data into Revit."""

        # Build command
        cmd = [
            str(self.tool_path),
            revit_file,
            excel_file,
            "-sheet", sheet_name,
            "-idcol", id_column
        ]

        # Add mapping file if provided
        if parameter_mapping:
            mapping_file = self._create_mapping_file(parameter_mapping)
            cmd.extend(["-mapping", mapping_file])

        # Execute
        result = subprocess.run(cmd, capture_output=True, text=True)

        # Parse result (format depends on tool)
        return self._parse_result(result)

    def _create_mapping_file(self, mapping: Dict[str, str]) -> str:
        """Create temporary mapping file."""
        mapping_path = Path("temp_mapping.json")
        with open(mapping_path, 'w') as f:
            json.dump(mapping, f)
        return str(mapping_path)

    def _parse_result(self, result: subprocess.CompletedProcess) -> ImportResult:
        """Parse CLI result."""
        # This is placeholder - actual parsing depends on tool output
        if result.returncode == 0:
            return ImportResult(
                elements_processed=0,
                elements_updated=0,
                elements_failed=0,
                parameters_updated=0,
                errors=[]
            )
        else:
            return ImportResult(
                elements_processed=0,
                elements_updated=0,
                elements_failed=0,
                parameters_updated=0,
                errors=[result.stderr]
            )


class DynamoScriptGenerator:
    """Generate Dynamo scripts for Revit data import."""

    def generate_parameter_update_script(self,
                                         mappings: Dict[str, str],
                                         excel_path: str,
                                         output_path: str) -> str:
        """Generate Dynamo Python script for parameter updates."""

        mappings_json = json.dumps(mappings)

        script = f'''
# Dynamo Python Script - Excel to Revit Parameter Update
# Generated by DDC

import clr
import sys
sys.path.append(r'C:\\Program Files (x86)\\IronPython 2.7\\Lib')

clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')
clr.AddReference('Microsoft.Office.Interop.Excel')

from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
from Autodesk.Revit.DB import *
import Microsoft.Office.Interop.Excel as Excel

# Configuration
excel_path = r'{excel_path}'
mappings = {mappings_json}

# Open Excel
excel_app = Excel.ApplicationClass()
excel_app.Visible = False
workbook = excel_app.Workbooks.Open(excel_path)
worksheet = workbook.Worksheets[1]

# Get Revit document
doc = DocumentManager.Instance.CurrentDBDocument

# Read Excel data
used_range = worksheet.UsedRange
rows = used_range.Rows.Count
cols = used_range.Columns.Count

# Find column indices
headers = {{}}
for col in range(1, cols + 1):
    header = str(worksheet.Cells[1, col].Value2 or '')
    headers[header] = col

# Process rows
TransactionManager.Instance.EnsureInTransaction(doc)

updated_count = 0
error_count = 0

for row in range(2, rows + 1):
    try:
        # Get element ID
        element_id_col = headers.get('ElementId', 1)
        element_id = int(worksheet.Cells[row, element_id_col].Value2 or 0)

        element = doc.GetElement(ElementId(element_id))
        if not element:
            continue

        # Update mapped parameters
        for excel_col, revit_param in mappings.items():
            if excel_col in headers:
                col_idx = headers[excel_col]
                value = worksheet.Cells[row, col_idx].Value2

                if value is not None:
                    param = element.LookupParameter(revit_param)
                    if param and not param.IsReadOnly:
                        if param.StorageType == StorageType.Double:
                            param.Set(float(value))
                        elif param.StorageType == StorageType.Integer:
                            param.Set(int(value))
                        elif param.StorageType == StorageType.String:
                            param.Set(str(value))

        updated_count += 1

    except Exception as e:
        error_count += 1

TransactionManager.Instance.TransactionTaskDone()

# Cleanup
workbook.Close(False)
excel_app.Quit()

OUT = f"Updated: {{updated_count}}, Errors: {{error_count}}"
'''

        with open(output_path, 'w') as f:
            f.write(script)

        return output_path

    def generate_schedule_creator(self,
                                  schedule_name: str,
                                  category: str,
                                  fields: List[str],
                                  output_path: str) -> str:
        """Generate script to create Revit schedule from Excel structure."""

        fields_json = json.dumps(fields)

        script = f'''
# Dynamo Python Script - Create Schedule
# Generated by DDC

import clr
clr.AddReference('RevitAPI')
clr.AddReference('RevitServices')

from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
from Autodesk.Revit.DB import *

doc = DocumentManager.Instance.CurrentDBDocument
fields = {fields_json}

# Get category
category = Category.GetCategory(doc, BuiltInCategory.OST_{category})

TransactionManager.Instance.EnsureInTransaction(doc)

# Create schedule
schedule = ViewSchedule.CreateSchedule(doc, category.Id)
schedule.Name = "{schedule_name}"

# Add fields
definition = schedule.Definition

for field_name in fields:
    # Find schedulable field
    for sf in definition.GetSchedulableFields():
        if sf.GetName(doc) == field_name:
            definition.AddField(sf)
            break

TransactionManager.Instance.TransactionTaskDone()

OUT = schedule
'''

        with open(output_path, 'w') as f:
            f.write(script)

        return output_path


class ExcelDataValidator:
    """Validate Excel data before Revit import."""

    def __init__(self, revit_elements: pd.DataFrame):
        """Initialize with exported Revit elements."""
        self.revit_data = revit_elements
        self.valid_ids = set(revit_elements['ElementId'].astype(str).tolist())

    def validate_import_data(self, import_df: pd.DataFrame,
                             id_column: str = 'ElementId') -> Dict[str, Any]:
        """Validate import data against Revit export."""

        results = {
            'valid': True,
            'total_rows': len(import_df),
            'matching_ids': 0,
            'missing_ids': [],
            'invalid_ids': [],
            'warnings': []
        }

        import_ids = import_df[id_column].astype(str).tolist()

        for import_id in import_ids:
            if import_id in self.valid_ids:
                results['matching_ids'] += 1
            else:
                results['invalid_ids'].append(import_id)

        if results['invalid_ids']:
            results['valid'] = False
            results['warnings'].append(
                f"{len(results['invalid_ids'])} element IDs not found in Revit model"
            )

        results['match_rate'] = round(
            results['matching_ids'] / results['total_rows'] * 100, 1
        ) if results['total_rows'] > 0 else 0

        return results

    def check_parameter_types(self, import_df: pd.DataFrame,
                              type_definitions: Dict[str, str]) -> List[str]:
        """Check if values match expected parameter types."""

        errors = []

        for column, expected_type in type_definitions.items():
            if column not in import_df.columns:
                continue

            for idx, value in import_df[column].items():
                if pd.isna(value):
                    continue

                if expected_type == 'number':
                    try:
                        float(value)
                    except ValueError:
                        errors.append(f"Row {idx}: '{column}' should be number, got '{value}'")

                elif expected_type == 'integer':
                    try:
                        int(value)
                    except ValueError:
                        errors.append(f"Row {idx}: '{column}' should be integer, got '{value}'")

        return errors

Quick Start

# Generate Dynamo script
generator = DynamoScriptGenerator()

mappings = {
    'OmniClass_Code': 'OmniClass Number',
    'Unit_Cost': 'Cost',
    'Material_Type': 'Material'
}

generator.generate_parameter_update_script(
    mappings=mappings,
    excel_path="enriched_data.xlsx",
    output_path="update_revit.py"
)

Validation

# Validate before import
validator = ExcelDataValidator(revit_export_df)
validation = validator.validate_import_data(import_df)

if validation['valid']:
    print(f"Ready to import. Match rate: {validation['match_rate']}%")
else:
    print(f"Issues found: {validation['warnings']}")

Complete Workflow

# 1. Export from Revit
# RvtExporter.exe model.rvt complete

# 2. Load and validate
revit_df = pd.read_excel("model.xlsx")
validator = ExcelDataValidator(revit_df)

# 3. Prepare import data
import_df = pd.read_excel("enriched_data.xlsx")
validation = validator.validate_import_data(import_df)

# 4. Generate update script
if validation['valid']:
    generator = DynamoScriptGenerator()
    generator.generate_parameter_update_script(
        mappings={'Classification': 'OmniClass Number'},
        excel_path="enriched_data.xlsx",
        output_path="apply_updates.py"
    )
    print("Run apply_updates.py in Dynamo to update Revit")

Resources

Source

git clone https://github.com/datadrivenconstruction/DDC_Skills_for_AI_Agents_in_Construction/blob/main/1_DDC_Toolkit/CAD-Converters/excel-to-rvt/SKILL.mdView on GitHub

Overview

Automates importing Excel data into Revit projects to update element parameters, generate schedules, and keep external data sources in sync. It leverages the DDC ImportExcelToRevit tool and Dynamo workflows for flexible automation across models.

How This Skill Works

Use the ImportExcelToRevit CLI to map an Excel sheet to Revit elements by a defined ID column, with an optional parameter mapping file. Dynamo scripts or the Revit API can drive the same workflow for more complex scenarios. The provided Python example demonstrates building the CLI command, supplying sheet, id column, and mapping, then parsing results to summarize updates.

When to Use It

  • Batch update thousands of element parameters from Excel during model updates
  • Link costs, specifications, and classifications from Excel to corresponding Revit parameters
  • Create or refresh schedules using external data without manual entry
  • Keep external Excel data sources in sync with the Revit model across revisions
  • Set up repeatable imports for ongoing projects using a reusable mapping file

Quick Start

  1. Step 1: Prepare your Excel file with an ElementId column and the target data
  2. Step 2: Run the CLI import, e.g. ImportExcelToRevit.exe model.rvt data.xlsx -sheet Elements -idcol ElementId [-mapping mapping.json]
  3. Step 3: Open Revit to verify results and adjust the mapping as needed

Best Practices

  • Clean and normalize Excel data before import (consistent column names and data types)
  • Define a clear parameter mapping file that maps Excel columns to Revit parameters
  • Test the import on a copy of the model and validate results in Revit
  • Version control both the Excel source and the mapping file for traceability
  • Document any exceptions or special cases so future imports are deterministic

Example Use Cases

  • Import cost estimates from Excel and populate cost-related parameters on elements
  • Apply classification codes from Excel to element parameters and schedules
  • Populate material quantities and properties into Revit schedules from a data sheet
  • Synchronize supplier data or external datasets with model properties through regular imports
  • Re-run imports after design changes to keep model data aligned with external data

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers