linkture 3.3.1__tar.gz → 4.1.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: linkture
3
- Version: 3.3.1
3
+ Version: 4.1.0
4
4
  Summary: PARSE and PROCESS BIBLE SCRIPTURE REFERENCES: extract, tag, link, rewrite, translate, BCV-encode and decode
5
5
  Keywords: bible,scriptures,scripture-references,scripture-translation,scripture-parser,scripture-linker
6
6
  Author-Email: "Eryk J." <infiniti@inventati.org>
@@ -23,7 +23,6 @@ Requires-Dist: setuptools>=59.6.0
23
23
  Requires-Dist: argparse>=1.4.0
24
24
  Requires-Dist: regex>=2023.8.8
25
25
  Requires-Dist: unidecode>=1.3.8
26
- Requires-Dist: pandas==2.2.*
27
26
  Requires-Dist: pathlib>=1.0.1
28
27
  Description-Content-Type: text/markdown
29
28
 
@@ -10,7 +10,6 @@ dependencies = [
10
10
  "argparse>=1.4.0",
11
11
  "regex>=2023.8.8",
12
12
  "unidecode>=1.3.8",
13
- "pandas==2.2.*",
14
13
  "pathlib>=1.0.1",
15
14
  ]
16
15
  requires-python = ">=3.9"
@@ -36,7 +35,7 @@ keywords = [
36
35
  "scripture-parser",
37
36
  "scripture-linker",
38
37
  ]
39
- version = "3.3.1"
38
+ version = "4.1.0"
40
39
 
41
40
  [project.license]
42
41
  text = "MIT"
@@ -26,7 +26,7 @@
26
26
  SOFTWARE.
27
27
  """
28
28
 
29
- import argparse
29
+ import argparse, sys, traceback
30
30
  from .linkture import _available_languages, __app__, __version__, Scriptures
31
31
  from ast import literal_eval
32
32
 
@@ -34,24 +34,31 @@ from ast import literal_eval
34
34
  def main(args):
35
35
 
36
36
  def switchboard(text):
37
- if args['l'] is not None:
38
- prefix = '<a href="'
39
- suffix = '">'
40
- if len(args['l']) > 1 and args['l'][1] != '':
41
- suffix = args['l'][1]
42
- if len(args['l']) > 0 and args['l'][0] != '':
43
- prefix = args['l'][0]
44
- return s.link_scriptures(text, prefix, suffix)
45
- elif args['c']:
46
- return s.code_scriptures(text)
47
- elif args['d']:
48
- return s.decode_scriptures(literal_eval(text))
49
- elif args['x']:
50
- return s.list_scriptures(text)
51
- elif args['t']:
52
- return s.tag_scriptures(text)
53
- else:
54
- return s.rewrite_scriptures(text)
37
+ try:
38
+ if args['l'] is not None:
39
+ prefix = '<a href="'
40
+ suffix = '">'
41
+ if len(args['l']) > 1 and args['l'][1] != '':
42
+ suffix = args['l'][1]
43
+ if len(args['l']) > 0 and args['l'][0] != '':
44
+ prefix = args['l'][0]
45
+ return s.link_scriptures(text, prefix, suffix)
46
+ elif args['c']:
47
+ return s.code_scriptures(text)
48
+ elif args['d']:
49
+ return s.decode_scriptures(literal_eval(text))
50
+ elif args['x']:
51
+ return s.list_scriptures(text)
52
+ elif args['t']:
53
+ return s.tag_scriptures(text)
54
+ else:
55
+ return s.rewrite_scriptures(text)
56
+ except Exception as e:
57
+ print("\n--- CRASH DETECTED ---", file=sys.stderr)
58
+ print("Input causing failure:", repr(text), file=sys.stderr)
59
+ print("Error:", str(e), file=sys.stderr)
60
+ traceback.print_exc()
61
+ raise
55
62
 
56
63
  form = None
57
64
  if args['standard']:
@@ -27,11 +27,10 @@
27
27
  """
28
28
 
29
29
  __app__ = 'linkture'
30
- __version__ = 'v3.3.1'
30
+ __version__ = 'v4.1.0'
31
31
 
32
32
 
33
33
  import json, regex, sqlite3
