scripturelookup 0.1.1__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {scripturelookup-0.1.1/src/scripturelookup.egg-info → scripturelookup-0.1.2}/PKG-INFO +1 -1
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/scripturelookup/data.py +25 -5
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/scripturelookup/lookup.py +11 -5
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/scripturelookup/patterns.py +5 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2/src/scripturelookup.egg-info}/PKG-INFO +1 -1
- scripturelookup-0.1.2/src/scripturelookup.egg-info/scm_version.json +8 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/tests/test_detect.py +48 -0
- scripturelookup-0.1.1/src/scripturelookup.egg-info/scm_version.json +0 -8
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/.github/workflows/publish.yml +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/.gitignore +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/LICENSE +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/README.md +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/pyproject.toml +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/requirements.txt +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/setup.cfg +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/geezify-python-main/LICENSE +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/geezify-python-main/README.md +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/geezify-python-main/arabify.py +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/geezify-python-main/arabify_test.py +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/geezify-python-main/geezify.py +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/geezify-python-main/geezify_test.py +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/geezify-python-main/test_data.py +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/scripturelookup/__init__.py +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/scripturelookup/command_line.py +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/scripturelookup/data/metadata-languages.min.json +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/scripturelookup/data/metadata-scriptures.min.json +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/scripturelookup/numbers.py +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/scripturelookup.egg-info/SOURCES.txt +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/scripturelookup.egg-info/dependency_links.txt +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/scripturelookup.egg-info/entry_points.txt +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/scripturelookup.egg-info/requires.txt +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/scripturelookup.egg-info/scm_file_list.json +0 -0
- {scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/scripturelookup.egg-info/top_level.txt +0 -0
|
@@ -144,11 +144,15 @@ highest_book_number = len(roman_numeral_words)
|
|
|
144
144
|
ordinal_forms_cache = {}
|
|
145
145
|
def get_ordinal_forms(lang):
|
|
146
146
|
if lang not in ordinal_forms_cache:
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
for
|
|
150
|
-
|
|
151
|
-
|
|
147
|
+
forms = {}
|
|
148
|
+
for index, group in enumerate(ordinal_words.get(lang, [])):
|
|
149
|
+
for form in group:
|
|
150
|
+
forms[form.lower()] = index + 1
|
|
151
|
+
# The ordinal indicators are hard to type on many keyboards, so "1º" is also accepted as "1o" and
|
|
152
|
+
# "1ª" as "1a". They're derived rather than listed, so a language only spells out what's linguistic.
|
|
153
|
+
plain_form = form.lower().replace('º', 'o').replace('ª', 'a')
|
|
154
|
+
forms.setdefault(plain_form, index + 1)
|
|
155
|
+
ordinal_forms_cache[lang] = forms
|
|
152
156
|
return ordinal_forms_cache[lang]
|
|
153
157
|
|
|
154
158
|
# Get a map of number form to the number it stands for, for the prefix on a numbered book name. It's the
|
|
@@ -163,6 +167,22 @@ def get_book_number_forms(lang):
|
|
|
163
167
|
book_number_forms_cache[lang] = forms
|
|
164
168
|
return book_number_forms_cache[lang]
|
|
165
169
|
|
|
170
|
+
# Get the maps above keyed for comparison, so the number a matched form stands for can be looked up however
|
|
171
|
+
# the form was spelled. The pattern built from these forms matches text the map has no key for: a character
|
|
172
|
+
# class matches "Decimo" where the map holds "décimo", and whitespace is matched loosely, so "décimo
|
|
173
|
+
# primero" can arrive with a non-breaking space between the words.
|
|
174
|
+
normalized_ordinal_forms_cache = {}
|
|
175
|
+
def get_normalized_ordinal_forms(lang):
|
|
176
|
+
if lang not in normalized_ordinal_forms_cache:
|
|
177
|
+
normalized_ordinal_forms_cache[lang] = {normalize_for_compare(form): number for form, number in get_ordinal_forms(lang).items()}
|
|
178
|
+
return normalized_ordinal_forms_cache[lang]
|
|
179
|
+
|
|
180
|
+
normalized_book_number_forms_cache = {}
|
|
181
|
+
def get_normalized_book_number_forms(lang):
|
|
182
|
+
if lang not in normalized_book_number_forms_cache:
|
|
183
|
+
normalized_book_number_forms_cache[lang] = {normalize_for_compare(form): number for form, number in get_book_number_forms(lang).items()}
|
|
184
|
+
return normalized_book_number_forms_cache[lang]
|
|
185
|
+
|
|
166
186
|
# List conjunctions from every language, split into the ones written as a word ("and", "y", "et") and the ones written as a symbol ("&")
|
|
167
187
|
all_list_conjunctions = sorted({c for words in reference_words.values() for c in words['list_conjunctions']})
|
|
168
188
|
list_conjunction_words = [c for c in all_list_conjunctions if any(char.isalpha() for char in c)]
|
|
@@ -201,7 +201,8 @@ def get_numbered_book_patterns(lang):
|
|
|
201
201
|
numbered_book_pattern_cache[lang] = {
|
|
202
202
|
'fragment': r'|'.join(rf'(?:{form_pattern})\s+(?:{stem_pattern})' for form_pattern, stem_pattern in branches),
|
|
203
203
|
'rewrite': re.compile(rf'{patterns.reference_start_boundary_pattern}({rewrite_alternation})\s+', flags=re.IGNORECASE),
|
|
204
|
-
'
|
|
204
|
+
# Keyed for comparison, since the patterns above match spellings the plain forms don't cover
|
|
205
|
+
'number_by_form': data.get_normalized_book_number_forms(lang),
|
|
205
206
|
}
|
|
206
207
|
return numbered_book_pattern_cache[lang]
|
|
207
208
|
|
|
@@ -239,7 +240,8 @@ def get_ordinal_patterns(lang):
|
|
|
239
240
|
ordinal_run = rf'(?:{ordinals})(?:(?:{joiner_alternation})(?:{ordinals}))*'
|
|
240
241
|
|
|
241
242
|
ordinal_pattern_cache[lang] = {
|
|
242
|
-
'
|
|
243
|
+
# Keyed for comparison, since the patterns above match spellings the plain forms don't cover
|
|
244
|
+
'number_by_form': data.get_normalized_ordinal_forms(lang),
|
|
243
245
|
# Spliced into the detection pattern as an anchor that needs no number after it – the ordinal is the number
|
|
244
246
|
'fragment': rf'(?:{ordinal_run})\s+(?:{books})',
|
|
245
247
|
'rewrite': re.compile(rf'{patterns.reference_start_boundary_pattern}({ordinal_run})\s+({books}){patterns.not_followed_by_letter_pattern}', flags=re.IGNORECASE),
|
|
@@ -263,7 +265,7 @@ def replace_ordinal_references(input_string, lang):
|
|
|
263
265
|
|
|
264
266
|
def replace(match):
|
|
265
267
|
ordinal_run, book_name = match.group(1), match.group(2)
|
|
266
|
-
ordinal_numbers = [ordinal_patterns['number_by_form'][ordinal.group(0)
|
|
268
|
+
ordinal_numbers = [ordinal_patterns['number_by_form'][data.normalize_for_compare(ordinal.group(0))] for ordinal in ordinal_patterns['single_ordinal'].finditer(ordinal_run)]
|
|
267
269
|
is_range = len(ordinal_numbers) > 1 and ordinal_patterns['range_conjunction'] and ordinal_patterns['range_conjunction'].search(ordinal_run)
|
|
268
270
|
numbers_string = f'{ordinal_numbers[0]}-{ordinal_numbers[-1]}' if is_range else ','.join(str(number) for number in ordinal_numbers)
|
|
269
271
|
return f'{book_name} {numbers_string}'
|
|
@@ -278,7 +280,7 @@ def replace_book_number_prefixes(input_string, lang):
|
|
|
278
280
|
return input_string
|
|
279
281
|
|
|
280
282
|
def replace(match):
|
|
281
|
-
return str(numbered_book_patterns['number_by_form'][match.group(1)
|
|
283
|
+
return str(numbered_book_patterns['number_by_form'][data.normalize_for_compare(match.group(1))]) + ' '
|
|
282
284
|
return numbered_book_patterns['rewrite'].sub(replace, input_string)
|
|
283
285
|
|
|
284
286
|
|
|
@@ -507,6 +509,8 @@ def parse_verses_string(verses_string, lang = 'en', range_split_limit = 1):
|
|
|
507
509
|
verses_string = (verses_string or '').replace('p', '').strip()
|
|
508
510
|
if not verses_string:
|
|
509
511
|
return None
|
|
512
|
+
# A footnote letter marks a study note, not part of the verse, so it's dropped. Example: "7a" –> "7"
|
|
513
|
+
verses_string = patterns.verse_footnote_letter.sub('', verses_string)
|
|
510
514
|
|
|
511
515
|
unique_verses = set()
|
|
512
516
|
all_verses_are_integers = True
|
|
@@ -732,7 +736,9 @@ def parse_references_string(input_string, lang = 'en', sort_by = None, skip_clea
|
|
|
732
736
|
unparsed = input_string
|
|
733
737
|
verses_string = ''
|
|
734
738
|
verses_string, remaining_context_verses_string = (patterns.opening_parenthesis.split(patterns.closing_parenthesis.sub('', verses_string)) + [''])[:2]
|
|
735
|
-
|
|
739
|
+
# Only numbers count as context verses, so stray words after an unclosed parenthesis are dropped. Example: "Matthew 7:1–2 (see" –> "Matthew 7:1–2"
|
|
740
|
+
remaining_context_verses_string = remaining_context_verses_string.strip()
|
|
741
|
+
if context_verses_string is None and patterns.numbers_and_separators.match(remaining_context_verses_string):
|
|
736
742
|
context_verses_string = remaining_context_verses_string
|
|
737
743
|
|
|
738
744
|
# Get chapter string and book string
|
|
@@ -56,6 +56,11 @@ chapter_range = re.compile(rf'\d+(?:{verse_range_separators_pattern})\d+')
|
|
|
56
56
|
number_continuation_pattern = rf'\d|\s|{chapter_verse_separators_pattern}|{verse_range_separators_pattern}|{verse_group_separators_pattern}'
|
|
57
57
|
# A string made up entirely of numbers and separators. Example: "3, 5–7"
|
|
58
58
|
numbers_and_separators = re.compile(rf'^\d(?:{number_continuation_pattern})*$')
|
|
59
|
+
# A footnote letter after a verse number. Example: "6:7a" –> "6:7"
|
|
60
|
+
# Only parsing strips it – the detection patterns stop at the digit, so a detected span covers "Alma 32:21"
|
|
61
|
+
# rather than "Alma 32:21a". Letting detection take a trailing letter would swallow the Spanish and
|
|
62
|
+
# Portuguese range conjunction, as in "Alma 32:21 a 23".
|
|
63
|
+
verse_footnote_letter = re.compile(rf'(?<=\d)[a-zA-Z]{not_followed_by_letter_pattern}')
|
|
59
64
|
# The chapter and verses at the end of a reference, so the book name can be split off the front
|
|
60
65
|
trailing_chapter = re.compile(rf'^.*?(\d(?:{number_continuation_pattern})*)$')
|
|
61
66
|
# Text after the end of a reference. Example: "1 John 3:2 2" –> " 2"
|
|
@@ -255,6 +255,31 @@ def test_spaced_out_verses_are_not_stripped_as_trailing_text():
|
|
|
255
255
|
assert lookup.get_label('1 John 3:2 2') == '1\xa0John\xa03:2'
|
|
256
256
|
|
|
257
257
|
|
|
258
|
+
# Words inside a parenthetical aren't context verses, even when a second reference splits the parenthetical apart
|
|
259
|
+
def test_words_in_a_parenthetical_are_not_context_verses():
|
|
260
|
+
text = 'Matthew 7:1–2 (see JST Matthew 7:1–2)'
|
|
261
|
+
assert lookup.get_church_uri(text) == '/scriptures/nt/matt/7.1-2\n/scriptures/jst/jst-matt/7.1-2'
|
|
262
|
+
assert lookup.get_church_uri(text, use_query_parameters = True) == '/scriptures/nt/matt/7?id=p1-p2\n/scriptures/jst/jst-matt/7?id=p1-p2'
|
|
263
|
+
# ...while numbers still are
|
|
264
|
+
assert lookup.get_church_uri('Gen. 1:3 (3–4)') == '/scriptures/ot/gen/1.3(3-4)'
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
# A footnote letter after a verse marks a study note, so it's dropped rather than taking the verse with it
|
|
268
|
+
@pytest.mark.parametrize('text, expected_uri', [
|
|
269
|
+
('Genesis 6:7a', '/scriptures/ot/gen/6.7'),
|
|
270
|
+
('Genesis 6:7a, 13', '/scriptures/ot/gen/6.7,13'),
|
|
271
|
+
('Alma 32:21a–23b', '/scriptures/bofm/alma/32.21-23'),
|
|
272
|
+
('Gen. 1:3a (3–4)', '/scriptures/ot/gen/1.3(3-4)'),
|
|
273
|
+
])
|
|
274
|
+
def test_verse_footnote_letters_are_dropped(text, expected_uri):
|
|
275
|
+
assert lookup.get_church_uri(text) == expected_uri
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
# A footnote letter is citation syntax rather than sloppy input, so it goes even when cleanup is skipped
|
|
279
|
+
def test_verse_footnote_letters_are_dropped_even_when_cleanup_is_skipped():
|
|
280
|
+
assert lookup.get_church_uri('Genesis 6:7a', skip_cleanup = True) == '/scriptures/ot/gen/6.7'
|
|
281
|
+
|
|
282
|
+
|
|
258
283
|
# A book with only one chapter takes a bare number as its verse – there is no Jude 3
|
|
259
284
|
@pytest.mark.parametrize('text, expected_uris', [
|
|
260
285
|
('Jude 3', ['/scriptures/nt/jude/1.3']),
|
|
@@ -291,11 +316,20 @@ def test_single_chapter_rule_applies_even_when_cleanup_is_skipped():
|
|
|
291
316
|
('Premier Néphi 3:7', 'fr', '/scriptures/bofm/1-ne/3.7'),
|
|
292
317
|
('1er Néphi 3:7', 'fr', '/scriptures/bofm/1-ne/3.7'),
|
|
293
318
|
('Primer Nefi 3:7', 'es', '/scriptures/bofm/1-ne/3.7'),
|
|
319
|
+
('1o Nefi 3:7', 'es', '/scriptures/bofm/1-ne/3.7'),
|
|
320
|
+
('3a Nefi 11', 'es', '/scriptures/bofm/3-ne/11'),
|
|
321
|
+
('2o Néfi 2:25', 'pt', '/scriptures/bofm/2-ne/2.25'),
|
|
294
322
|
])
|
|
295
323
|
def test_book_numbers_may_be_written_out(text, lang, expected_uri):
|
|
296
324
|
assert lookup.get_church_uri(text, lang = lang) == expected_uri
|
|
297
325
|
|
|
298
326
|
|
|
327
|
+
# "1o" and "1a" stand in for "1º" and "1ª", but a verse with a footnote letter is never read as an ordinal
|
|
328
|
+
def test_verse_letters_are_not_ordinals():
|
|
329
|
+
assert lookup.detect_references('Ver Génesis 6:7a Nefi 3:7.', lang = 'es') == [['Génesis 6:7', 4, 15]]
|
|
330
|
+
assert lookup.detect_references('Ver Alma 32:21a y 3a Nefi 11.', lang = 'es') == [['Alma 32:21', 4, 14], ['3a Nefi 11', 18, 28]]
|
|
331
|
+
|
|
332
|
+
|
|
299
333
|
# An article of faith cited by position is that article, since the book is a single chapter
|
|
300
334
|
@pytest.mark.parametrize('text, lang, expected_uris', [
|
|
301
335
|
('First Article of Faith', 'en', ['/scriptures/pgp/a-of-f/1.1']),
|
|
@@ -304,6 +338,8 @@ def test_book_numbers_may_be_written_out(text, lang, expected_uri):
|
|
|
304
338
|
('1er article de foi', 'fr', ['/scriptures/pgp/a-of-f/1.1']),
|
|
305
339
|
('Primer Artículo de Fe', 'es', ['/scriptures/pgp/a-of-f/1.1']),
|
|
306
340
|
('Primeira regra de fé', 'pt', ['/scriptures/pgp/a-of-f/1.1']),
|
|
341
|
+
('1o artículo de fe', 'es', ['/scriptures/pgp/a-of-f/1.1']),
|
|
342
|
+
('1a regra de fé', 'pt', ['/scriptures/pgp/a-of-f/1.1']),
|
|
307
343
|
('First and Third Articles of Faith', 'en', ['/scriptures/pgp/a-of-f/1.1', '/scriptures/pgp/a-of-f/1.3']),
|
|
308
344
|
('First through Third Articles of Faith', 'en', ['/scriptures/pgp/a-of-f/1.1-3']),
|
|
309
345
|
('Premier et troisième articles de foi', 'fr', ['/scriptures/pgp/a-of-f/1.1', '/scriptures/pgp/a-of-f/1.3']),
|
|
@@ -353,6 +389,18 @@ def test_abbreviated_ordinals_as_book_numbers(text, lang, expected_uri):
|
|
|
353
389
|
assert lookup.get_church_uri(text, lang = lang) == expected_uri
|
|
354
390
|
|
|
355
391
|
|
|
392
|
+
# An ordinal matched with its accent left out resolves to the same number, since the character classes in the
|
|
393
|
+
# pattern accept spellings that aren't listed as forms
|
|
394
|
+
@pytest.mark.parametrize('text, lang, expected_uri', [
|
|
395
|
+
('Decimo articulo de fe', 'es', '/scriptures/pgp/a-of-f/1.10'),
|
|
396
|
+
('decimo primer articulo de fe', 'es', '/scriptures/pgp/a-of-f/1.11'),
|
|
397
|
+
('Deuxieme article de foi', 'fr', '/scriptures/pgp/a-of-f/1.2'),
|
|
398
|
+
('Premier Nephi 3:7', 'fr', '/scriptures/bofm/1-ne/3.7'),
|
|
399
|
+
])
|
|
400
|
+
def test_ordinals_resolve_without_their_accents(text, lang, expected_uri):
|
|
401
|
+
assert lookup.get_church_uri(text, lang = lang) == expected_uri
|
|
402
|
+
|
|
403
|
+
|
|
356
404
|
# Rewriting an ordinal or a written-out book number is cleanup, so skip_cleanup skips it
|
|
357
405
|
def test_skip_cleanup_leaves_written_out_numbers_alone():
|
|
358
406
|
assert lookup.get_church_uri('Premier article de foi', lang = 'fr', skip_cleanup = True) == ''
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/scripturelookup/data/metadata-languages.min.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/scripturelookup.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/scripturelookup.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
{scripturelookup-0.1.1 → scripturelookup-0.1.2}/src/scripturelookup.egg-info/scm_file_list.json
RENAMED
|
File without changes
|
|
File without changes
|