File size: 13,489 Bytes
e8510d3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
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