Skip to Content

Create Master Report

Create Master Report

Original file: create_master_report.py
Type: PYTHON


import os
import json
import csv
from pathlib import Path
from collections import Counter, defaultdict

# Path to n8n nodes
nodes_path = r"C:\Working With AI\Odoo Projects\custom-modules-v18\the_ai_automator\static\src\n8n\n8n_nodes"

print("=" * 80)
print("MASTER N8N KNOWLEDGE REPORT - ALL HIERARCHY LEVELS")
print("=" * 80)

all_rows = []
supplier_stats = {}
supplier_services = defaultdict(set)  # Track services per supplier

# Walk through all suppliers
for supplier_folder in os.listdir(nodes_path):
    supplier_path = os.path.join(nodes_path, supplier_folder)

    # Skip if not a directory
    if not os.path.isdir(supplier_path):
        continue

    supplier_name = supplier_folder

    # Initialize supplier stats
    if supplier_name not in supplier_stats:
        supplier_stats[supplier_name] = {
            'actions': 0,
            'triggers': 0,
            'total': 0
        }

    # Check if supplier has direct .node.js files (Type 2 - flat structure)
    supplier_node_js_files = [f for f in os.listdir(supplier_path) if f.endswith('.node.js') and os.path.isfile(os.path.join(supplier_path, f))]

    if supplier_node_js_files:
        # TYPE 2: Flat structure - supplier has direct nodes
        for node_file in supplier_node_js_files:
            node_name = node_file.replace('.node.js', '')
            node_path = os.path.join(supplier_path, node_file)

            # Look for corresponding .node.json
            json_file = node_file.replace('.node.js', '.node.json')
            json_path = os.path.join(supplier_path, json_file)

            categories = ''
            display_name = ''
            node_type = ''

            if os.path.exists(json_path):
                try:
                    with open(json_path, 'r', encoding='utf-8') as f:
                        data = json.load(f)
                        categories = '|'.join(data.get('categories', []))
                        display_name = data.get('displayName', '')
                        node_type = data.get('node', '')
                except:
                    pass

            # Determine if trigger or action
            node_classification = 'Trigger' if 'Trigger' in node_name else 'Action'

            # Update stats
            supplier_stats[supplier_name]['total'] += 1
            if node_classification == 'Trigger':
                supplier_stats[supplier_name]['triggers'] += 1
            else:
                supplier_stats[supplier_name]['actions'] += 1

            all_rows.append({
                'supplier': supplier_name,
                'supplier_actions': '',  # Will fill at end
                'supplier_triggers': '',  # Will fill at end
                'supplier_services': '',  # Will fill at end
                'structure_type': 'Flat (Type 2)',
                'l1_service': '',
                'l2_node_name': node_name,
                'l2_classification': node_classification,
                'l2_display_name': display_name,
                'l2_node_type': node_type,
                'l2_categories': categories,
                'l2_has_json': 'Yes' if os.path.exists(json_path) else 'No',
                'l3_resource': '',
                'l4_operation': '',
                'file_path': supplier_folder,
                'notes': 'Supplier has direct nodes'
            })

    # TYPE 1: Nested structure - scan for L1 services
    for l1_folder in os.listdir(supplier_path):
        l1_path = os.path.join(supplier_path, l1_folder)

        # Skip files and special folders
        if not os.path.isdir(l1_path) or l1_folder.startswith('__') or l1_folder in ['test', 'v1', 'v2', 'v3']:
            continue

        # Check if L1 folder contains .node.js files
        l1_node_js_files = [f for f in os.listdir(l1_path) if f.endswith('.node.js') and os.path.isfile(os.path.join(l1_path, f))]

        if not l1_node_js_files:
            continue

        l1_service_name = l1_folder

        # Track this service for the supplier
        supplier_services[supplier_name].add(l1_service_name)

        # Process each L2 node in this L1 service
        for node_file in l1_node_js_files:
            node_name = node_file.replace('.node.js', '')
            node_path = os.path.join(l1_path, node_file)

            # Look for corresponding .node.json
            json_file = node_file.replace('.node.js', '.node.json')
            json_path = os.path.join(l1_path, json_file)

            categories = ''
            display_name = ''
            node_type = ''
            resources = []

            if os.path.exists(json_path):
                try:
                    with open(json_path, 'r', encoding='utf-8') as f:
                        data = json.load(f)
                        categories = '|'.join(data.get('categories', []))
                        display_name = data.get('displayName', '')
                        node_type = data.get('node', '')
                except:
                    pass

            # Try to extract resources from .node.js file
            try:
                with open(node_path, 'r', encoding='utf-8') as f:
                    content = f.read()
                    # Look for resource definitions (simplified extraction)
                    if 'resource:' in content or 'resources:' in content:
                        # Basic resource detection
                        import re
                        resource_matches = re.findall(r"resource:\s*['\"](\w+)['\"]", content)
                        resources = list(set(resource_matches))[:5]  # Limit to 5 for readability
            except:
                pass

            # Determine if trigger or action
            node_classification = 'Trigger' if 'Trigger' in node_name else 'Action'

            # Update stats
            supplier_stats[supplier_name]['total'] += 1
            if node_classification == 'Trigger':
                supplier_stats[supplier_name]['triggers'] += 1
            else:
                supplier_stats[supplier_name]['actions'] += 1

            # Create row for L2 node
            if resources:
                for resource in resources:
                    all_rows.append({
                        'supplier': supplier_name,
                        'supplier_actions': '',  # Will fill at end
                        'supplier_triggers': '',  # Will fill at end
                        'supplier_services': '',  # Will fill at end
                        'structure_type': 'Nested (Type 1)',
                        'l1_service': l1_service_name,
                        'l2_node_name': node_name,
                        'l2_classification': node_classification,
                        'l2_display_name': display_name,
                        'l2_node_type': node_type,
                        'l2_categories': categories,
                        'l2_has_json': 'Yes' if os.path.exists(json_path) else 'No',
                        'l3_resource': resource,
                        'l4_operation': '',
                        'file_path': f"{supplier_folder}\\{l1_folder}",
                        'notes': 'Has resources detected'
                    })
            else:
                all_rows.append({
                    'supplier': supplier_name,
                    'supplier_actions': '',  # Will fill at end
                    'supplier_triggers': '',  # Will fill at end
                    'supplier_services': '',  # Will fill at end
                    'structure_type': 'Nested (Type 1)',
                    'l1_service': l1_service_name,
                    'l2_node_name': node_name,
                    'l2_classification': node_classification,
                    'l2_display_name': display_name,
                    'l2_node_type': node_type,
                    'l2_categories': categories,
                    'l2_has_json': 'Yes' if os.path.exists(json_path) else 'No',
                    'l3_resource': '',
                    'l4_operation': '',
                    'file_path': f"{supplier_folder}\\{l1_folder}",
                    'notes': ''
                })

