Skip to content

Instantly share code, notes, and snippets.

@mark05e
Created April 15, 2025 12:19
Show Gist options
  • Save mark05e/25bc8b9d5cc45057f325feea5065978a to your computer and use it in GitHub Desktop.
Save mark05e/25bc8b9d5cc45057f325feea5065978a to your computer and use it in GitHub Desktop.
# Summary of the Python Code
# The Python script export_scripts_from_json.py takes a JSON file as input, extracts JavaScript code snippets from specific nodes within the JSON structure, and saves each snippet as a separate .js file.
#
# Key Functionality
# ------------------
# JSON Input: The script reads a JSON file from the path provided as a command-line argument.
# Data Extraction: It navigates the JSON structure to find nodes of type "script" and extracts the content of the "script" property within their "properties".
# File Output: Each extracted script is saved to a .js file. The files are named using a combination of a sequential counter (01, 02, 03, etc.) and the script's "uid" property. The script creates a directory (if it doesn't exist) named after the workflowName in the JSON file and saves all the .js files in that directory.
# Error Handling: The script includes error handling for file not found, invalid JSON format, missing keys in the JSON data, and issues during file writing.
# Filename Sanitization: The script sanitizes filenames, replacing invalid characters to ensure compatibility with file systems.
# Line Ending Normalization: The script normalizes line endings in the extracted scripts to use only \n, avoiding potential issues with inconsistent line breaks.
import json
import os
import re
import sys
def sanitize_filename(filename):
"""
Sanitizes a filename by removing or replacing invalid characters.
Args:
filename (str): The filename to sanitize.
Returns:
str: The sanitized filename.
"""
# Replace spaces and special characters with underscores.
sanitized = re.sub(r"[\s\/*\"<>?|:]", "_", filename)
# Remove leading/trailing underscores and dots
sanitized = sanitized.strip("_.")
# Limit the length of the filename
sanitized = sanitized[:250] # Limit to 250 characters to avoid issues
# Ensure the filename is not empty
if not sanitized:
sanitized = "untitled"
return sanitized
def export_scripts_from_json(json_file_path):
"""
Accepts a JSON file, extracts script content from 'script' type nodes,
and exports each script to a separate .js file into a subfolder.
The output filename is based on the order of script nodes and the 'uid'
property of the node. Handles errors and edge cases. The files
are exported into a subfolder named after the workflowName.
It also normalizes line endings to use only \\n.
Args:
json_file_path (str): The path to the input JSON file.
"""
try:
with open(json_file_path, 'r', encoding='utf-8') as f:
data = json.load(f)
if not isinstance(data, dict):
print(f"Error: The JSON data is not a dictionary. Expected a dictionary at the top level.")
return
if 'data' not in data or not isinstance(data['data'], dict):
print("Error: 'data' key not found or is not a dictionary in the JSON structure.")
return
if 'nodes' not in data['data'] or not isinstance(data['data']['nodes'], list):
print("Error: 'nodes' key not found or is not a list in the JSON structure.")
return
workflow_name = data.get('workflowName', 'default_workflow')
workflow_name = sanitize_filename(workflow_name) # Sanitize the folder name
try:
os.makedirs(workflow_name, exist_ok=True) # Create the folder
except OSError as e:
print(f"Error creating directory {workflow_name}: {e}")
print("Files will be saved in the current directory.")
workflow_name = "" # set to empty string so files are written to current dir
nodes = data['data']['nodes']
script_index = 1 # Keep track of the order of script nodes
for index, node in enumerate(nodes):
if not isinstance(node, dict):
print(f"Warning: Node at index {index} is not a dictionary. Skipping.")
continue
if node.get('type') == 'script':
if 'properties' in node and isinstance(node['properties'], dict):
script_content = node['properties'].get('script', '') # Default to empty string
uid = node['properties'].get('uid', f"script_{index}") # Default
else:
print(f"Warning: Node at index {index} has no 'properties' or 'properties' is not a dict. Skipping script extraction.")
continue
# Normalize line endings to use only '\n'
script_content = script_content.replace('\r\n', '\n')
uid = sanitize_filename(uid)
output_filename = os.path.join(workflow_name, f"{script_index:02d}_{uid}.js")
try:
with open(output_filename, 'w', encoding='utf-8') as outfile:
outfile.write(script_content)
print(f"Successfully exported script from element {index} (UID: {uid}, File: {output_filename})")
script_index += 1 # Increment script index for the next script node
except (IOError, OSError) as e:
print(f"Error writing to file {output_filename}: {e}")
except Exception as e:
print(f"Unexpected error writing to {output_filename}: {e}")
elif node.get('type'):
print(f"Skipping node of type {node['type']} at index {index}.")
else:
print(f"Skipping node with no type at index {index}.")
print("Script execution complete.")
except FileNotFoundError:
print(f"Error: File not found at {json_file_path}")
except json.JSONDecodeError:
print(f"Error: Could not decode JSON from {json_file_path}. Invalid JSON.")
except Exception as e:
print(f"An unexpected error occurred: {e}")
except KeyboardInterrupt:
print("Script interrupted by user.")
return
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python export_scripts.py <json_file_path>")
sys.exit(1)
input_file = sys.argv[1]
export_scripts_from_json(input_file)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment