new version

This commit is contained in:
Robert Nasarek 2025-10-29 21:21:54 +00:00
parent 522298a6ec
commit b41cd757e4
412 changed files with 20890 additions and 6 deletions

129
config_selector.py Executable file → Normal file
View file

@ -5,20 +5,31 @@ import json
import shutil
import curses
import re
import csv
import filecmp
import yaml
from typing import List, Dict, Set, Tuple, Optional
# Use current directory instead of hardcoded path
BASE_DIR = os.getcwd()
class ConfigSelector:
def __init__(self, json_path: str, original_config_path: str, config_path: str):
def __init__(self, json_path: str, original_config_path: str, config_path: str,
old_recipe_path: str = None, changed_files_path: str = None, deleted_files_csv: str = None,
ignored_files_yml: str = None):
self.json_path = json_path
self.original_config_path = original_config_path
self.config_path = config_path
self.old_recipe_path = old_recipe_path or os.path.join(BASE_DIR, "old_recipe_config")
self.changed_files_path = changed_files_path or os.path.join(BASE_DIR, "changed_files")
self.deleted_files_csv = deleted_files_csv or os.path.join(BASE_DIR, "deleted_files.csv")
self.ignored_files_yml = ignored_files_yml or os.path.join(BASE_DIR, "ignored_files.yml")
self.prefixes: List[str] = []
self.matched_files: Dict[str, List[str]] = {}
self.unmatched_files: Dict[str, List[str]] = {}
self.ignored_files: Set[str] = set()
self.load_prefixes()
self.load_ignored_files()
self.scan_configs()
def load_prefixes(self) -> None:
@ -37,6 +48,23 @@ class ConfigSelector:
print(f"Error loading JSON file: {e}")
exit(1)
def load_ignored_files(self) -> None:
"""Load ignored file names from YAML file."""
self.ignored_files = set()
if not os.path.exists(self.ignored_files_yml):
return
try:
with open(self.ignored_files_yml, 'r') as f:
data = yaml.safe_load(f)
if data:
# Iterate through all categories in the YAML.
for category, info in data.items():
if isinstance(info, dict) and 'name' in info:
self.ignored_files.add(info['name'])
except (yaml.YAMLError, FileNotFoundError) as e:
print(f"Warning: Error loading ignored files YAML: {e}")
def save_prefixes(self) -> None:
"""Save current prefixes back to JSON file."""
try:
@ -118,6 +146,75 @@ class ConfigSelector:
print(f"Copied {copied_count} files to {self.config_path} ({processed_count} files were processed to remove UUIDs and _core structures)")
def compare_and_track_changes(self) -> Tuple[int, int, int]:
"""
Compare files between old_recipe_config and new_recipe_config.
Copy changed files to changed_files folder.
Track deleted files in deleted_files.csv.
Returns:
Tuple of (changed_count, deleted_count, unchanged_count)
"""
# Ensure directories exist
if not os.path.exists(self.changed_files_path):
os.makedirs(self.changed_files_path)
# Track statistics
changed_count = 0
deleted_count = 0
unchanged_count = 0
ignored_count = 0
deleted_files_list = []
# Get all files in old_recipe_config
old_files = set()
if os.path.exists(self.old_recipe_path):
old_files = {f for f in os.listdir(self.old_recipe_path) if f.endswith('.yml')}
# Get all files in new_recipe_config
new_files = set()
if os.path.exists(self.config_path):
new_files = {f for f in os.listdir(self.config_path) if f.endswith('.yml')}
# Find deleted files (in old but not in new)
deleted_files = old_files - new_files
for filename in sorted(deleted_files):
deleted_files_list.append(filename)
deleted_count += 1
# Find common files and check for changes
common_files = old_files & new_files
for filename in sorted(common_files):
old_file_path = os.path.join(self.old_recipe_path, filename)
new_file_path = os.path.join(self.config_path, filename)
# Compare file contents
if filecmp.cmp(old_file_path, new_file_path, shallow=False):
# Files are identical
unchanged_count += 1
else:
# Files have changed - check if ignored
if filename in self.ignored_files:
# Skip copying ignored files
ignored_count += 1
else:
# Copy to changed_files
changed_dest = os.path.join(self.changed_files_path, filename)
shutil.copy2(new_file_path, changed_dest)
changed_count += 1
# Write deleted files to CSV
with open(self.deleted_files_csv, 'w', newline='') as csvfile:
writer = csv.writer(csvfile)
writer.writerow(['filename']) # Header
for filename in deleted_files_list:
writer.writerow([filename])
if ignored_count > 0:
print(f"Ignored {ignored_count} changed file(s) as per ignored_files.yml")
return changed_count, deleted_count, unchanged_count
def add_prefix(self, prefix: str) -> None:
"""Add a new prefix to the list if it doesn't exist."""
if prefix and prefix not in self.prefixes:
@ -301,7 +398,7 @@ def draw_menu(stdscr, selector: ConfigSelector, selected_idx: int, view_mode: st
# Instructions
stdscr.addstr(h-4, 2, "LEFT/RIGHT: Navigate tree levels | SPACE: Toggle view | ENTER: Select files")
stdscr.addstr(h-3, 2, "A: Add prefix | O: Choose prefix | D: Add current path as prefix | C: Copy | S: Save | Q: Quit")
stdscr.addstr(h-3, 2, "A: Add prefix | O: Choose | D: Add/Del | C: Copy | M: Compare | S: Save | Q: Quit")
# Content area
start_y = 3
@ -805,11 +902,33 @@ def confirm_yaml_cleaning(stdscr, selector):
return confirm_dialog(stdscr, "Copy all matched files to config directory and remove UUID and _core?")
def show_comparison_results(stdscr, changed_count, deleted_count, unchanged_count):
"""Show the results of file comparison."""
h, w = stdscr.getmaxyx()
dialog_h, dialog_w = 10, 60
dialog_y = (h - dialog_h) // 2
dialog_x = (w - dialog_w) // 2
# Draw dialog box
dialog_win = curses.newwin(dialog_h, dialog_w, dialog_y, dialog_x)
dialog_win.box()
dialog_win.addstr(1, 2, "File Comparison Results", curses.A_BOLD)
dialog_win.addstr(3, 4, f"Changed files: {changed_count}")
dialog_win.addstr(4, 4, f"Deleted files: {deleted_count}")
dialog_win.addstr(5, 4, f"Unchanged files: {unchanged_count}")
dialog_win.addstr(7, 4, "Changed files copied to: changed_files/")
dialog_win.addstr(8, 4, "Deleted files tracked in: deleted_files.csv")
dialog_win.addstr(dialog_h - 2, 2, "Press any key to continue...")
dialog_win.refresh()
# Wait for key
stdscr.getch()
def main(stdscr):
# Set paths relative to current directory
json_path = os.path.join(BASE_DIR, "config_prefixes.json")
original_config_path = os.path.join(BASE_DIR, "original_config")
config_path = os.path.join(BASE_DIR, "config")
config_path = os.path.join(BASE_DIR, "new_recipe_config")
# Create directories if they don't exist
if not os.path.exists(original_config_path):
@ -975,6 +1094,10 @@ def main(stdscr):
elif key == ord('c') or key == ord('C'):
if confirm_yaml_cleaning(stdscr, selector):
selector.copy_matched_files()
elif key == ord('m') or key == ord('M'):
if confirm_dialog(stdscr, "Compare old and new recipe configs and track changes?"):
changed_count, deleted_count, unchanged_count = selector.compare_and_track_changes()
show_comparison_results(stdscr, changed_count, deleted_count, unchanged_count)
elif key == ord('s') or key == ord('S'):
selector.save_prefixes()
elif key in (10, 13): # ENTER key to select individual files