import json import os import logging import sqlite3 import re from typing import Dict, List, Any from gematria import calculate_gematria, strip_diacritics from deep_translator import GoogleTranslator logger = logging.getLogger(__name__) def process_bible_files(start: int, end: int) -> Dict[int, Dict[str, Any]]: """ Processes Bible JSON files and returns a dictionary mapping book IDs to their data. Args: start: The starting book ID (inclusive). end: The ending book ID (inclusive). Returns: A dictionary where keys are book IDs and values are dictionaries containing 'title' and 'text' fields. """ base_path = "texts/bible" results = {} for i in range(start, end + 1): file_name = f"{base_path}/{i}.json" try: with open(file_name, 'r', encoding='utf-8') as file: data = json.load(file) if data: # Extract title and verses title = data.get("title", "No title") text = data.get("text", []) # Store book ID as key and book data as value results[i] = {"title": title, "text": text} except FileNotFoundError: logger.warning(f"File {file_name} not found.") except json.JSONDecodeError as e: logger.warning(f"File {file_name} could not be read as JSON: {e}") except Exception as e: logger.warning(f"Error processing {file_name}: {e}") return results def process_json_files(start, end, step, rounds="1", length=0, tlang="en", strip_spaces=True, strip_in_braces=True, strip_diacritics_value=True, translate=False): """ Processes Bible JSON files and performs ELS (Equidistant Letter Sequence) search. Parameters: - start (int): Start number of the Bible book. - end (int): End number of the Bible book. - step (int): Step size for character selection. - rounds (str): Comma-separated list of round numbers (can include negative values). - length (int): Maximum length of the result text. - tlang (str): Target language for translation. - strip_spaces (bool): Whether to remove spaces from the text. - strip_in_braces (bool): Whether to remove text within braces. - strip_diacritics_value (bool): Whether to remove diacritics from the text. - translate (bool): Whether to translate the result text. Returns: - list: A list of dictionaries containing processed data or error messages. """ logger.debug(f"Processing Bible files {start}-{end} with step {step}, rounds {rounds}") results = [] try: bible_data = process_bible_files(start, end) if not bible_data: return [{"error": f"No Bible data found for books {start}-{end}"}] rounds_list = [int(r.strip()) for r in rounds.split(",")] for book_id, book_info in bible_data.items(): book_title = book_info.get("title", "Unknown") chapters = book_info.get("text", []) if not chapters: results.append({"error": f"No text found for book {book_title} (ID: {book_id})"}) continue # Flatten the text flattened_text = "" for chapter_idx, chapter in enumerate(chapters, 1): for verse_idx, verse in enumerate(chapter, 1): if verse: flattened_text += verse + " " # Clean the text based on parameters processed_text = flattened_text.lower() if strip_in_braces: # Remove content within brackets or parentheses processed_text = re.sub(r'\[.*?\]|\(.*?\)', '', processed_text) if strip_spaces: # Remove spaces and other whitespace processed_text = re.sub(r'\s+', '', processed_text) if strip_diacritics_value: # Remove diacritics from letters processed_text = strip_diacritics(processed_text) # To match daily_psalm, process round 1 first, then other rounds sorted_rounds = sorted(rounds_list, key=lambda r: 0 if r == 1 else abs(r)) # Perform ELS search for each round for round_num in sorted_rounds: if round_num == 0: continue direction = 1 if round_num > 0 else -1 abs_round = abs(round_num) # Calculate step size actual_step = step * abs_round # Apply ELS algorithm els_result = None # Determine start index based on direction start_idx = 0 if direction > 0 else len(processed_text) - 1 # Loop through possible starting positions for i in range(abs_round): # Skip if starting position is out of bounds if start_idx + i >= len(processed_text) or start_idx - i < 0: continue # Get starting position for this round pos = start_idx + i if direction > 0 else start_idx - i # Extract letters at intervals result_text = "" while 0 <= pos < len(processed_text) and (length == 0 or len(result_text) < length): result_text += processed_text[pos] pos += actual_step * direction # Only record non-empty results if result_text: # Prepare result object result = { "result_text": result_text, "step": actual_step * direction, "start_pos": i, "book_id": book_id, "book_title": book_title, "round": round_num, "gematria": calculate_gematria(result_text) } # Translate result if requested if translate and result_text and tlang != "en": try: translator = GoogleTranslator(source='en', target=tlang) result["translated_text"] = translator.translate(result_text) except Exception as e: logger.warning(f"Translation error: {e}") result["translated_text"] = result_text # Add result to results list results.append(result) # For efficiency, once we find a valid result for this round, break the loop # This ensures we get one result per round but don't waste time on additional calculations break except Exception as e: logger.error(f"Error processing Bible files: {e}", exc_info=True) results.append({"error": f"Error processing Bible files: {str(e)}"}) return results def create_bible_display_iframe(book_title, book_id, chapter=None, verse=None): """ Creates an iframe HTML string for BibleGateway. Args: book_title (str): The title of the book. book_id (int): The ID of the book. chapter (int, optional): The chapter number. verse (int, optional): The verse number. Returns: str: An iframe HTML string for displaying the specified Bible passage. """ from urllib.parse import quote_plus # Format the passage reference if chapter and verse: reference = f"{book_title}+{chapter}:{verse}" elif chapter: reference = f"{book_title}+{chapter}" else: reference = book_title encoded_reference = quote_plus(reference) url = f"https://www.biblegateway.com/passage/?search={encoded_reference}&version=CJB" iframe = f'' return iframe def find_bible_verse_by_gematria(gematria_sum, db_file="bible.db"): """ Finds a Bible verse with a matching gematria sum. Args: gematria_sum (int): The gematria sum to match. db_file (str): Path to the database file. Returns: dict or None: Dictionary with verse details or None if no match found. """ try: with sqlite3.connect(db_file) as conn: cursor = conn.cursor() cursor.execute(''' SELECT book, chapter, verse, words FROM verses WHERE gematria_sum = ? ORDER BY LENGTH(words) ASC LIMIT 1 ''', (gematria_sum,)) result = cursor.fetchone() if result: book, chapter, verse, words = result return { "book": book, "chapter": chapter, "verse": verse, "words": words } return None except sqlite3.Error as e: logger.error(f"Database error: {e}") return None