"""Data provider for the TUI.""" from __future__ import annotations import json from dataclasses import dataclass from pathlib import Path from src.data.loader import DataLoader from src.evaluation.storage import EvaluationStorage from src.models.evaluation import VerseEvaluation from src.models.verse import Verse, VerseReference @dataclass class VerseData: """Complete data for a verse including sources, translation, and evaluation.""" book: str chapter: int verse: int # Source texts greek_text: str | None kjv_text: str | None japanese_reference: str | None glossary_terms: list[str] # Translation translation_text: str | None translation_model: str | None translation_timestamp: str | None # Evaluation evaluation: VerseEvaluation | None class TUIDataProvider: """Provides data for the TUI from existing storage.""" def __init__(self, data_dir: Path | str) -> None: """Initialize the data provider. Args: data_dir: Path to the data directory. """ self.data_dir = Path(data_dir) self.loader = DataLoader(data_dir) self.eval_storage = EvaluationStorage(data_dir) self.output_dir = self.data_dir / "output" def get_available_books(self) -> list[str]: """Get list of books that have translations. Returns: List of book names. """ if not self.output_dir.exists(): return [] books = [] for book_dir in sorted(self.output_dir.iterdir()): if book_dir.is_dir(): # Check if there are any chapter files chapter_files = list(book_dir.glob("chapter_*.json")) # Filter out evaluation files chapter_files = [ f for f in chapter_files if not f.stem.endswith("_eval") ] if chapter_files: books.append(book_dir.name) return books def get_book_chapters(self, book: str) -> list[int]: """Get chapters that have translations for a book. Args: book: Book name. Returns: List of chapter numbers. """ book_dir = self.output_dir / book if not book_dir.exists(): return [] chapters = [] for chapter_file in sorted(book_dir.glob("chapter_*.json")): # Skip evaluation files if chapter_file.stem.endswith("_eval"): continue # Extract chapter number from filename (chapter_01.json -> 1) try: chapter_num = int(chapter_file.stem.split("_")[1]) chapters.append(chapter_num) except (IndexError, ValueError): continue return sorted(chapters) def get_chapter_verses(self, book: str, chapter: int) -> list[int]: """Get verse numbers in a translated chapter. Args: book: Book name. chapter: Chapter number. Returns: List of verse numbers. """ chapter_file = self.output_dir / book / f"chapter_{chapter:02d}.json" if not chapter_file.exists(): return [] with chapter_file.open("r", encoding="utf-8") as f: data = json.load(f) verses = data.get("verses", {}) return sorted(int(v) for v in verses.keys()) def get_flagged_verses(self, book: str, chapter: int) -> set[int]: """Get set of flagged verse numbers in a chapter. Args: book: Book name. chapter: Chapter number. Returns: Set of flagged verse numbers. """ chapter_eval = self.eval_storage.load_chapter_evaluation(book, chapter) if not chapter_eval: return set() return set(chapter_eval.flagged_verses) def is_chapter_evaluated(self, book: str, chapter: int) -> bool: """Check if a chapter has been evaluated. Args: book: Book name. chapter: Chapter number. Returns: True if evaluation exists. """ return self.eval_storage.is_chapter_evaluated(book, chapter) def get_verse_data(self, book: str, chapter: int, verse: int) -> VerseData: """Get complete verse data including sources, translation, and evaluation. Args: book: Book name. chapter: Chapter number. verse: Verse number. Returns: VerseData object with all available data. """ # Load source texts ref = VerseReference(book=book, chapter=chapter, verse=verse) source_verse: Verse = self.loader.load_verse(ref) # Load translation translation_text = None translation_model = None translation_timestamp = None glossary_terms: list[str] = [] chapter_file = self.output_dir / book / f"chapter_{chapter:02d}.json" if chapter_file.exists(): with chapter_file.open("r", encoding="utf-8") as f: data = json.load(f) verse_data = data.get("verses", {}).get(str(verse), {}) translation_text = verse_data.get("text") translation_model = verse_data.get("model") translation_timestamp = verse_data.get("timestamp") glossary_terms = verse_data.get("glossary_terms_applied", []) # Load evaluation evaluation = None chapter_eval = self.eval_storage.load_chapter_evaluation(book, chapter) if chapter_eval and verse in chapter_eval.verses: evaluation = chapter_eval.verses[verse] # Get Japanese reference (prefer Kougo, fallback to Taisho) japanese_ref = source_verse.japanese_kougo or source_verse.japanese_taisho return VerseData( book=book, chapter=chapter, verse=verse, greek_text=source_verse.greek_text, kjv_text=source_verse.english_kjv, japanese_reference=japanese_ref, glossary_terms=glossary_terms, translation_text=translation_text, translation_model=translation_model, translation_timestamp=translation_timestamp, evaluation=evaluation, )