Created
April 15, 2025 12:19
-
-
Save mark05e/25bc8b9d5cc45057f325feea5065978a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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