neuralworm commited on
Commit
409d42b
โ€ข
1 Parent(s): 396d07b

word position, layout

Browse files
Files changed (4) hide show
  1. .gitignore +1 -1
  2. app.py +267 -297
  3. database-structure.txt +7 -0
  4. gematria.db +2 -2
.gitignore CHANGED
@@ -1,2 +1,2 @@
1
  __pycache__/
2
- gematria.db.bak
 
1
  __pycache__/
2
+ .idea
app.py CHANGED
@@ -10,332 +10,302 @@ from util import process_json_files
10
  from gematria import calculate_gematria
11
  from deep_translator import GoogleTranslator, exceptions
12
  from urllib.parse import quote_plus
13
- from tqdm import tqdm # Import tqdm for progress bars
14
 
15
  # Constants
16
  DATABASE_FILE = 'gematria.db'
17
- MAX_PHRASE_LENGTH_LIMIT = 20 # Populate database for phrases up to 5 words
18
- BATCH_SIZE = 1000 # Insert phrases into database in batches
19
 
20
  # Set up logging
21
- logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(filename)s - %(lineno)d - %(message)s')
22
 
23
  # Global variables
24
  conn: sqlite3.Connection = None
25
  translator: GoogleTranslator = None
26
  book_names: Dict[int, str] = {}
27
- gematria_cache: Dict[Tuple[int, int], List[Tuple[str, str, int, int]]] = {}
28
  translation_cache: Dict[str, str] = {}
 
 
29
 
30
  def initialize_database() -> None:
31
- """Initializes the SQLite database."""
32
- global conn
33
- conn = sqlite3.connect(DATABASE_FILE, isolation_level=None) # Autocommit for faster insertion
34
- cursor = conn.cursor()
35
-
36
- # Create tables if they don't exist
37
- cursor.execute('''
38
- CREATE TABLE IF NOT EXISTS results (
39
- gematria_sum INTEGER,
40
- words TEXT,
41
- translation TEXT,
42
- book TEXT,
43
- chapter INTEGER,
44
- verse INTEGER,
45
- PRIMARY KEY (gematria_sum, words, book, chapter, verse)
46
- )
47
- ''')
48
- cursor.execute('''
49
- CREATE TABLE IF NOT EXISTS processed_books (
50
- book TEXT PRIMARY KEY,
51
- max_phrase_length INTEGER
52
- )
53
- ''')
54
- cursor.execute('''
55
- CREATE TABLE IF NOT EXISTS translations (
56
- hebrew_phrase TEXT PRIMARY KEY,
57
- english_translation TEXT
58
- )
59
- ''')
 
 
 
 
60
 
61
  def initialize_translator() -> None:
62
- """Initializes the Google Translator."""
63
- global translator
64
- translator = GoogleTranslator(source='iw', target='en')
65
- logging.info("Translator initialized.")
66
 
67
- def populate_database(start_book: int, end_book: int, max_phrase_length: int = 1) -> None:
68
- """Populates the database with phrases from the Tanach and their Gematria values."""
69
- global conn, book_names
70
- logging.info(f"Populating database with books from {start_book} to {end_book}...")
71
- cursor = conn.cursor()
72
 
73
- for book_id in tqdm(range(start_book, end_book + 1), desc="Processing Books"):
74
- book_data = process_json_files(book_id, book_id) # Get data for the single book
 
 
 
75
 
76
- # process_json_files returns a dictionary with book_id as key,
77
- # so access the book data directly
78
  if book_id in book_data:
