Tag_Prop/compare_json.py
2025-06-23 17:38:52 +04:00

127 lines
4.7 KiB
Python

#!/usr/bin/env python3
import json
import sys
import argparse
import os
from typing import Set, List, Dict, Any, Optional
# --- Constants (mirroring those used in tag structure) ---
TAGS = 'tags'
NAME = 'name'
TAG_TYPE = 'tagType'
FOLDER = 'Folder'
UDT_INSTANCE = 'UdtInstance'
def load_json_file(file_path: str) -> Optional[Dict[str, Any]]:
"""Loads JSON data from a file path. Returns the loaded dict or None on error."""
if not os.path.exists(file_path):
print(f"Error: File not found: '{file_path}'", file=sys.stderr)
return None
try:
with open(file_path, 'r') as f:
data = json.load(f)
# Basic validation that it's a dictionary (expected top level)
if not isinstance(data, dict):
print(f"Error: Expected JSON root to be an object/dictionary in '{file_path}'", file=sys.stderr)
return None
return data
except json.JSONDecodeError:
print(f"Error: Invalid JSON format in file: '{file_path}'", file=sys.stderr)
return None
except IOError as e:
print(f"Error reading file '{file_path}': {e}", file=sys.stderr)
return None
except Exception as e:
print(f"An unexpected error occurred while loading '{file_path}': {e}", file=sys.stderr)
return None
def _find_udt_instances_recursive(items: List[Dict[str, Any]], found_names: Set[str]):
"""Helper function to recursively find UDT instance names within a list of tags/folders."""
# Copy logic from check_tags.py
if not isinstance(items, list):
return # Stop recursion for this branch
for item in items:
if not isinstance(item, dict):
continue # Skip malformed items
item_tag_type = item.get(TAG_TYPE)
item_name = item.get(NAME)
if item_tag_type == UDT_INSTANCE and item_name:
found_names.add(item_name)
elif item_tag_type == FOLDER:
# Recursively check the tags within this folder
folder_tags = item.get(TAGS, [])
_find_udt_instances_recursive(folder_tags, found_names)
def extract_udt_names(file_path: str) -> Optional[Set[str]]:
"""
Loads a tags JSON file and extracts the names of all UDT instances.
Returns a set of names, or None if the file cannot be processed.
"""
print(f"Extracting UDT names from: {file_path}")
data = load_json_file(file_path)
if data is None:
return None
# Expecting a top-level object like {"tags": [...list of MCM folders...]}
if TAGS not in data or not isinstance(data.get(TAGS), list):
print(f"Error: '{file_path}' does not contain a top-level '{TAGS}' key with a list value.", file=sys.stderr)
return None
udt_names = set()
top_level_tags = data[TAGS]
# Start the recursive search from the list of MCM folders/tags
_find_udt_instances_recursive(top_level_tags, udt_names)
print(f" Found {len(udt_names)} UDT instance names.")
return udt_names
def main():
parser = argparse.ArgumentParser(
description="Compares the set of UDT Instance names found in two tags.json files.",
formatter_class=argparse.ArgumentDefaultsHelpFormatter
)
parser.add_argument("file1", help="Path to the first JSON tags file (e.g., baseline).")
parser.add_argument("file2", help="Path to the second JSON tags file (e.g., new result).")
args = parser.parse_args()
print(f"Comparing UDT Instance names in:\n File 1: {args.file1}\n File 2: {args.file2}\n")
# Extract UDT names from both files
names1 = extract_udt_names(args.file1)
if names1 is None:
sys.exit(1)
names2 = extract_udt_names(args.file2)
if names2 is None:
sys.exit(1)
# Compare the sets of names
print("\n--- Comparison Results ---")
if names1 == names2:
print(f"Success: Both files contain the exact same {len(names1)} UDT instance names.")
sys.exit(0)
else:
print("Difference Found: The set of UDT instance names differs between the files.")
missing_in_file2 = sorted(list(names1 - names2))
added_in_file2 = sorted(list(names2 - names1))
if missing_in_file2:
print(f"\nNames in '{os.path.basename(args.file1)}' but NOT in '{os.path.basename(args.file2)}' ({len(missing_in_file2)}):")
for name in missing_in_file2:
print(f" - {name}")
if added_in_file2:
print(f"\nNames in '{os.path.basename(args.file2)}' but NOT in '{os.path.basename(args.file1)}' ({len(added_in_file2)}):")
for name in added_in_file2:
print(f" - {name}")
sys.exit(1) # Exit with a non-zero code to indicate difference
if __name__ == "__main__":
main()