# Fill in supplier stats and services for all rows
for row in all_rows:
    supplier = row['supplier']
    row['supplier_actions'] = supplier_stats[supplier]['actions']
    row['supplier_triggers'] = supplier_stats[supplier]['triggers']

    # Add services list
    if supplier in supplier_services and supplier_services[supplier]:
        services_list = sorted(list(supplier_services[supplier]))
        row['supplier_services'] = ', '.join(services_list)
    else:
        row['supplier_services'] = ''

print(f"\nProcessed {len(all_rows)} total rows")

# Calculate overall stats
total_actions = sum(stats['actions'] for stats in supplier_stats.values())
total_triggers = sum(stats['triggers'] for stats in supplier_stats.values())
total_nodes = sum(stats['total'] for stats in supplier_stats.values())

print(f"\nOVERALL STATISTICS:")
print(f"  Total Suppliers: {len(supplier_stats)}")
print(f"  Total Nodes: {total_nodes}")
print(f"  Total Actions: {total_actions}")
print(f"  Total Triggers: {total_triggers}")
print(f"  Suppliers with Services (Type 1): {len(supplier_services)}")

# Write to CSV
csv_path = r"C:\Users\total\n8n_master_knowledge_report.csv"
with open(csv_path, 'w', newline='', encoding='utf-8') as f:
    fieldnames = [
        'supplier',
        'supplier_actions',
        'supplier_triggers',
        'supplier_services',
        'structure_type',
        'l1_service',
        'l2_node_name',
        'l2_classification',
        'l2_display_name',
        'l2_node_type',
        'l2_categories',
        'l2_has_json',
        'l3_resource',
        'l4_operation',
        'file_path',
        'notes'
    ]

    writer = csv.DictWriter(f, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerows(all_rows)

# Print top suppliers by node count
print(f"\nTOP 10 SUPPLIERS BY NODE COUNT:")
top_suppliers = sorted(supplier_stats.items(), key=lambda x: x[1]['total'], reverse=True)[:10]
for supplier, stats in top_suppliers:
    services_str = ''
    if supplier in supplier_services and supplier_services[supplier]:
        services_str = f" [Services: {', '.join(sorted(list(supplier_services[supplier])))}]"
    print(f"  {supplier:30} Actions: {stats['actions']:3}, Triggers: {stats['triggers']:3}{services_str}")

print(f"\n[COMPLETE] Master knowledge report saved to:")
print(f"{csv_path}")
print("\nColumn structure:")
print("  - supplier (Column A)")
print("  - supplier_actions (Column B)")
print("  - supplier_triggers (Column C)")
print("  - supplier_services (Column D) - comma-separated list of L1 services")
print("\nYou can now open this in Excel and:")
print("  - Filter by supplier (Column A)")
print("  - See action/trigger counts per supplier")
print("  - See which suppliers have services (Type 1 nested structure)")
print("  - Sort/filter by structure type")
print("=" * 80)
Was this helpful?