"""Utility functions for loading data profile artifacts.""" import json import pickle from pathlib import Path from typing import Any, Dict, Optional, Set, Tuple def find_latest_run(profile_dir: Path, version: str, split: str = "train") -> Optional[Path]: """Find the latest run directory for a given version. Args: profile_dir: Base directory containing all profiles version: Version string (e.g., "v0", "v1", "v9") split: Data split ("train" or "val") Returns: Path to the latest run directory, or None if not found """ # Look for directories matching the version pattern pattern = f"{version}_full_*" matching_dirs = sorted(profile_dir.glob(pattern)) if not matching_dirs: print(f"Warning: No directories found for {version} in {profile_dir}") return None # Use the latest run (last in sorted order) latest_dir = matching_dirs[-1] split_dir = latest_dir / split if not split_dir.exists(): print(f"Warning: Split directory {split_dir} does not exist") return None return split_dir def load_analysis_results(run_dir: Path) -> Optional[Dict[str, Any]]: """Load analysis_results.json from a run directory. Args: run_dir: Path to the run directory (e.g., v0_full_20250930_150622/train/) Returns: Dictionary containing analysis results, or None if file not found """ analysis_file = run_dir / "analysis_results.json" if not analysis_file.exists(): print(f"Warning: Analysis file not found: {analysis_file}") return None try: with open(analysis_file, "r") as f: return json.load(f) except Exception as e: print(f"Error loading {analysis_file}: {e}") return None def load_id_set(run_dir: Path) -> Optional[Set[str]]: """Load ID set from pickle file. Args: run_dir: Path to the run directory (e.g., v0_full_20250930_150622/train/) Returns: Set of ID strings, or None if file not found """ id_set_file = run_dir / "id_set_train.pkl" # Try alternate filename pattern if not id_set_file.exists(): id_set_file = run_dir / "id_set_val.pkl" if not id_set_file.exists(): print(f"Warning: ID set file not found in {run_dir}") return None try: with open(id_set_file, "rb") as f: id_set = pickle.load(f) if not isinstance(id_set, set): print(f"Warning: ID set is not a Python set, got {type(id_set)}") return None return id_set except Exception as e: print(f"Error loading ID set from {id_set_file}: {e}") return None def load_version_data( profile_dir: Path, version: str, split: str = "train" ) -> Optional[Tuple[Dict[str, Any], Set[str], Path]]: """Load both analysis results and ID set for a version. Args: profile_dir: Base directory containing all profiles version: Version string (e.g., "v0", "v1", "v9") split: Data split ("train" or "val") Returns: Tuple of (analysis_results, id_set, run_dir) or None if loading failed """ run_dir = find_latest_run(profile_dir, version, split) if run_dir is None: return None analysis_results = load_analysis_results(run_dir) id_set = load_id_set(run_dir) if analysis_results is None or id_set is None: print(f"Warning: Failed to load data for {version}") return None return analysis_results, id_set, run_dir def get_source_jsonl_path(analysis_results: Dict[str, Any]) -> Optional[Path]: """Extract source JSONL file path from analysis results. Args: analysis_results: Dictionary from analysis_results.json Returns: Path to source JSONL file, or None if not found """ try: metadata = analysis_results.get("_metadata", {}) file_path = metadata.get("file_path") if file_path: return Path(file_path) print("Warning: No file_path found in _metadata") return None except Exception as e: print(f"Error extracting source JSONL path: {e}") return None def get_version_summary(analysis_results: Dict[str, Any]) -> Dict[str, Any]: """Extract key summary statistics from analysis results. Args: analysis_results: Dictionary from analysis_results.json Returns: Dictionary with summary statistics """ id_section = analysis_results.get("id", {}).get("summary", {}) tags_section = analysis_results.get("tags", {}).get("summary", {}) text_section = analysis_results.get("text", {}).get("summary", {}) language_section = analysis_results.get("language", {}).get("summary", {}) weight_section = analysis_results.get("weight", {}).get("summary", {}) stems_section = analysis_results.get("stems", {}).get("summary", {}) paths_section = analysis_results.get("paths", {}).get("summary", {}) metadata_section = analysis_results.get("_metadata", {}) return { "total_records": id_section.get("total_records", 0), "unique_ids": id_section.get("unique_ids", 0), "duplicate_ids": id_section.get("duplicate_ids", 0), "records_with_tags": tags_section.get("records_with_tags", 0), "unique_tags": tags_section.get("unique_tags", 0), "records_with_text": text_section.get("records_with_text", 0), "records_with_lang": language_section.get("records_with_lang", 0), "unique_languages": language_section.get("unique_languages", 0), "language_mismatch_rate": language_section.get("mismatch_rate", 0.0), "records_with_weight": weight_section.get("records_with_weight", 0), "records_with_stems": stems_section.get("records_with_stems", 0), "unique_stem_names": stems_section.get("unique_stem_names", 0), "local_path_count": paths_section.get("records_with_local_path", 0), "s3_path_count": paths_section.get("records_with_s3_path", 0), "path_anomalies": paths_section.get("total_anomalies", 0), "source_file": metadata_section.get("file_path", ""), "processing_time": metadata_section.get("timing", {}).get("overall_time", 0), }