34
- import pandas as pd
35
34
  from pathlib import Path
36
35
  from unidecode import unidecode
37
36
 
@@ -66,25 +65,27 @@ class Scriptures():
66
65
  form = 5
67
66
  else:
68
67
  form = 3
69
- self._src_book_names = {}
70
- path = Path(__file__).resolve().parent
71
68
 
72
- self._tr_book_names = ['Bible']
69
+ path = Path(__file__).resolve().parent
73
70
  con = sqlite3.connect(path / 'res/resources.db')
74
71
  cur = con.cursor()
75
- for rec in cur.execute(f"SELECT * FROM Books WHERE Language = '{translate}';").fetchall():
72
+
73
+ self._src_book_names = {}
74
+ self._tr_book_names = ['Bible']
75
+ for rec in cur.execute(f"SELECT * FROM Books WHERE Language = ?;", (translate,)).fetchall():
76
76
  if self._upper:
77
77
  tr = rec[form].upper()
78
78
  else:
79
79
  tr = rec[form]
80
80
  self._tr_book_names.insert(rec[2], tr)
81
- for rec in cur.execute(f"SELECT * FROM Books WHERE Language = '{language}';").fetchall():
81
+ for rec in cur.execute(f"SELECT * FROM Books WHERE Language = ?;", (language,)).fetchall():
82
82
  for i in range(3,6):
83
83
  item = rec[i]
84
84
  if not self._nl:
85
85
  item = unidecode(item)
86
86
  normalized = regex.sub(r'\p{P}|\p{Z}', '', item.upper())
87
87
  self._src_book_names[normalized] = rec[2]
88
+
88
89
  with open(path / 'res/custom.json', 'r', encoding='UTF-8') as json_file:
89
90
  b = json.load(json_file)
90
91
  if language in b.keys():
@@ -95,12 +96,30 @@ class Scriptures():
95
96
  item = unidecode(item)
96
97
  normalized = regex.sub(r'\p{P}|\p{Z}', '', item.upper())
97
98
  self._src_book_names[normalized] = row[0]
98
- self._ranges = pd.read_sql_query("SELECT * FROM Ranges;", con)
99
- self._verses = pd.read_sql_query("SELECT * FROM Verses;", con)
100
- self._chapters = pd.read_sql_query("SELECT * FROM Chapters;", con)
101
- self._headings = (3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 34, 35, 36, 37, 38, 39, 40, 41, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 92, 98, 100, 101, 102, 103, 108, 109, 110, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 138, 139, 140, 141, 142, 143, 144, 145)
99
+
100
+ # Ranges: {(book, chapter): last} (chapter 0 -> num of chapters in book)
101
+ self._ranges = {}
102
+ for book, chapter, last in cur.execute("SELECT Book, Chapter, Last FROM Ranges;"):
103
+ self._ranges[(book, chapter)] = last
104
+
105
+ # Chapters: two-way mappings
106
+ self._chapters = {}
107
+ self._chapters_id = {}
108
+ for chapter_id, book, chapter in cur.execute("SELECT ChapterId, Book, Chapter FROM Chapters;"):
109
+ self._chapters[(book, chapter)] = chapter_id
110
+ self._chapters_id[chapter_id] = (book, chapter)
111
+
112
+ # Verses: two-way mappings
113
+ self._verses = {}
114
+ self._verses_id = {}
115
+ for verse_id, book, chapter, verse in cur.execute("SELECT VerseId, Book, Chapter, Verse FROM Verses;"):
116
+ self._verses[(book, chapter, verse)] = verse_id
117
+ self._verses_id[verse_id] = (book, chapter, verse)
118
+
102
119
  cur.close()
103
120
  con.close()
121
+
122
+ self._headings = (3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 34, 35, 36, 37, 38, 39, 40, 41, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 92, 98, 100, 101, 102, 103, 108, 109, 110, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 138, 139, 140, 141, 142, 143, 144, 145)
104
123
  self._reported = []
105
124
  self._encoded = {}
106
125
  self._linked = {}
