neuralworm's picture
fix: Convert embedded repo to regular directory
e8510d3
Raw
History Blame
13.5 kB
import json
import os
import logging
import sqlite3
import re
from gematria import calculate_gematria, strip_diacritics
from deep_translator import GoogleTranslator
logger = logging.getLogger(__name__)
def get_sura_count() -> int:
"""Returns the total number of suras in the Quran."""
base_path = "texts/quran"
# Count the number of JSON files in the quran directory
try:
files = [f for f in os.listdir(base_path) if f.endswith('.json')]
return len(files)
except FileNotFoundError:
logger.error(f"Directory {base_path} not found.")
return 114 # Default number of suras in the Quran
def get_quran_text_and_map():
"""
Loads all Quran text and creates a character-to-location map.
This is a helper to avoid repeatedly loading and processing the text.
"""
# This could be cached in memory for performance if the app is long-running
all_text = ""
char_map = [] # List of (sura_id, sura_name, verse_id) for each character
sura_count = get_sura_count()
for sura_num in range(1, sura_count + 1):
sura_str = f"{sura_num:03d}"
file_path = f"texts/quran/{sura_str}.json"
if not os.path.exists(file_path):
continue
try:
with open(file_path, 'r', encoding='utf-8') as f:
sura_data = json.load(f)
except (json.JSONDecodeError, FileNotFoundError):
continue
sura_name = sura_data.get('name', f"Sura {sura_num}")
if all_text:
all_text += " "
verses = sura_data.get('text', []) or sura_data.get('verse', {})
if isinstance(verses, dict):
verses = [verses[key] for key in sorted(verses.keys())]
for verse_idx, verse in enumerate(verses, 1):
if verse:
# Add original verse text to all_text
all_text += verse + " "
# For the char_map, use the cleaned version
cleaned_verse = strip_diacritics(verse).replace(" ", "")
for _ in cleaned_verse:
char_map.append((sura_num, sura_name, verse_idx))
# Clean up the text for ELS: strip diacritics, remove any special characters, etc.
clean_text = strip_diacritics(all_text)
# The reference implementation is more aggressive in cleaning.
clean_text = ''.join(c for c in clean_text if c.isalpha() or c.isspace())
text_no_spaces = clean_text.replace(" ", "")
return text_no_spaces, char_map
def get_first_els_result_quran(gematria_sum: int, tlang: str = "en", cached_process_func=None) -> dict:
"""
Gets the first ELS result from the Quran using the gematria sum as the step.
This version is aligned with the logic from the reference 'daily_psalm' project.
"""
import hashlib
logger.debug(f"Entering get_first_els_result_quran (reference logic) with gematria_sum: {gematria_sum}")
# Caching logic remains similar
cache_key = f"quran_els_ref_{gematria_sum}_{tlang}"
cache_file = "els_cache.db"
try:
with sqlite3.connect(cache_file) as conn:
cursor = conn.cursor()
cursor.execute(
"SELECT results FROM els_cache WHERE query_hash = ?",
(hashlib.sha256(cache_key.encode()).hexdigest(),))
result = cursor.fetchone()
if result:
logger.info(f"Cache hit for Quran ELS query: {cache_key}")
return json.loads(result[0])
except sqlite3.Error as e:
logger.error(f"Database error checking cache: {e}")
logger.info(f"Cache miss for Quran ELS query: {cache_key}, performing search")
text_no_spaces, char_map = get_quran_text_and_map()
logger.debug(f"Processed text length (for ELS): {len(text_no_spaces)}")
logger.debug(f"First 100 chars of processed text: {text_no_spaces[:100]}")
result = None
# Start positions to try (first 100 positions for better coverage)
for start_pos in range(min(100, len(text_no_spaces))):
extracted = ""
positions = []
pos = start_pos
# Extract until the end of the text is reached
while pos < len(text_no_spaces):
extracted += text_no_spaces[pos]
positions.append(pos)
pos += gematria_sum
if len(extracted) >= 3: # At least 3 characters
first_pos = positions[0]
last_pos = positions[-1]
if first_pos < len(char_map) and last_pos < len(char_map):
first_loc = char_map[first_pos]
last_loc = char_map[last_pos]
result = {
"result_text": extracted,
"source": "Quran",
"start_position": start_pos,
"step": gematria_sum,
"start_sura": first_loc[0],
"start_sura_name": first_loc[1],
"start_verse": first_loc[2],
"end_sura": last_loc[0],
"end_sura_name": last_loc[1],
"end_verse": last_loc[2],
"positions": positions,
"gematria": calculate_gematria(extracted) # Add gematria here
}
logger.debug(f"Found ELS result: {result}")
break # Found a result, stop searching
else:
logger.warning(f"Character position mapping inconsistency: {first_pos}, {last_pos} vs {len(char_map)}")
continue
if result:
try:
with sqlite3.connect(cache_file) as conn:
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS els_cache (
query_hash TEXT PRIMARY KEY, function_name TEXT,
args TEXT, kwargs TEXT, results TEXT
)
''')
cursor.execute(
"INSERT OR REPLACE INTO els_cache (query_hash, function_name, args, kwargs, results) VALUES (?, ?, ?, ?, ?)",
(hashlib.sha256(cache_key.encode()).hexdigest(), "get_first_els_result_quran_ref",
json.dumps([gematria_sum]), json.dumps({"tlang": tlang}), json.dumps(result)))
conn.commit()
logger.debug("Cached Quran ELS results in database.")
except sqlite3.Error as e:
logger.error(f"Database error caching results: {e}")
logger.debug(f"Exiting get_first_els_result_quran, returning: {result}")
return result
def find_shortest_sura_match(gematria_sum, db_file='abjad.db'):
"""
Finds the shortest Sura (Aya) entry in the database with matching gematria sum.
Parameters:
- gematria_sum (int): The gematria sum to match.
- db_file (str): Path to the database file.
Returns:
- dict or None: Dictionary with match details or None if no match found.
"""
logger.debug(f"Finding shortest sura match for gematria sum: {gematria_sum}")
try:
with sqlite3.connect(db_file) as conn:
cursor = conn.cursor()
cursor.execute('''
SELECT words, book, chapter, verse
FROM results
WHERE gematria_sum = ?
ORDER BY LENGTH(words) ASC
LIMIT 1
''', (gematria_sum,))
result = cursor.fetchone()
if result:
logger.debug(f"Shortest sura match found: {result}")
return {
"words": result[0],
"book": result[1],
"chapter": result[2],
"verse": result[3]
}
logger.debug("No matching sura found.")
return None
except sqlite3.Error as e:
logger.error(f"Database error: {e}")
return None
def create_quran_display_iframe(sura, verse=None):
"""
Creates an iframe HTML string for Quran.com.
Parameters:
- sura (str or int): The sura name or number.
- verse (str or int, optional): The verse number.
Returns:
- str: An iframe HTML string for displaying the specified Quran passage.
"""
# Format URL based on parameters
if verse:
url = f"https://quran.com/{sura}/{verse}"
else:
url = f"https://quran.com/{sura}"
# Create iframe HTML
iframe = f'<iframe src="{url}" width="800" height="600"></iframe>'
return iframe
def initialize_quran_database(db_file='abjad.db', max_phrase_length=3):
"""
Initialize the database with Quran verses and their gematria sums.
Parameters:
- db_file (str): Path to the database file.
- max_phrase_length (int): Maximum number of words to include in phrases.
Returns:
- bool: True if initialization was successful, False otherwise.
"""
logger.info(f"Initializing Quran database {db_file} with max phrase length {max_phrase_length}")
try:
# Create database and tables if they don't exist
with sqlite3.connect(db_file) as conn:
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS results (
id INTEGER PRIMARY KEY AUTOINCREMENT,
book TEXT,
chapter INTEGER,
verse INTEGER,
words TEXT,
gematria_sum INTEGER
)
''')
# Check if there are already Quran entries in the database
cursor.execute("SELECT COUNT(*) FROM results WHERE book != 'Psalms'")
count = cursor.fetchone()[0]
if count > 0:
logger.info(f"Database already contains {count} Quran entries. Skipping initialization.")
return True
# Process Quran files
logger.info("Processing Quran files...")
quran_base_path = "texts/quran"
# Get list of all Quran JSON files
quran_files = []
try:
for filename in os.listdir(quran_base_path):
if filename.endswith('.json'):
quran_files.append(os.path.join(quran_base_path, filename))
except FileNotFoundError:
logger.error(f"Quran texts directory not found: {quran_base_path}")
return False
# Process each sura file
for file_path in sorted(quran_files):
try:
with open(file_path, 'r', encoding='utf-8') as f:
sura_data = json.load(f)
# Extract sura details
sura_name = sura_data.get('name', os.path.basename(file_path))
sura_number = int(os.path.basename(file_path).split('.')[0])
verses = sura_data.get('text', [])
logger.debug(f"Processing Sura {sura_number}: {sura_name} with {len(verses)} verses")
# Process each verse
for verse_idx, verse_text in enumerate(verses, 1):
if not verse_text:
continue
# Split verse into words
words = verse_text.split()
# Generate phrases of different lengths
for length in range(1, min(max_phrase_length + 1, len(words) + 1)):
for i in range(len(words) - length + 1):
phrase = ' '.join(words[i:i+length])
# Calculate gematria value
gematria_sum = calculate_gematria(strip_diacritics(phrase.lower()))
# Skip if gematria sum is 0
if gematria_sum == 0:
continue
# Insert into database
cursor.execute('''
INSERT INTO results (book, chapter, verse, words, gematria_sum)
VALUES (?, ?, ?, ?, ?)
''', (sura_name, sura_number, verse_idx, phrase, gematria_sum))
except Exception as e:
logger.error(f"Error processing {file_path}: {str(e)}")
# Commit changes
conn.commit()
# Count total entries
cursor.execute("SELECT COUNT(*) FROM results WHERE book != 'Psalms'")
count = cursor.fetchone()[0]
logger.info(f"Successfully initialized Quran database with {count} entries")
return True
except sqlite3.Error as e:
logger.error(f"Database error during initialization: {str(e)}")
return False
except Exception as e:
logger.error(f"Error initializing database: {str(e)}")
return False