Spaces:
Sleeping
Sleeping
| 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 | |