79
- book_data = book_data[book_id]
80
- if 'title' not in book_data or not isinstance(book_data['title'], str):
81
- logging.warning(f"Skipping book {book_id} due to missing or invalid 'title' field.")
82
- continue
83
-
84
- title = book_data['title']
85
- book_names[book_id] = title
86
-
87
- # Check if the book is already processed for this max_phrase_length
88
- cursor.execute('''SELECT max_phrase_length FROM processed_books WHERE book = ?''', (title,))
89
- result = cursor.fetchone()
90
- if result and result[0] >= max_phrase_length:
91
- logging.info(f"Skipping book {title}: Already processed with max_phrase_length {result[0]}")
92
- continue
93
-
94
- logging.info(f"Processing book {title} with max_phrase_length {max_phrase_length}")
95
-
96
- if 'text' not in book_data or not isinstance(book_data['text'], list):
97
- logging.warning(f"Skipping book {book_id} due to missing or invalid 'text' field.")
98
- continue
99
-
100
- chapters = book_data['text']
101
- # Faster iteration with enumerate and list comprehension
102
- for chapter_id, chapter in enumerate(chapters):
103
- for verse_id, verse in enumerate(chapter):
104
- verse_text = flatten_text(verse)
105
- # Remove text in square brackets and non-Hebrew characters
106
- verse_text = re.sub(r'\[.*?\]', '', verse_text)
107
- verse_text = re.sub(r"[^\u05D0-\u05EA ]+", "", verse_text)
108
- verse_text = re.sub(r" +", " ", verse_text)
109
- words = verse_text.split()
110
-
111
- # Use a generator to avoid building large lists in memory
112
- for length in range(1, max_phrase_length + 1):
113
- for start in range(len(words) - length + 1):
114
- phrase_candidate = " ".join(words[start:start + length])
115
- gematria_sum = calculate_gematria(phrase_candidate.replace(" ", ""))
116
- yield gematria_sum, phrase_candidate, title, chapter_id + 1, verse_id + 1
117
-
118
- # Mark the book as processed with the current max_phrase_length
119
- cursor.execute('''
120
- INSERT OR REPLACE INTO processed_books (book, max_phrase_length)
121
- VALUES (?, ?)
122
- ''', (title, max_phrase_length))
123
-
124
- def insert_phrases_to_db(phrases: List[Tuple[int, str, str, int, int]]) -> None:
125
- """Inserts a list of phrases into the database efficiently."""
126
- global conn
127
- cursor = conn.cursor()
128
-
129
- # Use executemany to insert multiple rows at once
130
- cursor.executemany('''
131
- INSERT OR IGNORE INTO results (gematria_sum, words, book, chapter, verse)
132
- VALUES (?, ?, ?, ?, ?)
133
- ''', phrases)
134
-
135
- # Commit the changes outside the loop for better performance
136
- conn.commit()
 
 
 
 
 
 
 
 
 
 
 
 
 
137
 
138
  def get_translation(phrase: str) -> str:
139
- """Retrieves or generates the English translation of a Hebrew phrase."""
140
- global translator, conn, translation_cache
141
- if phrase in translation_cache:
142
- return translation_cache[phrase]
143
- else:
144
- cursor = conn.cursor()
145
- cursor.execute('''
146
- SELECT english_translation FROM translations
147
- WHERE hebrew_phrase = ?
148
- ''', (phrase,))
149
- result = cursor.fetchone()
150
- if result and result[0]:
151
- translation = result[0]
152
- return translation
153
  else:
154
- translation = translate_and_store(phrase)
155
- cursor.execute('''
156
- INSERT OR IGNORE INTO translations (hebrew_phrase, english_translation)
157
- VALUES (?, ?)
158
- ''', (phrase, translation))
159
- return translation
160
 
161
  def translate_and_store(phrase: str) -> str:
162
- """Translates a Hebrew phrase to English using Google Translate and handles potential errors."""
163
- global translator
164
- max_retries = 3
165
- retries = 0
166
-
167
- while retries < max_retries:
168
- try:
169
- translation = translator.translate(phrase)
170
- logging.debug(f"Translated phrase: {translation}")
171
- return translation
172
- except (exceptions.TranslationNotFound, exceptions.NotValidPayload,
173
- exceptions.ServerException, exceptions.RequestError, requests.exceptions.ConnectionError) as e:
174
- retries += 1
175
- logging.warning(f"Error translating phrase '{phrase}': {e}. Retrying... ({retries}/{max_retries})")
176
-
177
- logging.error(f"Failed to translate phrase '{phrase}' after {max_retries} retries.")
178
- return "[Translation Error]"
179
-
180
- def search_gematria_in_db(gematria_sum: int, max_words: int) -> List[Tuple[str, str, int, int]]:
181
- """Searches the database for phrases with a given Gematria value and word count.
182
- Returns phrases with word count <= max_words."""
183
- global conn
184
- cursor = conn.cursor()
185
- logging.debug(f"Searching for phrases with Gematria: {gematria_sum} and max words: {max_words}")
186
- cursor.execute('''
187
- SELECT words, book, chapter, verse FROM results WHERE gematria_sum = ?
188
- ''', (gematria_sum,)) # Retrieve all matching phrases first
189
- results = cursor.fetchall()
190
- filtered_results = []
191
- logging.debug(f"Found {len(results)} matching phrases before filtering.")
192
- for words, book, chapter, verse in results:
193
- # Filter by word count (including phrases with fewer words)
194
- word_count = len(words.split()) # Correctly split and count words
195
- logging.debug(f"Word count for '{words}': {word_count}")
196
- if word_count <= max_words: # Include phrases with word count <= max_words
197
- filtered_results.append((words, book, chapter, verse))
198
- logging.debug(f"Found {len(filtered_results)} matching phrases after filtering.")
199
- return filtered_results
200
 