@@ -151,7 +170,6 @@ class Scriptures():
151
170
  """, flags=regex.VERBOSE | regex.IGNORECASE)
152
171
 
153
172
  self._tagged = regex.compile(r'({{.*?}})')
154
-
155
173
  self._cv_cv = regex.compile(r'(\d+):(\d+)-(\d+):(\d+)')
156
174
  self._v_cv = regex.compile(r'(\d+)-(\d+):(\d+)')
157
175
  self._cv_v = regex.compile(r'(\d+):(\d+)-(\d+)')
@@ -162,7 +180,6 @@ class Scriptures():
162
180
  self._d_d = regex.compile(r'(\d+)-(\d+)(?!:)')
163
181
  self._dd = regex.compile(r'(\d+),(\d+)')
164
182
  self._d = regex.compile(r'(\d+)')
165
-
166
183
  self._chunk = regex.compile(r'([^,;\p{Z}]+.*)')
167
184
  self._sep = regex.compile(r'(?<!;)\s')
168
185
 
@@ -181,7 +198,7 @@ class Scriptures():
181
198
  return None, 0
182
199
  else:
183
200
  bk_num = self._src_book_names[bk_name]
184
- return self._ranges.loc[(self._ranges.Book == bk_num) & (self._ranges.Chapter.isnull()), ['Book', 'Last']].values[0]
201
+ return bk_num, self._ranges.get((bk_num, 0))
185
202
 
186
203
  reduced = regex.sub(r'\p{Z}', '', scripture)
187
204
  reduced = regex.sub(r'\p{Pd}', '-', reduced)
@@ -258,7 +275,7 @@ class Scriptures():
258
275
 
259
276
  def _code_scripture(self, scripture, bk_num, rest, last):
260
277
 
261
- def reform_series(txt): # rewrite comma-separated consecutive sequences as (1, 2, 3) as ranges (1-3)
278
+ def reform_series(txt): # rewrite comma-separated consecutive sequences (1, 2, 3) as ranges (1-3)
262
279
  for result in self._d_dd.finditer(txt, overlapped=True):
263
280
  end = result.group(3)
264
281
  mid = result.group(2)
@@ -287,13 +304,13 @@ class Scriptures():
287
304
  v = int(vs)
288
305
  if not (0 < b <= 66): # book out of range
289
306
  return None
290
- if not (0 < c <= self._ranges.loc[(self._ranges.Book == b) & (self._ranges.Chapter.isnull()), ['Last']].values[0]): # chapter out of range
307
+ if not (0 < c <= self._ranges.get((b, 0), 0)): # chapter out of range
291
308
  return None
292
309
  if b == 19 and c in self._headings:
293
310
  first = 0
294
311
  else:
295
312
  first = 1
296
- if not (first <= v <= self._ranges.loc[(self._ranges.Book == b) & (self._ranges.Chapter == c), ['Last']].values[0]): # verse out of range
313
+ if not (first <= v <= self._ranges.get((b, c), 0)): # verse out of range
297
314
  return None
298
315
  return True
299
316
 
@@ -376,7 +393,7 @@ class Scriptures():
376
393
  if not validate(book, c, v):
377
394
  return None, 0
378
395
  ch2 = c.zfill(3)
379
- v2 = str(self._ranges.loc[(self._ranges.Book == book) & (self._ranges.Chapter == int(ch2)), ['Last']].values[0][0]).zfill(3)
396
+ v2 = str(self._ranges.get((book, int(ch2)))).zfill(3)
380
397
  return (b+ch1+v1, b+ch2+v2), None
381
398
  else:
382
399
  c = 1
@@ -405,7 +422,7 @@ class Scriptures():
405
422
  v1 = '000'
406
423
  else:
407
424
  v1 = '001'
408
- v2 = str(self._ranges.loc[(self._ranges.Book == book) & (self._ranges.Chapter == int(ch1)), ['Last']].values[0][0]).zfill(3)
425
+ v2 = str(self._ranges.get((book, int(ch1)))).zfill(3)
409
426
  return (b+ch1+v1, b+ch1+v2), None
410
427
  else:
411
428
  c = 1
@@ -420,7 +437,7 @@ class Scriptures():
420
437
 
421
438
  lst = []
422
439
  if rest == '': # whole book
423
- v = self._ranges.loc[(self._ranges.Book == bk_num) & (self._ranges.Chapter == last), ['Last']].values[0][0]
440
+ v = self._ranges.get((bk_num, last))
424
441
  if last == 1:
425
442
  rest = f'1-{v}'
426
443
  else:
@@ -465,11 +482,11 @@ class Scriptures():
465
482
  return None, '', 0, False, ''
466
483
  if not ((0 < sb <= 66) & (sb == eb)): # book out of range
467
484
  return None, '', 0, False, ''
468
- lc = self._ranges.loc[(self._ranges.Book == sb) & (self._ranges.Chapter.isnull()), ['Last']].values[0][0]
485
+ lc = self._ranges.get((sb, 0), 0)
469
486
  if not (0 < sc <= ec <= lc): # chapter(s) out of range
470
487
  return None, '', 0, False, ''
471
- se = self._ranges.loc[(self._ranges.Book == sb) & (self._ranges.Chapter == sc), ['Last']].values[0][0]
472
- le = self._ranges.loc[(self._ranges.Book == sb) & (self._ranges.Chapter == ec), ['Last']].values[0][0]
488
+ se = self._ranges.get((sb, sc), 0)
489
+ le = self._ranges.get((sb, ec), 0)
473
490
  minev = 1
474
491
  minsv = 1
475
492
  if sb == 19 and (sc in self._headings):
@@ -607,22 +624,22 @@ class Scriptures():
607
624
 
608
625
  def serial_chapter_number(self, bcv):
609
626
  try:
610
- return int(self._chapters.loc[(self._chapters['Book'] == int(bcv[0:2])) & (self._chapters['Chapter'] == int(bcv[2:5]))].values[0][0])
627
+ return self._chapters[(int(bcv[0:2]), int(bcv[2:5]))]
611
628
  except:
612
629
  self._error_report(bcv, 'OUT OF RANGE')
613
630
  return None
614
631
 
615
632
  def serial_verse_number(self, bcv):
616
633
  try:
617
- return int(self._verses.loc[(self._verses['Book'] == int(bcv[0:2])) & (self._verses['Chapter'] == int(bcv[2:5])) & (self._verses['Verse'] == int(bcv[5:]))].values[0][0]) + 1
634
+ return self._verses[(int(bcv[0:2]), int(bcv[2:5]), int(bcv[5:]))] + 1
618
635
  except:
619
636
  self._error_report(bcv, 'OUT OF RANGE')
620
637
  return None
621
638
 
622
639
  def code_chapter(self, chapter):
623
640
  try:
624
- book, chapter = self._chapters[self._chapters['ChapterId'] == int(chapter)].values[0][1:]
625
- last = self._ranges.loc[(self._ranges.Book == book) & (self._ranges.Chapter == chapter), ['Last']].values[0][0]
641
+ book, chapter = self._chapters_id[int(chapter)]
642
+ last = self._ranges.get((book, chapter))
626
643
  bc = str(book).zfill(2) + str(chapter).zfill(3)
627
644
  if book == 19 and chapter in self._headings: # some chapters start at verse 0
628
645
  v = '000'
@@ -636,9 +653,9 @@ class Scriptures():
636
653
  def code_verse(self, verse):
637
654
  bcv = ''
638
655
  try:
639
- for i in self._verses[self._verses['VerseId'] == int(verse)-1].values[0][1:]:
640
- bcv += str(i).zfill(3)
641
- return f"('{bcv[1:]}', '{bcv[1:]}')"
656
+ bk, ch, vs = self._verses_id[int(verse)-1]
657
+ bcv = f"{bk:02d}{ch:03d}{vs:03d}"
658
+ return f"('{bcv}', '{bcv}')"
642
659
  except:
643
660
  self._error_report(verse, 'OUT OF RANGE')
644
661
  return None
@@ -17,7 +17,7 @@
17
17
  [19, "Psalm, Psal, Pss, Psa"],
18
18
  [20, "Pro"],
19
19
  [21, "Ecc, Eccles, Qoh"],
20
- [22, "Cant, Son, Sng"],
20
+ [22, "Cant, Son, Sng, SoS"],
21
21
  [26, "Ezek"],
22
22
  [30, "Amo"],
23
23
  [31, "Oba"],
index 2500a66..e597681 100644
Binary file
File without changes
File without changes