Claude-skill-registry excel-to-rvt
Import Excel data into RVT projects. Update element parameters, create schedules, and sync external data sources.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/excel-to-rvt" ~/.claude/skills/majiayu000-claude-skill-registry-excel-to-rvt && rm -rf "$T"
manifest:
skills/data/excel-to-rvt/SKILL.mdsource content
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
- ImportExcelToRevit CLI - Direct command-line update
- Dynamo Script - Visual programming approach
- Revit API - Full programmatic control
ImportExcelToRevit CLI
ImportExcelToRevit.exe <model.rvt> <data.xlsx> [options]
| Option | Description |
|---|---|
| Excel sheet name |
| Element ID column |
| Parameter 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
- GitHub: DDC Update Revit from Excel
- Dynamo: https://dynamobim.org/