201
  def gematria_search_interface(phrase: str, max_words: int, show_translation: bool) -> str:
202
- """The main function for the Gradio interface."""
203
- if not phrase.strip():
204
- return "Please enter a phrase."
205
-
206
- global conn, book_names, gematria_cache
207
- conn = sqlite3.connect(DATABASE_FILE)
208
- cursor = conn.cursor()
209
-
210
- # Extract numbers from the input text
211
- numbers = re.findall(r'\d+', phrase)
212
- # Calculate Gematria for the remaining text (non-numbers)
213
- text_without_numbers = re.sub(r'\d+', '', phrase)
214
- phrase_gematria = calculate_gematria(text_without_numbers.replace(" ", ""))
215
-
216
- # Add sum of numbers to Gematria
217
- phrase_gematria += sum(int(number) for number in numbers)
218
-
219
- logging.info(f"Searching for phrases with Gematria: {phrase_gematria}")
220
-
221
- # Debugging output
222
- logging.debug(f"Phrase Gematria: {phrase_gematria}")
223
- logging.debug(f"Max Words: {max_words}")
224
-
225
- # Check if Gematria is in cache for the specific max_words value
226
- if (phrase_gematria, max_words) in gematria_cache:
227
- matching_phrases = gematria_cache[(phrase_gematria, max_words)]
228
- logging.debug(f"Retrieved matching phrases from cache for max_words: {max_words}.")
229
- else:
230
- # Search in the database
231
- matching_phrases = search_gematria_in_db(phrase_gematria, max_words)
232
- # Cache the results with the max_words value
233
- gematria_cache[(phrase_gematria, max_words)] = matching_phrases
234
- logging.debug(f"Retrieved matching phrases from database for max_words: {max_words}.")
235
-
236
- if not matching_phrases:
237
- return "No matching phrases found."
238
-
239
- # Sort results by book, chapter, and verse
240
- sorted_phrases = sorted(matching_phrases, key=lambda x: (int(list(book_names.keys())[list(book_names.values()).index(x[1])]), x[2], x[3]))
241
- logging.debug(f"Sorted matching phrases: {sorted_phrases}")
242
-
243
- # Group results by book
244
- results_by_book = defaultdict(list)
245
- for words, book, chapter, verse in sorted_phrases:
246
- results_by_book[book].append((words, chapter, verse))
247
- logging.debug(f"Grouped results by book: {results_by_book}")
248
-
249
- # Format results for display
250
- results = []
251
- results.append("<div class='results-container'>")
252
- for book, phrases in results_by_book.items():
253
- results.append(f"<h4>Book: {book}</h4>") # Directly display book name
254
- for words, chapter, verse in phrases:
255
- translation = get_translation(words) if show_translation else ""
256
- link = f"https://www.biblegateway.com/passage/?search={quote_plus(book)}+{chapter}%3A{verse}&version=CJB"
257
- results.append(f"""
258
- <div class='result-item'>
259
- <p>Chapter: {chapter}, Verse: {verse}</p>
260
- <p class='hebrew-phrase'>Hebrew Phrase: {words}</p>
261
- <p>Translation: {translation}</p>
262
- <a href='{link}' target='_blank' class='bible-link'>[See on Bible Gateway]</a>
263
- </div>
264
- """)
265
- results.append("</div>") # Close results-container div
266
-
267
- conn.close()
268
-
269
- # Add CSS styling
270
- style = """
271
- <style>
272
- .results-container {
273
- display: grid;
274
- grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
275
- gap: 20px;
276
- }
277
-
278
- .result-item {
279
- border: 1px solid #ccc;
280
- padding: 15px;
281
- border-radius: 5px;
282
- box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1);
283
- }
284
-
285
- .hebrew-phrase {
286
- font-family: 'SBL Hebrew', 'Ezra SIL', serif;
287
- direction: rtl;
288
- }
289
-
290
- .bible-link {
291
- display: block;
292
- margin-top: 10px;
293
- color: #007bff;
294
- text-decoration: none;
295
- }
296
- </style>
297
- """
298
-
299
- return style + "\n".join(results)
300
 
