Source code for build_tools.syllable_walk_tui.services.exporter

"""
Export functionality for Syllable Walker TUI.

Provides functions for exporting selected names to various formats.
"""

from __future__ import annotations

import json
import random
from datetime import datetime, timezone
from pathlib import Path


[docs] def export_names_to_txt( names: list[str], json_output_path: str, ) -> tuple[Path, str | None]: """ Export names to a plain text file (one name per line). The TXT file is written to the same directory as the JSON output, using the same base filename with .txt extension. Args: names: List of names to export json_output_path: Path to JSON output file (TXT will have same base name) Returns: Tuple of (output_path, error_message_or_none) - output_path: Path where TXT was written (or would be written on error) - error: Error message if failed, None if successful Examples: >>> names = ["Alara", "Benton", "Carla"] >>> txt_path, error = export_names_to_txt(names, "/path/to/names.json") >>> if error: ... print(f"Export failed: {error}") >>> else: ... print(f"Exported to {txt_path}") """ # Derive TXT path from JSON path json_path = Path(json_output_path) txt_path = json_path.with_suffix(".txt") try: # Write names to TXT file (one per line) with open(txt_path, "w", encoding="utf-8") as f: for name in names: f.write(f"{name}\n") return txt_path, None except PermissionError as e: return txt_path, f"Permission denied: {e}" except OSError as e: return txt_path, f"File system error: {e}" except Exception as e: return txt_path, f"Unexpected error: {e}"
[docs] def export_sample_json( names: list[str], name_class: str, selections_dir: Path, sample_size: int = 5, seed: int | None = None, ) -> tuple[Path, str | None]: """ Export a random sample of names to a JSON file. This helper is used by the TUI renderer screen to create lightweight example payloads for API consumers. The output is named ``<name_class>_sample.json`` inside the selections directory. Args: names: Raw selected names to sample from name_class: Name class identifier (e.g., "first_name") selections_dir: Directory to write the JSON file into sample_size: Number of names to include (default: 5) seed: Optional seed for deterministic sampling (test support) Returns: Tuple of (output_path, error_message_or_none) """ # Ensure we have a valid selections directory to write into try: selections_dir.mkdir(parents=True, exist_ok=True) except OSError as exc: return selections_dir / f"{name_class}_sample.json", ( f"Failed to create selections dir: {exc}" ) # Normalize names to lowercase and remove duplicates while preserving order seen: set[str] = set() normalized: list[str] = [] for raw in names: if not raw: continue value = raw.strip().lower() if not value or value in seen: continue seen.add(value) normalized.append(value) if not normalized: return selections_dir / f"{name_class}_sample.json", "No names available to sample." # Clamp the sample size to the available pool sample_count = min(sample_size, len(normalized)) # Use deterministic RNG when a seed is provided for testability; no crypto needed. rng = random.Random(seed) if seed is not None else random.SystemRandom() # nosec B311 sampled = rng.sample(normalized, sample_count) output_path = selections_dir / f"{name_class}_sample.json" # Build the sample payload for downstream consumers payload = { "name_class": name_class, "generated_at": datetime.now(timezone.utc).isoformat(), "sample_count": sample_count, "samples": sampled, } try: with open(output_path, "w", encoding="utf-8") as handle: json.dump(payload, handle, indent=2) return output_path, None except OSError as exc: return output_path, f"Failed to write sample JSON: {exc}"