Пример #1
0
    async def get_passage(self, bible: Bible, verses: VerseRange,
                          /) -> Passage:
        async with self.session.get(
                self._passage_url.with_query({
                    'search': str(verses),
                    'version': bible.service_version,
                    'interface': 'print',
                })) as response:
            text = await response.text(errors='replace')
            strainer = SoupStrainer(class_=re.compile(
                re.WORD_BOUNDARY,
                'result-text-style-',
                re.either('normal', 'rtl'),
                re.WORD_BOUNDARY,
            ))
            soup = BeautifulSoup(text, 'html.parser', parse_only=strainer)
            verse_block = soup.select_one(
                '.result-text-style-normal, .result-text-style-rtl')

            if verse_block is None:
                raise DoNotUnderstandError

            return self.__transform_verse_node(bible, verses, verse_block)
Пример #2
0
from __future__ import annotations

from typing import Final

from attrs import define, field
from botus_receptus import re
from bs4 import BeautifulSoup
from yarl import URL

from ..data import Passage, SearchResults, VerseRange
from ..exceptions import DoNotUnderstandError
from ..types import Bible
from .base_service import BaseService

_number_re: Final = re.compile(re.capture(re.one_or_more(re.DIGITS), re.DOT))
_book_map: Final[dict[str, str]] = {
    'Genesis': '01O',
    'Exodus': '02O',
    'Leviticus': '03O',
    'Numbers': '04O',
    'Deuteronomy': '05O',
    'Joshua': '06O',
    'Judges': '07O',
    'Ruth': '08O',
    '1 Samuel': '09O',
    '2 Samuel': '10O',
    '1 Kings': '11O',
    '2 Kings': '12O',
    '1 Chronicles': '13O',
    '2 Chronicles': '14O',
Пример #3
0
class BookDict(TypedDict):
    name: str
    osis: str
    alt: List[str]
    section: int


with (Path(__file__).resolve().parent / 'data' / 'books.json').open() as f:
    books_data: List[BookDict] = load(f)

# Inspired by
# https://github.com/TehShrike/verse-reference-regex/blob/master/create-regex.js
_book_re = re.compile(
    re.named_group('book')(re.either(*re.escape_all(
        unique_everseen(
            chain.from_iterable([[book['name'], book['osis']] + book['alt']
                                 for book in books_data]))))),
    re.optional(re.DOT),
)

_chapter_start_group = re.named_group('chapter_start')
_chapter_end_group = re.named_group('chapter_end')
_verse_start_group = re.named_group('verse_start')
_verse_end_group = re.named_group('verse_end')
_version_group = re.named_group('version')
_one_or_more_digit = re.one_or_more(re.DIGIT)
_colon = re.combine(re.any_number_of(re.WHITESPACE), ':',
                    re.any_number_of(re.WHITESPACE))

_reference_re = re.compile(
    _book_re,
Пример #4
0
from __future__ import annotations

from typing import Final

from attrs import define, field
from botus_receptus import re
from bs4 import BeautifulSoup, NavigableString, SoupStrainer, Tag
from yarl import URL

from ..data import Passage, SearchResults, VerseRange
from ..exceptions import DoNotUnderstandError
from ..types import Bible
from .base_service import BaseService

_total_re: Final = re.compile(
    re.START,
    re.named_group('total')(re.one_or_more(re.DIGITS)))


@define
class BibleGateway(BaseService):
    _passage_url: URL = field(init=False)
    _search_url: URL = field(init=False)

    def __attrs_post_init__(self, /) -> None:
        self._passage_url = URL('https://www.biblegateway.com/passage/')
        self._search_url = URL('https://www.biblegateway.com/quicksearch/')

    def __transform_verse_node(
        self,
        bible: Bible,
Пример #5
0
from typing import Any, Optional, List, Dict, AsyncIterator
from typing_extensions import TypedDict
from attr import dataclass, attrib
from aiohttp import BasicAuth
from botus_receptus import re
from bs4 import BeautifulSoup
from contextlib import asynccontextmanager
from yarl import URL

from .base_service import BaseService
from ..data import VerseRange, SearchResults, Passage
from ..exceptions import DoNotUnderstandError
from ..json import loads, get, has
from ..protocols import Bible

_img_re = re.compile('src="', re.named_group('src')('[^"]+'), '"')


class SummaryDict(TypedDict):
    total: int


class VerseDict(TypedDict):
    reference: str
    text: str


class SearchResultDict(TypedDict):
    summary: SummaryDict
    verses: List[VerseDict]
Пример #6
0
from __future__ import annotations

import logging
from abc import abstractmethod
from typing import Any, Final

import aiohttp
from attrs import define
from botus_receptus import re

from ..data import Passage, SearchResults, VerseRange
from ..types import Bible

_log: Final = logging.getLogger(__name__)

_whitespace_re: Final = re.compile(re.one_or_more(re.WHITESPACE))
_punctuation_re: Final = re.compile(re.one_or_more(re.WHITESPACE),
                                    re.capture(r'[,.;:]'),
                                    re.one_or_more(re.WHITESPACE))
_bold_re: Final = re.compile(r'__BOLD__')
_italic_re: Final = re.compile(r'__ITALIC__')
_specials_re: Final = re.compile(re.capture(r'[\*`]'))
_number_re: Final = re.compile(
    re.capture(r'\*\*', re.one_or_more(re.DIGIT), re.DOT, r'\*\*'))


@define
class BaseService(object):
    session: aiohttp.ClientSession
    config: dict[str, Any] | None
Пример #7
0
import logging

from typing import Any, Optional, Dict, List, AsyncContextManager, AsyncIterator
from abc import abstractmethod
from attr import dataclass
from contextlib import asynccontextmanager
from botus_receptus import re
from yarl import URL

from ..data import VerseRange, Passage, SearchResults
from ..protocols import Bible
from ..exceptions import ServiceLookupTimeout, ServiceSearchTimeout

log = logging.getLogger(__name__)

whitespace_re = re.compile(re.one_or_more(re.WHITESPACE))
bold_re = re.compile(r'__BOLD__')
italic_re = re.compile(r'__ITALIC__')
specials_re = re.compile(re.capture(r'[\*`]'))
number_re = re.compile(
    re.capture(r'\*\*', re.one_or_more(re.DIGIT), re.DOT, r'\*\*'))


@dataclass(slots=True)
class BaseService(object):
    session: aiohttp.ClientSession
    config: Optional[Dict[str, Any]]

    async def get_passage(self, bible: Bible, verses: VerseRange) -> Passage:
        log.debug(f'Getting passage {verses} ({bible.abbr})')