301
  def flatten_text(text: List) -> str:
302
- """Helper function to flatten nested lists into a single list."""
303
- if isinstance(text, list):
304
- return " ".join(flatten_text(item) if isinstance(item, list) else item for item in text)
305
- return text
 
306
 
307
  def run_app() -> None:
308
- """Initializes and launches the Gradio app."""
309
- initialize_database()
310
- initialize_translator()
311
-
312
- # Pre-populate the database
313
- logging.info("Starting database population...")
314
- phrases_to_insert = [] # Collect phrases before inserting in bulk
315
- for max_phrase_length in range(1, MAX_PHRASE_LENGTH_LIMIT + 1): # Populate for phrases up to MAX_PHRASE_LENGTH_LIMIT words
316
- for gematria_sum, phrase, book, chapter, verse in tqdm(populate_database(1, 39, max_phrase_length=max_phrase_length), desc=f"Populating Database (Max Length: {max_phrase_length})"): # Books 1 to 39
317
- phrases_to_insert.append((gematria_sum, phrase, book, chapter, verse))
318
- if len(phrases_to_insert) >= BATCH_SIZE: # Insert in batches of BATCH_SIZE for efficiency
319
- insert_phrases_to_db(phrases_to_insert)
320
- phrases_to_insert = []
321
- if phrases_to_insert: # Insert remaining phrases
322
- insert_phrases_to_db(phrases_to_insert)
323
- logging.info("Database population complete.")
324
-
325
- iface = gr.Interface(
326
- fn=gematria_search_interface,
327
- inputs=[
328
- gr.Textbox(label="Enter word(s) or numbers (e.g., 'abc', '888' or 'abc 111 777')"),
329
- gr.Number(label="Max Word Count in Result Phrases", value=1, minimum=1, maximum=MAX_PHRASE_LENGTH_LIMIT),
330
- gr.Checkbox(label="Show Translation", value=True)
331
- ],
332
- outputs=gr.HTML(label="Results"),
333
- title="Gematria Search in Tanach",
334
- description="Search for phrases and/or numbers in the Tanach that have the same Gematria value.",
335
- live=False,
336
- allow_flagging="never"
337
- )
338
- iface.launch()
339
 
340
  if __name__ == "__main__":
341
- run_app()
 
10
  from gematria import calculate_gematria
11
  from deep_translator import GoogleTranslator, exceptions
12
  from urllib.parse import quote_plus
13
+ from tqdm import tqdm
14
 
15
  # Constants
16
  DATABASE_FILE = 'gematria.db'
17
+ MAX_PHRASE_LENGTH_LIMIT = 20
18
+ BATCH_SIZE = 10000
19
 
20
  # Set up logging
21
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
22
 
23
  # Global variables
24
  conn: sqlite3.Connection = None
25
  translator: GoogleTranslator = None
26
  book_names: Dict[int, str] = {}
27
+ gematria_cache: Dict[Tuple[int, int], List[Tuple[str, str, int, int, int, str]]] = {}
28
  translation_cache: Dict[str, str] = {}
29
+ total_word_count: int = 0 # Global counter for word position
30
+
31
 
32
  def initialize_database() -> None:
33
+ """Initializes the SQLite database."""
34
+ global conn
35
+ conn = sqlite3.connect(DATABASE_FILE)
36
+ cursor = conn.cursor()
37
+
38
+ cursor.execute('''
39
+ CREATE TABLE IF NOT EXISTS results (
40
+ gematria_sum INTEGER,
41
+ words TEXT,
42
+ translation TEXT,
43
+ book TEXT,
44
+ chapter INTEGER,
45
+ verse INTEGER,
46
+ phrase_length INTEGER,
47
+ word_position TEXT,
48
+ PRIMARY KEY (gematria_sum, words, book, chapter, verse, word_position) -- Primary key constraint
49
+ )
50
+ ''')
51
+
52
+ cursor.execute('''
53
+ CREATE INDEX IF NOT EXISTS idx_results_gematria
54
+ ON results (gematria_sum)
55
+ ''')
56
+
57
+ cursor.execute('''
58
+ CREATE TABLE IF NOT EXISTS processed_books (
59
+ book TEXT PRIMARY KEY,
60
+ max_phrase_length INTEGER
61
+ )
62
+ ''')
63
+
64
+ conn.commit()
65
+
66
 
