refspy.navigator

Navigation functions for Reference objects

  1"""Navigation functions for Reference objects"""
  2
  3from refspy.models.book import Book
  4from refspy.types.number import Number
  5from refspy.models.range import range
  6from refspy.models.reference import Reference, reference
  7from refspy.models.verse import verse
  8
  9
 10class Navigator:
 11    def __init__(
 12        self,
 13        books: dict[tuple[Number, Number], Book],
 14        book_aliases: dict[str, tuple[Number, Number]],
 15    ) -> None:
 16        """
 17        Note:
 18            Taking prev and next from references makes the most sense if ranges
 19            are sorted or have been collated by book and chapter.
 20        Example:
 21
 22            Using the Navigator module on its own:
 23
 24            ```
 25            from refspy.indexers import index_book_aliases, index_books
 26            from refspy.libraries.en_US import NT, OT
 27            from refspy.navigator import Navigator
 28            from refspy.models.reference import chapter_reference
 29
 30            libraries = [OT, NT]
 31            navigator = Navigator(
 32                index_books(libraries),
 33                index_book_aliases(libraries)
 34            )
 35            matt_1 = chapter_reference(NT.id, 1, 1)
 36            matt_2 = chapter_reference(NT.id, 1, 2)
 37            assert navigator.next_chapter(matt_1) == matt_2
 38            assert navigator.prev_chapter(matt_1) is None
 39            ```
 40
 41        Example:
 42
 43            Using the navigator within the `refspy` package.
 44
 45            ```
 46            from refspy import refspy
 47
 48            __ = refspy()
 49            matt_1 = __.bcv('Matt', 1)
 50            matt_2 = __.bcv('Matt', 2)
 51            assert __.next_chapter(matt_1) == matt_2
 52            assert __.prev_chapter(matt_1) is None
 53            ```
 54        """
 55
 56        self.books: dict[tuple[Number, Number], Book] = books
 57        """Lookup `refspy.models.book.Book` by `(library.id, book.id)`."""
 58
 59        self.book_aliases: dict[str, tuple[Number, Number]] = book_aliases
 60        """Lookup `(library.id, book.id)` by book alias."""
 61
 62    def prev_chapter(self, ref: Reference) -> Reference | None:
 63        """Find a reference to the previous chapter.
 64
 65        We step into preceding books but not preceding libraries.
 66
 67        Returns:
 68            None: If this is the first chapter of a `refspy.library.Library`.
 69        """
 70        v1 = ref.ranges[0].start
 71        if v1.chapter > 1:
 72            return reference(
 73                range(
 74                    verse(v1.library, v1.book, v1.chapter - 1, 1),
 75                    verse(v1.library, v1.book, v1.chapter - 1, 999),
 76                )
 77            )
 78        else:
 79            if (v1.library, v1.book - 1) not in self.books:
 80                return None
 81            prev_book = self.books[v1.library, v1.book - 1]
 82            end_chapter = prev_book.chapters
 83            return reference(
 84                range(
 85                    verse(v1.library, prev_book.id, end_chapter, 1),
 86                    verse(v1.library, prev_book.id, end_chapter, 999),
 87                )
 88            )
 89
 90    def next_chapter(self, ref: Reference) -> Reference | None:
 91        """Find a reference to the next chapter.
 92
 93        We step into succeeding books but not succeeding libraries.
 94
 95        Returns:
 96            None: if this is the last chapter of a `refspy.library.Library`.
 97        """
 98        v1 = ref.ranges[0].start
 99        end_chapter = self.books[v1.library, v1.book].chapters
100        if v1.chapter < end_chapter:
101            return reference(
102                range(
103                    verse(v1.library, v1.book, v1.chapter + 1, 1),
104                    verse(v1.library, v1.book, v1.chapter + 1, 999),
105                )
106            )
107        else:
108            if (v1.library, v1.book + 1) not in self.books:
109                return None
110            next_book = self.books[v1.library, v1.book + 1]
111            return reference(
112                range(
113                    verse(v1.library, next_book.id, 1, 1),
114                    verse(v1.library, next_book.id, 1, 999),
115                )
116            )