Pre-publication review draft
日常訳NICHIJOYAKU日本語 ↗

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

software/src/models/pericope.py230 linesDownload this file ↓

This is the same source included in the download. Each line number is a link you can save or share.

File verification detailsSHA-256 9eb493af1fb6e92fbaf73ec6a07cd3581c352eb635bdc138857e718c6795666c
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:11    from src.models.verse import VerseReference121314class Pericope(BaseModel):15    """A cohesive unit of biblical text (paragraph/section).1617    Pericopes are the natural divisions of biblical text used for18    chunk-based translation to ensure smooth verse transitions.19    """2021    id: str = Field(..., description="Unique pericope identifier")22    title: str | None = Field(None, description="Section heading (English)")23    title_ja: str | None = Field(None, description="Section heading (Japanese)")24    start_chapter: int = Field(..., ge=1)25    start_verse: int = Field(..., ge=1)26    end_chapter: int = Field(..., ge=1)27    end_verse: int = Field(..., ge=1)28    verses: list[str] = Field(29        ..., description="List of verse refs like '1:1', '1:2'"30    )31    theme: str | None = Field(None, description="Thematic summary")32    auto_generated: bool = Field(33        default=False, description="True if generated from paragraph data"34    )3536    @computed_field37    @property38    def verse_count(self) -> int:39        """Get the number of verses in this pericope."""40        return len(self.verses)4142    @computed_field43    @property44    def is_single_chapter(self) -> bool:45        """Check if pericope spans only one chapter."""46        return self.start_chapter == self.end_chapter4748    def contains(self, chapter: int, verse: int) -> bool:49        """Check if a verse is in this pericope.5051        Args:52            chapter: Chapter number.53            verse: Verse number.5455        Returns:56            True if the verse is within this pericope.57        """58        ref = f"{chapter}:{verse}"59        return ref in self.verses6061    def get_verse_refs(self, book: str) -> list[VerseReference]:62        """Get VerseReference objects for all verses.6364        Args:65            book: Book name to use in references.6667        Returns:68            List of VerseReference objects.69        """70        from src.models.verse import VerseReference7172        refs = []73        for v in self.verses:74            chapter, verse = v.split(":")75            refs.append(76                VerseReference(book=book, chapter=int(chapter), verse=int(verse))77            )78        return refs7980    def get_display_range(self) -> str:81        """Get a human-readable verse range string.8283        Returns:84            String like '1:1-18' or '1:1-2:12'.85        """86        if self.is_single_chapter:87            return f"{self.start_chapter}:{self.start_verse}-{self.end_verse}"88        return (89            f"{self.start_chapter}:{self.start_verse}-"90            f"{self.end_chapter}:{self.end_verse}"91        )929394class PericopeIndex(BaseModel):95    """Index of all pericopes for a book."""9697    book: str = Field(..., description="Book name")98    name: str = Field(..., description="Full book name")99    pericopes: list[Pericope] = Field(default_factory=list)100    sblgnt_paragraphs: list[dict] | None = Field(101        None, description="Raw SBLGNT paragraph data"102    )103104    def find_pericope(self, chapter: int, verse: int) -> Pericope | None:105        """Find the pericope containing a verse.106107        Args:108            chapter: Chapter number.109            verse: Verse number.110111        Returns:112            The Pericope containing the verse, or None if not found.113        """114        for p in self.pericopes:115            if p.contains(chapter, verse):116                return p117        return None118119    def get_pericope_by_id(self, pericope_id: str) -> Pericope | None:120        """Get a pericope by its ID.121122        Args:123            pericope_id: The pericope identifier.124125        Returns:126            The Pericope if found, None otherwise.127        """128        for p in self.pericopes:129            if p.id == pericope_id:130                return p131        return None132133    def get_chapter_pericopes(self, chapter: int) -> list[Pericope]:134        """Get all pericopes that include verses from a chapter.135136        Args:137            chapter: Chapter number.138139        Returns:140            List of pericopes that contain verses from the chapter.141        """142        return [143            p144            for p in self.pericopes145            if p.start_chapter <= chapter <= p.end_chapter146        ]147148149class ChunkTranslationOutput(BaseModel):150    """Output from chunk-based translation."""151152    pericope_id: str = Field(..., description="Source pericope ID")153    book: str = Field(..., description="Book name")154    translations: dict[str, str] = Field(155        ..., description="Verse ref to translation mapping"156    )157    model: str = Field(..., description="Model used for translation")158    timestamp: datetime = Field(default_factory=datetime.utcnow)159    coherence_note: str | None = Field(160        None, description="Note about translation coherence"161    )162163    @computed_field164    @property165    def verse_count(self) -> int:166        """Get number of verses translated."""167        return len(self.translations)168169    def get_verse_translation(self, chapter: int, verse: int) -> str | None:170        """Get translation for a specific verse.171172        Args:173            chapter: Chapter number.174            verse: Verse number.175176        Returns:177            Translation text or None if not found.178        """179        ref = f"{chapter}:{verse}"180        return self.translations.get(ref)181182183class ChunkTranslationContext(BaseModel):184    """Full context for translating a pericope/chunk.185186    Contains all verses in the pericope plus surrounding context187    for cohesive translation.188    """189190    pericope: Pericope = Field(..., description="The pericope being translated")191    book: str = Field(..., description="Book name")192    verses: list[Any] = Field(..., description="All verses in the pericope")193    preceding_verses: list[Any] = Field(194        default_factory=list, description="Context before pericope"195    )196    following_verses: list[Any] = Field(197        default_factory=list, description="Context after pericope"198    )199    chapter_summary: Any = None200    book_metadata: Any = None201    glossary_terms: list[Any] = Field(default_factory=list)202203    model_config = {"arbitrary_types_allowed": True}204205    @property206    def verse_refs(self) -> list[str]:207        """Get list of verse reference strings like '1:1'."""208        return self.pericope.verses209210    @property211    def source_texts(self) -> dict[str, str]:212        """Get mapping of verse ref to Greek/Hebrew source text."""213        texts = {}214        for verse in self.verses:215            ref = f"{verse.reference.chapter}:{verse.reference.verse}"216            source = verse.greek_text or verse.hebrew_text or ""217            if source:218                texts[ref] = source219        return texts220221    def get_combined_source_text(self) -> str:222        """Get all source texts combined for the pericope."""223        texts = []224        for verse in self.verses:225            source = verse.greek_text or verse.hebrew_text226            if source:227                ref = f"{verse.reference.chapter}:{verse.reference.verse}"228                texts.append(f"[{ref}] {source}")229        return "\n".join(texts)230