67
  def initialize_translator() -> None:
68
+ """Initializes the Google Translator."""
69
+ global translator
70
+ translator = GoogleTranslator(source='iw', target='en')
71
+ logging.info("Translator initialized.")
72
 
 
 
 
 
 
73
 
74
+ def process_book(book_id: int, max_phrase_length: int, cursor):
75
+ """Processes a single book and returns phrases to insert."""
76
+ global book_names, total_word_count
77
+ book_data = process_json_files(book_id, book_id)
78
+ phrases_to_insert = []
79
 
 
 
80
  if book_id in book_data:
81
+ book_data = book_data[book_id]
82
+ if 'title' not in book_data or not isinstance(book_data['title'], str):
83
+ logging.warning(f"Skipping book {book_id} due to missing 'title' field.")
84
+ return phrases_to_insert
85
+
86
+ title = book_data['title']
87
+ book_names[book_id] = title
88
+
89
+ # Check if this book has already been processed for this phrase length
90
+ cursor.execute('''SELECT max_phrase_length FROM processed_books WHERE book = ?''', (title,))
91
+ result = cursor.fetchone()
92
+ if result and result[0] >= max_phrase_length:
93
+ logging.info(f"Skipping book {title}: Already processed with max_phrase_length {result[0]}")
94
+ return phrases_to_insert
95
+
96
+ if 'text' not in book_data or not isinstance(book_data['text'], list):
97
+ logging.warning(f"Skipping book {book_id} due to missing 'text' field.")
98
+ return phrases_to_insert
99
+
100
+ chapters = book_data['text']
101
+ for chapter_id, chapter in enumerate(chapters):
102
+ for verse_id, verse in enumerate(chapter):
103
+ verse_text = flatten_text(verse)
104
+ verse_text = re.sub(r'\[.*?\]', '', verse_text)
105
+ verse_text = re.sub(r"[^\u05D0-\u05EA ]+", "", verse_text)
106
+ verse_text = re.sub(r" +", " ", verse_text)
107
+ words = verse_text.split()
108
+
109
+ for length in range(1, max_phrase_length + 1):
110
+ for start in range(len(words) - length + 1):
111
+ phrase_candidate = " ".join(words[start:start + length])
112
+ gematria_sum = calculate_gematria(phrase_candidate.replace(" ", ""))
113
+
114
+ word_position_range = f"{total_word_count + start + 1}-{total_word_count + start + length}"
115
+
116
+ phrases_to_insert.append(
117
+ (gematria_sum, phrase_candidate, None, title, chapter_id + 1, verse_id + 1, length,
118
+ word_position_range))
119
+
120
+ total_word_count += len(words)
121
+
122
+ return phrases_to_insert
123
+
124
+
125
+ def populate_database(start_book: int, end_book: int, max_phrase_length: int = 1) -> None:
126
+ """Populates the database with phrases from the Tanach."""
127
+ global conn, book_names, total_word_count
128
+ logging.info(f"Populating database with books from {start_book} to {end_book}...")
129
+
130
+ with sqlite3.connect(DATABASE_FILE) as conn:
131
+ cursor = conn.cursor()
132
+
133
+ for book_id in tqdm(range(start_book, end_book + 1), desc="Processing Books"):
134
+ phrases_to_insert = process_book(book_id, max_phrase_length, cursor)
135
+
136
+ if phrases_to_insert:
137
+ cursor.executemany('''
138
+ INSERT OR IGNORE INTO results (gematria_sum, words, translation, book, chapter, verse, phrase_length, word_position)
139
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
140
+ ''', phrases_to_insert)
141
+
142
+ # Update processed_books after processing each book
143
+ cursor.execute('''
144
+ INSERT OR REPLACE INTO processed_books (book, max_phrase_length)
145
+ VALUES (?, ?)
146
+ ''', (book_names[book_id], max_phrase_length))
147
+
148
+ conn.commit()
149
+
150
+ total_word_count = 0 # Reset for the next set of phrase lengths
151
+
152
 
