OPEN SOURCE
software/src/models/pericope.py
Read on this site and download the files you need.
Nichijoyaku was created using AI. A complete review by human biblical-language and biblical-studies specialists has not been established. Read alongside established translations and original-language resources.
← Back to the open-source guide
1"""Pericope models for chunk-based translation."""23from __future__ import annotations45from datetime import datetime6from typing import TYPE_CHECKING, Any78from pydantic import BaseModel, Field, computed_field910if TYPE_CHECKING:11from src.models.verse import VerseReference121314class Pericope(BaseModel):15"""A cohesive unit of biblical text (paragraph/section).1617Pericopes are the natural divisions of biblical text used for18chunk-based translation to ensure smooth verse transitions.19"""2021id: str = Field(..., description="Unique pericope identifier")22title: str | None = Field(None, description="Section heading (English)")23title_ja: str | None = Field(None, description="Section heading (Japanese)")24start_chapter: int = Field(..., ge=1)25start_verse: int = Field(..., ge=1)26end_chapter: int = Field(..., ge=1)27end_verse: int = Field(..., ge=1)28verses: list[str] = Field(29..., description="List of verse refs like '1:1', '1:2'"30)31theme: str | None = Field(None, description="Thematic summary")32auto_generated: bool = Field(33default=False, description="True if generated from paragraph data"34)3536@computed_field37@property38def verse_count(self) -> int:39"""Get the number of verses in this pericope."""40return len(self.verses)4142@computed_field43@property44def is_single_chapter(self) -> bool:45"""Check if pericope spans only one chapter."""46return self.start_chapter == self.end_chapter4748def contains(self, chapter: int, verse: int) -> bool:49"""Check if a verse is in this pericope.5051Args:52chapter: Chapter number.53verse: Verse number.5455Returns:56True if the verse is within this pericope.57"""58ref = f"{chapter}:{verse}"59return ref in self.verses6061def get_verse_refs(self, book: str) -> list[VerseReference]:62"""Get VerseReference objects for all verses.6364Args:65book: Book name to use in references.6667Returns:68List of VerseReference objects.69"""70from src.models.verse import VerseReference7172refs = []73for v in self.verses:74chapter, verse = v.split(":")75refs.append(76VerseReference(book=book, chapter=int(chapter), verse=int(verse))77)78return refs7980def get_display_range(self) -> str:81"""Get a human-readable verse range string.8283Returns:84String like '1:1-18' or '1:1-2:12'.85"""86if self.is_single_chapter:87return f"{self.start_chapter}:{self.start_verse}-{self.end_verse}"88return (89f"{self.start_chapter}:{self.start_verse}-"90f"{self.end_chapter}:{self.end_verse}"91)929394class PericopeIndex(BaseModel):95"""Index of all pericopes for a book."""9697book: str = Field(..., description="Book name")98name: str = Field(..., description="Full book name")99pericopes: list[Pericope] = Field(default_factory=list)100sblgnt_paragraphs: list[dict] | None = Field(101None, description="Raw SBLGNT paragraph data"102)103104def find_pericope(self, chapter: int, verse: int) -> Pericope | None:105"""Find the pericope containing a verse.106107Args:108chapter: Chapter number.109verse: Verse number.110111Returns:112The Pericope containing the verse, or None if not found.113"""114for p in self.pericopes:115if p.contains(chapter, verse):116return p117return None118119def get_pericope_by_id(self, pericope_id: str) -> Pericope | None:120"""Get a pericope by its ID.121122Args:123pericope_id: The pericope identifier.124125Returns:126The Pericope if found, None otherwise.127"""128for p in self.pericopes:129if p.id == pericope_id:130return p131return None132133def get_chapter_pericopes(self, chapter: int) -> list[Pericope]:134"""Get all pericopes that include verses from a chapter.135136Args:137chapter: Chapter number.138139Returns:140List of pericopes that contain verses from the chapter.141"""142return [143p144for p in self.pericopes145if p.start_chapter <= chapter <= p.end_chapter146]147148149class ChunkTranslationOutput(BaseModel):150"""Output from chunk-based translation."""151152pericope_id: str = Field(..., description="Source pericope ID")153book: str = Field(..., description="Book name")154translations: dict[str, str] = Field(155..., description="Verse ref to translation mapping"156)157model: str = Field(..., description="Model used for translation")158timestamp: datetime = Field(default_factory=datetime.utcnow)159coherence_note: str | None = Field(160None, description="Note about translation coherence"161)162163@computed_field164@property165def verse_count(self) -> int:166"""Get number of verses translated."""167return len(self.translations)168169def get_verse_translation(self, chapter: int, verse: int) -> str | None:170"""Get translation for a specific verse.171172Args:173chapter: Chapter number.174verse: Verse number.175176Returns:177Translation text or None if not found.178"""179ref = f"{chapter}:{verse}"180return self.translations.get(ref)181182183class ChunkTranslationContext(BaseModel):184"""Full context for translating a pericope/chunk.185186Contains all verses in the pericope plus surrounding context187for cohesive translation.188"""189190pericope: Pericope = Field(..., description="The pericope being translated")191book: str = Field(..., description="Book name")192verses: list[Any] = Field(..., description="All verses in the pericope")193preceding_verses: list[Any] = Field(194default_factory=list, description="Context before pericope"195)196following_verses: list[Any] = Field(197default_factory=list, description="Context after pericope"198)199chapter_summary: Any = None200book_metadata: Any = None201glossary_terms: list[Any] = Field(default_factory=list)202203model_config = {"arbitrary_types_allowed": True}204205@property206def verse_refs(self) -> list[str]:207"""Get list of verse reference strings like '1:1'."""208return self.pericope.verses209210@property211def source_texts(self) -> dict[str, str]:212"""Get mapping of verse ref to Greek/Hebrew source text."""213texts = {}214for verse in self.verses:215ref = f"{verse.reference.chapter}:{verse.reference.verse}"216source = verse.greek_text or verse.hebrew_text or ""217if source:218texts[ref] = source219return texts220221def get_combined_source_text(self) -> str:222"""Get all source texts combined for the pericope."""223texts = []224for verse in self.verses:225source = verse.greek_text or verse.hebrew_text226if source:227ref = f"{verse.reference.chapter}:{verse.reference.verse}"228texts.append(f"[{ref}] {source}")229return "\n".join(texts)230