import sys import os import json import subprocess import re from datetime import timedelta # --- Configuration --- HIGHLIGHT_SCRIPT_NAME = "highlight.py" GOOD_DATASET_FILE = "dataset.jsonl" BAD_FILES_LOG = "errors.txt" PROGRESS_FILE = ".curation_progress.log" # --- Unbuffered Input (Cross-Platform) --- try: # Unix-like systems (Linux, macOS) import tty, termios def getch(): fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) try: tty.setraw(sys.stdin.fileno()) ch = sys.stdin.read(1) finally: termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) return ch except ImportError: # Windows import msvcrt def getch(): return msvcrt.getch().decode() # --- Main Curation Logic --- def review_files(file_paths: list[str]): """ Iterates through files, displays them for review, and sorts them based on user input. """ if not os.path.exists(HIGHLIGHT_SCRIPT_NAME): print(f"Error: The visualization script '{HIGHLIGHT_SCRIPT_NAME}' was not found in this directory.") sys.exit(1) # Load the set of already processed files processed_files = set() if os.path.exists(PROGRESS_FILE): with open(PROGRESS_FILE, 'r', encoding='utf-8') as f: processed_files = {line.strip() for line in f} total_files = len(file_paths) for i, filepath in enumerate(file_paths): if filepath in processed_files: print(f"Skipping already processed file: {filepath}") continue print("-" * 80) print(f"Reviewing file ({i + 1}/{total_files}): {filepath}") # 1. Run highlight.py and pipe its output to the user's pager try: pager_cmd = os.environ.get("PAGER", "less").split() highlight_process = subprocess.Popen( ["python", HIGHLIGHT_SCRIPT_NAME, filepath], stdout=subprocess.PIPE, text=True, encoding='utf-8' ) pager_process = subprocess.Popen(pager_cmd, stdin=highlight_process.stdout) highlight_process.stdout.close() return_code = pager_process.wait() if return_code != 0: print(f"Warning: Pager exited with code {return_code}.") except FileNotFoundError: print(f"Error: Pager command not found. Is '{os.environ.get('PAGER', 'less')}' installed?") continue except Exception as e: print(f"An error occurred while running the subprocess: {e}") continue # 2. Prompt for user decision while True: print("\nWas this file's labeling correct? (y/n): ", end="", flush=True) choice = getch().lower() print(choice) if choice == 'y': print(f" -> Appending {filepath} to {GOOD_DATASET_FILE}") try: with open(filepath, 'r', encoding='utf-8') as infile, \ open(GOOD_DATASET_FILE, 'a', encoding='utf-8') as outfile: for line in infile: outfile.write(line) except Exception as e: print(f"Error appending to dataset file: {e}") break elif choice == 'n': print(f" -> Logging {filepath} to {BAD_FILES_LOG}") try: with open(BAD_FILES_LOG, 'a', encoding='utf-8') as errfile: errfile.write(filepath + '\n') except Exception as e: print(f"Error writing to log file: {e}") break elif choice in ['\x03', 'q']: # Handle Ctrl+C or 'q' to quit print("\nExiting review process.") sys.exit(0) else: print("Invalid input. Please press 'y' for yes or 'n' for no.") # Log the file as processed after a decision has been made with open(PROGRESS_FILE, 'a', encoding='utf-8') as f: f.write(filepath + '\n') if __name__ == "__main__": if len(sys.argv) < 2: print("Usage: python curate_dataset.py ...") print("Or: python curate_dataset.py /path/to/*/*.jsonl") sys.exit(1) review_files(sys.argv[1:]) print("-" * 80) print("Curation complete.")