153
  def get_translation(phrase: str) -> str:
154
+ """Retrieves or generates the English translation of a Hebrew phrase."""
155
+ global translator, translation_cache
156
+ if phrase in translation_cache:
157
+ return translation_cache[phrase]
 
 
 
 
 
 
 
 
 
 
158
  else:
159
+ translation = translate_and_store(phrase)
160
+ translation_cache[phrase] = translation
161
+ return translation
162
+
 
 
163
 
164
  def translate_and_store(phrase: str) -> str:
165
+ """Translates a Hebrew phrase to English using Google Translate."""
166
+ global translator
167
+ max_retries = 3
168
+ retries = 0
169
+ while retries < max_retries:
170
+ try:
171
+ translation = translator.translate(phrase)
172
+ return translation
173
+ except (exceptions.TranslationNotFound, exceptions.NotValidPayload,
174
+ exceptions.ServerException, exceptions.RequestError) as e:
175
+ retries += 1
176
+ logging.warning(f"Error translating phrase '{phrase}': {e}. Retrying... ({retries}/{max_retries})")
177
+ logging.error(f"Failed to translate phrase '{phrase}' after {max_retries} retries.")
178
+ return "[Translation Error]"
179
+
180
+
181
+ def search_gematria_in_db(gematria_sum: int, max_words: int) -> List[Tuple[str, str, int, int, int, str]]:
182
+ """Searches the database for phrases with a given Gematria value."""
183
+ global conn
184
+ with sqlite3.connect(DATABASE_FILE) as conn:
185
+ cursor = conn.cursor()
186
+ cursor.execute('''
187
+ SELECT words, book, chapter, verse, phrase_length, word_position
188
+ FROM results
189
+ WHERE gematria_sum = ? AND phrase_length <= ?
190
+ ''', (gematria_sum, max_words))
191
+ results = cursor.fetchall()
192
+ return results
193
+
 
 
 
 
 
 
 
 
 
194
 
195
  def gematria_search_interface(phrase: str, max_words: int, show_translation: bool) -> str:
196
+ """The main function for the Gradio interface."""
197
+ if not phrase.strip():
198
+ return "Please enter a phrase."
199
+
200
+ global conn, book_names, gematria_cache
201
+
202
+ numbers = re.findall(r'\d+', phrase)
203
+ text_without_numbers = re.sub(r'\d+', '', phrase)
204
+ phrase_gematria = calculate_gematria(text_without_numbers.replace(" ", ""))
205
+ phrase_gematria += sum(int(number) for number in numbers)
206
+
207
+ if (phrase_gematria, max_words) in gematria_cache:
208
+ matching_phrases = gematria_cache[(phrase_gematria, max_words)]
209
+ else:
210
+ matching_phrases = search_gematria_in_db(phrase_gematria, max_words)
211
+ gematria_cache[(phrase_gematria, max_words)] = matching_phrases
212
+
213
+ if not matching_phrases:
214
+ return "No matching phrases found."
215
+
216
+ sorted_phrases = sorted(matching_phrases,
217
+ key=lambda x: (int(list(book_names.keys())[list(book_names.values()).index(x[1])]), x[2], x[3]))
218
+ results_by_book = defaultdict(list)
219
+ for words, book, chapter, verse, phrase_length, word_position in sorted_phrases:
220
+ results_by_book[book].append((words, chapter, verse, phrase_length, word_position))
221
+
222
+ results = []
223
+ results.append("<div class='results-container'>")
224
+ for book, phrases in results_by_book.items():
225
+ for words, chapter, verse, phrase_length, word_position in phrases:
226
+ translation = get_translation(words) if show_translation else ""
227
+ link = f"https://www.biblegateway.com/passage/?search={quote_plus(book)}+{chapter}%3A{verse}&version=CJB"
228
+ results.append(f"""
229
+ <div class='result-item'>
230
+ <p><b>Book:</b> {book}</p>
231
+ <p><b>Chapter:</b> {chapter}, <b>Verse:</b> {verse}</p>
232
+ <p class='hebrew-phrase'><b>Hebrew Phrase:</b> {words}</p>
233
+ <p><b>Translation:</b> {translation}</p>
234
+ <p><b>Phrase Length:</b> {phrase_length} words</p>
235
+ <p><b>Phrase Gematria:</b> {phrase_gematria}</p>
236
+ <p><b>Word Position in the Tanach:</b> {word_position}</p>
237
+ <a href='{link}' target='_blank' class='bible-link'>[See on Bible Gateway]</a>
238
+ </div>
239
+ """)
240
+ results.append("</div>")
241
+
242
+ # Style modified to position search on top and results below
243
+ style = """
244
+ <style>
245
+ .results-container {
246
+ display: grid;
247
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
248
+ gap: 20px;
249
+ width: 100%; /* Make results container take full width */
250
+ }
251
+ .result-item {
252
+ border: 1px solid #ccc;
253
+ padding: 15px;
254
+ border-radius: 5px;
255
+ box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1);
256
+ }
257
+ .hebrew-phrase {
258
+ font-family: 'SBL Hebrew', 'Ezra SIL', serif;
259
+ direction: rtl;
260
+ }
261
+ .bible-link {
262
+ display: block;
263
+ margin-top: 10px;
264
+ color: #007bff;
265
+ text-decoration: none;
266
+ }
267
+ </style>
268
+ """
269
+ return style + "\n".join(results)
270
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
271
 
