
My workflow 11
描述
分类
🔧 Engineering
使用的节点
n8n-nodes-base.ifn8n-nodes-base.ifn8n-nodes-base.setn8n-nodes-base.setn8n-nodes-base.setn8n-nodes-base.setn8n-nodes-base.setn8n-nodes-base.setn8n-nodes-base.coden8n-nodes-base.merge
价格免费
浏览量0
最后更新11/28/2025
workflow.json
{
"id": "t92A5nG34BQ1ETsq",
"meta": {
"instanceId": "faa70e11b7175129a74fd834d3451fdc1862589b16d68ded03f91ca7b1ecca12"
},
"name": "My workflow 11",
"tags": [],
"nodes": [
{
"id": "92efb3dc-aaf3-42c9-ac5e-c1cd237335dc",
"name": "Start - Click to begin",
"type": "n8n-nodes-base.manualTrigger",
"position": [
-2300,
80
],
"parameters": {},
"typeVersion": 1
},
{
"id": "95816564-1418-4634-8d43-b501929cb49a",
"name": "Setup - Define file paths",
"type": "n8n-nodes-base.set",
"position": [
-2060,
80
],
"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 pipelines\\Validation\\2023 racbasicsampleproject.rvt"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "71fb07b2-0664-4799-8a4e-64abbf952261",
"name": "Sticky Note - Start",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2340,
-180
],
"parameters": {
"color": 4,
"width": 190,
"height": 540,
"content": "## 🚀 START WORKFLOW\n\nClick \"Execute Workflow\" button to begin the validation process"
},
"typeVersion": 1
},
{
"id": "f86c21fe-e5db-4c06-b2bb-681a20041443",
"name": "Sticky Note - Settings",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2120,
-300
],
"parameters": {
"color": 4,
"width": 220,
"height": 660,
"content": "## ⚙️ SETTINGS\n\n**Update file paths here:**\n- `path_to_converter` - path to converter\n- `project_file` - path to your project file (IFC/DWG/RVT)"
},
"typeVersion": 1
},
{
"id": "21fa1ff2-e1c1-4aa6-9891-936edbeee737",
"name": "Read Project Excel File",
"type": "n8n-nodes-base.readBinaryFile",
"position": [
-480,
220
],
"parameters": {
"filePath": "={{ $json[\"xlsx_filename\"] }}"
},
"typeVersion": 1
},
{
"id": "1ac7b052-ff92-4b7e-a8fd-8ef736a08199",
"name": "Set Validation Rules Path",
"type": "n8n-nodes-base.set",
"position": [
-480,
20
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "validation-rules-path-id",
"name": "validation_rules_path",
"type": "string",
"value": "C:\\Users\\Artem Boiko\\Desktop\\n8n pipelines\\Validation\\n8n_3_DDC_BIM_Requirements_Table_for_Revit_IFC_DWG.xlsx"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "59dcf928-8e10-4747-beb0-de38b0b10426",
"name": "Sticky Note - Validation Rules",
"type": "n8n-nodes-base.stickyNote",
"position": [
-540,
-300
],
"parameters": {
"color": 4,
"width": 220,
"height": 680,
"content": "## 📋 VALIDATION RULES\n\n**Specify the path to rules file:**\n`validation_rules_path`\n\nThis file contains validation criteria"
},
"typeVersion": 1
},
{
"id": "c844345a-6634-4305-85e9-ee898eb11fad",
"name": "Read Validation Rules File",
"type": "n8n-nodes-base.readBinaryFile",
"position": [
-240,
100
],
"parameters": {
"filePath": "={{ $json[\"validation_rules_path\"] }}"
},
"typeVersion": 1
},
{
"id": "33182b90-0463-4d4f-ae56-06b168413f74",
"name": "Merge Excel Files",
"type": "n8n-nodes-base.merge",
"position": [
-20,
240
],
"parameters": {},
"typeVersion": 3
},
{
"id": "50aac1fe-7aa0-450f-910a-922e41b3cfe1",
"name": "Validate - Enhanced",
"type": "n8n-nodes-base.code",
"position": [
160,
100
],
"parameters": {
"language": "python",
"pythonCode": "import micropip\nawait micropip.install(\"openpyxl\")\nfrom io import BytesIO\nimport pandas as pd\nimport datetime\nimport base64\nimport os\nfrom openpyxl import load_workbook\nfrom openpyxl.styles import Alignment, PatternFill\n\n# Get the uploaded Excel files\ninput_data = _input.all()\n\n# Get project Excel data (from Read Project Excel File node)\nproject_binary = input_data[0][\"binary\"]\nproject_b64_excel = project_binary[\"data\"][\"data\"]\nproject_excel_bytes = BytesIO(base64.b64decode(project_b64_excel))\n\n# Get validation rules Excel data (from Read Validation Rules File node)\nvalidation_binary = input_data[1][\"binary\"]\nvalidation_b64_excel = validation_binary[\"data\"][\"data\"]\nvalidation_excel_bytes = BytesIO(base64.b64decode(validation_b64_excel))\n\n# Get the validation rules path from the previous node to determine output directory\nvalidation_rules_path = input_data[1][\"json\"][\"validation_rules_path\"]\noutput_directory = os.path.dirname(validation_rules_path)\nprint(f\"Output directory: {output_directory}\")\n\n# Load project data\ndf = pd.read_excel(project_excel_bytes)\nprint(f\"Loaded {len(df)} rows from project file\")\n\n# Handle duplicate columns\nduplicate_cols = df.columns[df.columns.duplicated(keep=False)].unique().tolist()\nif duplicate_cols:\n print(f\"WARNING: Duplicate columns found: {duplicate_cols}\")\n\n# Clean column names\ncolumn_mapping = {col: col.split(' : ')[0] if ':' in col else col for col in df.columns}\ncleaned_columns = list(column_mapping.values())\n\n# Load and prepare workbook\nwb = load_workbook(validation_excel_bytes)\nws = wb.active\n\n# Unmerge cells in columns D-G\nfor mr in list(ws.merged_cells.ranges):\n if any(col in range(mr.min_col, mr.max_col + 1) for col in [4, 5, 6, 7]):\n print(f\"Unmerging: {mr}\")\n ws.unmerge_cells(str(mr))\n\n# Get category column\ncategory_col = ws['B2'].value\nprint(f\"Category column: {category_col}\")\n\n# Find category column in data\nif category_col not in df.columns:\n for orig, cleaned in column_mapping.items():\n if cleaned == category_col:\n category_col = orig\n break\n if category_col not in df.columns:\n raise ValueError(f\"Category column '{category_col}' not found\")\n\nprint(f\"Data type: {df[category_col].dtype}, Unique values: {df[category_col].nunique()}\")\n\n# Read validation rules from the Excel\nvalidation_excel_bytes.seek(0) # Reset the stream\ndf_excel = pd.read_excel(validation_excel_bytes)\nvalid_mask = df_excel.iloc[1:, 1].notna() & df_excel.iloc[1:, 2].notna()\nvalidation_data = pd.DataFrame({\n 'row': df_excel.index[1:][valid_mask] + 2,\n 'group': df_excel.iloc[1:, 1][valid_mask].values,\n 'param': df_excel.iloc[1:, 2][valid_mask].values\n})\n\n# Define fills\nred_fill = PatternFill(start_color='FFCCCC', end_color='FFCCCC', fill_type='solid')\ngreen_fill = PatternFill(start_color='CCFFCC', end_color='CCFFCC', fill_type='solid')\n\nprint(f\"Processing {len(validation_data)} rules...\")\n\n# Helper function\ndef get_best_column(df, param, group_df):\n matches = [i for i, col in enumerate(df.columns) if cleaned_columns[i] == param]\n \n if not matches:\n return param if param in df.columns else None\n \n if len(matches) == 1:\n return df.columns[matches[0]]\n \n print(f\" Found {len(matches)} columns named '{param}'\")\n best_idx, best_count = matches[0], -1\n \n for idx in matches:\n col = df.columns[idx]\n if col in group_df.columns:\n count = group_df[col].notna().sum()\n print(f\" Column {idx}: {count} values\")\n if count > best_count:\n best_idx, best_count = idx, count\n \n return df.columns[best_idx]\n\n# Process validation rules\nfor _, row in validation_data.iterrows():\n r, grp, prm = row['row'], str(row['group']).strip(), str(row['param']).strip()\n \n try:\n # Filter by category\n cat_df = df[df[category_col] == grp]\n \n # Try alternative matching if no exact match\n if len(cat_df) == 0 and ':' in grp:\n cat_df = df[df[category_col].astype(str).str.contains(grp, na=False, regex=False)]\n \n total = len(cat_df)\n \n # Get column and calculate metrics\n col = get_best_column(df, prm, cat_df)\n \n if col and col in cat_df.columns:\n valid_df = cat_df[cat_df[col].notna() & ~cat_df[col].isin([0, 0.0, \"0\", \"0.0\", \"0,0\"])]\n filled = len(valid_df)\n fill_pct = filled / total if total > 0 else 0\n unique_cnt = valid_df[col].nunique()\n unique_vals = valid_df[col].unique()[:10]\n unique_str = \", \".join(map(str, unique_vals))\n if len(unique_vals) > 10:\n unique_str += f\"... ({valid_df[col].nunique()} total)\"\n else:\n filled = fill_pct = unique_cnt = 0\n unique_str = \"\"\n \n # Write to Excel\n ws[f'D{r}'] = total\n ws[f'D{r}'].alignment = Alignment(horizontal='center')\n \n ws[f'E{r}'].value = fill_pct\n ws[f'E{r}'].number_format = '0.00%'\n ws[f'E{r}'].alignment = Alignment(horizontal='center')\n ws[f'E{r}'].fill = green_fill if fill_pct > 0 else red_fill\n \n ws[f'F{r}'] = unique_cnt\n ws[f'F{r}'].alignment = Alignment(horizontal='center')\n \n ws[f'G{r}'] = unique_str\n ws[f'G{r}'].alignment = Alignment(horizontal='left')\n \n except Exception as e:\n print(f\" ERROR: {type(e).__name__}: {str(e)}\")\n ws[f'D{r}'] = 0\n ws[f'E{r}'].value = 0\n ws[f'E{r}'].fill = red_fill\n ws[f'F{r}'] = 0\n ws[f'G{r}'] = f\"ERROR: {str(e)}\"\n\n# Save to bytes & return to n8n\nwith BytesIO() as out_stream:\n wb.save(out_stream)\n out_stream.seek(0)\n result_b64 = base64.b64encode(out_stream.read()).decode()\n\noutput_timestamped = f\"DDC_Revit_IFC_Validation__{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx\"\n\n# Use the same directory as the validation rules file\nfull_output_path = os.path.join(output_directory, output_timestamped)\n\nreturn [{\n \"json\": {\n \"filename\": output_timestamped,\n \"full_path\": full_output_path\n },\n \"binary\": {\n \"data\": {\n \"data\": result_b64,\n \"fileName\": output_timestamped,\n \"fileExtension\": \"xlsx\",\n \"mimeType\": \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\"\n }\n }\n}]\n"
},
"typeVersion": 2
},
{
"id": "4925aa8f-4590-4280-83fe-cd8188baa47d",
"name": "Sticky Note - Validation",
"type": "n8n-nodes-base.stickyNote",
"position": [
-300,
-180
],
"parameters": {
"color": 2,
"width": 620,
"height": 660,
"content": "## 🤖 AUTOMATED VALIDATION\n\nProcessing includes:\n- Project data analysis\n- Rules-based checking\n- Report creation with color coding\n\n🟩 Green = data present\n🟥 Red = data missing"
},
"typeVersion": 1
},
{
"id": "6df2ce0a-6e99-4cc9-860c-73b5c6b6f73b",
"name": "Sticky Note - Conversion",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1880,
-180
],
"parameters": {
"width": 1320,
"height": 660,
"content": "## 🔄 CONVERSION\n\nAutomatic conversion Project files → Excel\n\nIf file already exists, conversion is skipped"
},
"typeVersion": 1
},
{
"id": "c0cd20fa-8e9b-4b13-b989-ab9f35cbf6f0",
"name": "Sticky Note - Save & Open",
"type": "n8n-nodes-base.stickyNote",
"position": [
340,
-180
],
"parameters": {
"color": 6,
"width": 420,
"height": 660,
"content": "## 📂 SAVE & OPEN\n\n✅ Report saved automatically\n✅ Excel opens for viewing\n\nFilename includes date and time"
},
"typeVersion": 1
},
{
"id": "78b9973e-a1b8-45c6-87c6-987e8a520533",
"name": "Save Validation Report1",
"type": "n8n-nodes-base.writeBinaryFile",
"position": [
420,
100
],
"parameters": {
"options": {},
"fileName": "={{ $json.full_path }}"
},
"typeVersion": 1
},
{
"id": "52e44f10-633e-4f2b-9b02-7f279bc56efa",
"name": "Open Excel Report1",
"type": "n8n-nodes-base.executeCommand",
"position": [
600,
100
],
"parameters": {
"command": "=start \"\" \"{{ $json.full_path }}\""
},
"typeVersion": 1
},
{
"id": "f10e9818-6d49-43f0-8174-227cb50714e4",
"name": "Create - Excel filename",
"type": "n8n-nodes-base.set",
"position": [
-1820,
80
],
"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": "8b0c05f8-8eee-4096-9160-9f5db87bfff6",
"name": "Check - Does Excel file exist?",
"type": "n8n-nodes-base.readBinaryFile",
"position": [
-1640,
80
],
"parameters": {
"filePath": "={{ $json[\"xlsx_filename\"] }}"
},
"typeVersion": 1,
"continueOnFail": true,
"alwaysOutputData": true
},
{
"id": "b8593fdb-f7b7-4c75-9e6d-59c03ff45d3c",
"name": "If - File exists?",
"type": "n8n-nodes-base.if",
"position": [
-1480,
80
],
"parameters": {
"options": {},
"conditions": {
"options": {
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "e7fb1577-e753-43f5-9f5a-4d5285aeb96e",
"operator": {
"type": "boolean",
"operation": "equals"
},
"leftValue": "={{ $binary.data ? true : false }}",
"rightValue": "={{ true }}"
}
]
}
},
"typeVersion": 2
},
{
"id": "6c3d3da3-639a-4c5d-9c08-43f2054ef577",
"name": "Extract - Run converter",
"type": "n8n-nodes-base.executeCommand",
"position": [
-1280,
200
],
"parameters": {
"command": "=\"{{$node[\"Setup - Define file paths\"].json[\"path_to_converter\"]}}\" \"{{$node[\"Setup - Define file paths\"].json[\"project_file\"]}}\""
},
"typeVersion": 1,
"continueOnFail": true
},
{
"id": "97156ad5-e987-401b-8bbb-360a0b5da7b6",
"name": "Info - Skip conversion",
"type": "n8n-nodes-base.set",
"position": [
-1200,
-20
],
"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 filename\"].json[\"xlsx_filename\"] }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "d618645d-acf6-4dca-8ea6-ba17b573ffb2",
"name": "Check - Did extraction succeed?",
"type": "n8n-nodes-base.if",
"position": [
-1120,
200
],
"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 converter\"].json.error }}",
"rightValue": ""
}
]
}
},
"typeVersion": 2
},
{
"id": "8059bfbc-0fb0-4894-8595-8aa8ae1f3d22",
"name": "Error - Show what went wrong",
"type": "n8n-nodes-base.set",
"position": [
-940,
100
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "error-message-id",
"name": "error_message",
"type": "string",
"value": "=Extraction failed: {{ $node[\"Extract - Run converter\"].json.error || \"Unknown error\" }}"
},
{
"id": "error-code-id",
"name": "error_code",
"type": "number",
"value": "={{ $node[\"Extract - Run converter\"].json.code || -1 }}"
},
{
"id": "xlsx-filename-error",
"name": "xlsx_filename",
"type": "string",
"value": "={{ $node[\"Create - Excel filename\"].json[\"xlsx_filename\"] }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "12da9c51-2e54-405c-9abb-339acfe37c10",
"name": "Set xlsx_filename after success",
"type": "n8n-nodes-base.set",
"position": [
-940,
280
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "xlsx-filename-success",
"name": "xlsx_filename",
"type": "string",
"value": "={{ $node[\"Create - Excel filename\"].json[\"xlsx_filename\"] }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "0607ae8e-453d-420a-ace9-f7c949449806",
"name": "Merge - Continue workflow",
"type": "n8n-nodes-base.merge",
"position": [
-700,
20
],
"parameters": {},
"typeVersion": 3
},
{
"id": "e1bb7a42-ef83-48f5-a687-9efe58c3ef4e",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1880,
-500
],
"parameters": {
"width": 540,
"height": 300,
"content": "## ⚠️ Troubleshooting\nIf you encounter errors during conversion, be sure to reference the executable inside the **`datadrivenlibs`** folder. Use this path:\n\n```text\n\"DDC_Exporter_XXXXXXX\\datadrivenlibs\\RvtExporter.exe\"\n```\ninstead of:\n```text\n\"DDC_Exporter_XXXXXXX\\RvtExporter.exe\"\n```"
},
"typeVersion": 1
},
{
"id": "ebb5cc53-2c09-48f8-bdf4-6fafe0ccf866",
"name": "Sticky Note1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-300,
-660
],
"parameters": {
"width": 540,
"height": 460,
"content": "## ⚠️ Troubleshooting\nIn n8n versions 1.98.0–1.101.x, the Python Code node (Pyodide) completely blocks the `os` module, causing this error:\n\n### ✅ Solution \nTo avoid this error, **use n8n version 1.97** where the `os` module is not blocked. \nYou can launch it with the following command:\n\n```bash\nnpx [email protected]\n```\nor\n```bash\nnpm install [email protected]\n```\nThen run n8n as usual:\n```\nnpx n8n\n```\n\n"
},
"typeVersion": 1
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "cac8701f-a570-4992-b139-33f47ce6894c",
"connections": {
"If - File exists?": {
"main": [
[
{
"node": "Info - Skip conversion",
"type": "main",
"index": 0
}
],
[
{
"node": "Extract - Run converter",
"type": "main",
"index": 0
}
]
]
},
"Merge Excel Files": {
"main": [
[
{
"node": "Validate - Enhanced",
"type": "main",
"index": 0
}
]
]
},
"Validate - Enhanced": {
"main": [
[
{
"node": "Save Validation Report1",
"type": "main",
"index": 0
}
]
]
},
"Info - Skip conversion": {
"main": [
[
{
"node": "Merge - Continue workflow",
"type": "main",
"index": 0
}
]
]
},
"Start - Click to begin": {
"main": [
[
{
"node": "Setup - Define file paths",
"type": "main",
"index": 0
}
]
]
},
"Create - Excel filename": {
"main": [
[
{
"node": "Check - Does Excel file exist?",
"type": "main",
"index": 0
}
]
]
},
"Extract - Run converter": {
"main": [
[
{
"node": "Check - Did extraction succeed?",
"type": "main",
"index": 0
}
]
]
},
"Read Project Excel File": {
"main": [
[
{
"node": "Merge Excel Files",
"type": "main",
"index": 0
}
]
]
},
"Save Validation Report1": {
"main": [
[
{
"node": "Open Excel Report1",
"type": "main",
"index": 0
}
]
]
},
"Merge - Continue workflow": {
"main": [
[
{
"node": "Read Project Excel File",
"type": "main",
"index": 0
},
{
"node": "Set Validation Rules Path",
"type": "main",
"index": 0
}
]
]
},
"Set Validation Rules Path": {
"main": [
[
{
"node": "Read Validation Rules File",
"type": "main",
"index": 0
}
]
]
},
"Setup - Define file paths": {
"main": [
[
{
"node": "Create - Excel filename",
"type": "main",
"index": 0
}
]
]
},
"Read Validation Rules File": {
"main": [
[
{
"node": "Merge Excel Files",
"type": "main",
"index": 1
}
]
]
},
"Error - Show what went wrong": {
"main": [
[
{
"node": "Merge - Continue workflow",
"type": "main",
"index": 1
}
]
]
},
"Check - Does Excel file exist?": {
"main": [
[
{
"node": "If - File exists?",
"type": "main",
"index": 0
}
]
]
},
"Check - Did extraction succeed?": {
"main": [
[
{
"node": "Error - Show what went wrong",
"type": "main",
"index": 0
}
],
[
{
"node": "Set xlsx_filename after success",
"type": "main",
"index": 0
}
]
]
},
"Set xlsx_filename after success": {
"main": [
[
{
"node": "Merge - Continue workflow",
"type": "main",
"index": 1
}
]
]
}
}
}