272
  def flatten_text(text: List) -> str:
273
+ """Flattens nested lists into a single list."""
274
+ if isinstance(text, list):
275
+ return " ".join(flatten_text(item) if isinstance(item, list) else item for item in text)
276
+ return text
277
+
278
 
279
  def run_app() -> None:
280
+ """Initializes and launches the Gradio app."""
281
+ global conn
282
+ initialize_database()
283
+ initialize_translator()
284
+
285
+ logging.info("Starting database population...")
286
+ for max_phrase_length in range(1, MAX_PHRASE_LENGTH_LIMIT + 1):
287
+ populate_database(1, 39, max_phrase_length=max_phrase_length)
288
+ logging.info("Database population complete.")
289
+
290
+ with gr.Blocks() as iface: # Use gr.Blocks() for layout control
291
+ with gr.Row(): # Place inputs in a row
292
+ textbox = gr.Textbox(label="Enter word(s) or numbers")
293
+ slider = gr.Slider(label="Max Word Count in Result Phrases", minimum=1, maximum=MAX_PHRASE_LENGTH_LIMIT, step=1,
294
+ value=1)
295
+ checkbox = gr.Checkbox(label="Show Translation", value=True)
296
+ with gr.Row(): # Place buttons in a row
297
+ clear_button = gr.Button("Clear")
298
+ submit_button = gr.Button("Submit", variant="primary")
299
+
300
+ html_output = gr.HTML(label="Results") # Output for the results
301
+
302
+ submit_button.click(fn=gematria_search_interface,
303
+ inputs=[textbox, slider, checkbox],
304
+ outputs=html_output)
305
+ clear_button.click(fn=lambda: "", inputs=None, outputs=html_output) # Clear the output
306
+
307
+ iface.launch()
308
+
 
 
309
 
310
  if __name__ == "__main__":
311
+ run_app()
database-structure.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ Gematria Sum, Words, Translation, Book, Chapter, Verse, Phrase Length, Phrase Position
2
+ 913 ื‘ืจืืฉื™ืช Genesis 1 1 1 1-1
3
+ 1116 ื‘ืจืืฉื™ืช ื‘ืจื Genesis 1 1 2 1-2
4
+ 1762 ื‘ืจืืฉื™ืช ื‘ืจื ืืœื”ื™ื Genesis 1 1 3 1-3
5
+ 2163 ื‘ืจืืฉื™ืช ื‘ืจื ืืœื”ื™ื ืืช Genesis 1 1 4 1-4
6
+ 3118 ื‘ืจืืฉื™ืช ื‘ืจื ืืœื”ื™ื ืืช ื”ืฉืžื™ื Genesis 1 1 5 1-5
7
+ 3525 ื‘ืจืืฉื™ืช ื‘ืจื ืืœื”ื™ื ืืช ื”ืฉืžื™ื ื•ืืช Genesis 1 1 6 1-6
gematria.db CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:ae87f4cf5e8720cf2c2f9367df7753bc501369138b059291128ff0dac5d41ad6
3
- size 334843904
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7f2cbde78ee764e0b28fa7aee9862781c91521d62b99a9a344bdf4cff5237556
3
+ size 417710080