import logging import os import sqlite3 import re from datetime import datetime import hashlib import json import math import gradio as gr import torah # Assuming you have your torah module from utils import date_to_words, custom_normalize from gematria import calculate_gematria, strip_diacritics from urllib.parse import quote_plus from gradio_calendar import Calendar # --- Setup Logging --- logger = logging.getLogger(__name__) logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') # --- Constants and Global Variables --- DATABASE_FILE = 'gematria.db' ELS_CACHE_DB = 'els_cache.db' DATABASE_TIMEOUT = 60 ADJUSTMENT_CONSTANT = 137.035999177 # Use the more precise constant # --- Helper Functions --- def get_query_hash(func, args, kwargs): key = (func.__name__, args, kwargs) logger.debug(f"Generating query hash for key: {key}") hash_value = hashlib.sha256(json.dumps(key).encode()).hexdigest() logger.debug(f"Generated hash: {hash_value}") return hash_value def create_els_cache_table(): if not os.path.exists(ELS_CACHE_DB): logger.debug(f"Creating ELS cache table in {ELS_CACHE_DB}") with sqlite3.connect(ELS_CACHE_DB) as conn: conn.execute(''' CREATE TABLE IF NOT EXISTS els_cache ( query_hash TEXT PRIMARY KEY, function_name TEXT, args TEXT, kwargs TEXT, results TEXT ) ''') logger.debug("ELS cache table created.") else: logger.debug(f"ELS cache table already exists in {ELS_CACHE_DB}") def cached_process_json_files(func, *args, **kwargs): logger.debug(f"Entering cached_process_json_files with func: {func}, args: {args}, kwargs: {kwargs}") params = { "function": f"{func.__module__}.{func.__name__}" } arg_names = func.__code__.co_varnames[:func.__code__.co_argcount] for name, value in zip(arg_names, args): params[name] = value for name, value in kwargs.items(): params[name] = value params_json = json.dumps(params) logger.debug(f"Parameters as JSON: {params_json}") query_hash = get_query_hash(func, params_json, "") create_els_cache_table() try: with sqlite3.connect(ELS_CACHE_DB, timeout=DATABASE_TIMEOUT) as conn: cursor = conn.cursor() cursor.execute( "SELECT results FROM els_cache WHERE query_hash = ?", (query_hash,)) result = cursor.fetchone() if result: logger.info(f"Cache hit for query: {query_hash}") logger.debug(f"Cached result: {result[0]}") return json.loads(result[0]) except sqlite3.Error as e: logger.error(f"Database error checking cache: {e}") logger.info(f"Cache miss for query: {query_hash}") results = func(*args, **kwargs) logger.debug(f"Results from function call: {results}") try: with sqlite3.connect(ELS_CACHE_DB, timeout=DATABASE_TIMEOUT) as conn: cursor = conn.cursor() cursor.execute( "INSERT INTO els_cache (query_hash, function_name, args, kwargs, results) VALUES (?, ?, ?, ?, ?)", (query_hash, params["function"], params_json, json.dumps({}), json.dumps(results))) conn.commit() logger.debug("Cached results in database.") except sqlite3.Error as e: logger.error(f"Database error caching results: {e}") logger.debug(f"Exiting cached_process_json_files, returning: {results}") return results def calculate_gematria_sum(text, date_words): logger.debug(f"Entering calculate_gematria_sum with text: '{text}', date_words: '{date_words}'") if text or date_words: combined_input = f"{text} {date_words}" logger.info(f"Calculating Gematria sum for input: {combined_input}") numbers = re.findall(r'\d+', combined_input) logger.debug(f"Extracted numbers: {numbers}") text_without_numbers = re.sub(r'\d+', '', combined_input) logger.debug(f"Text without numbers: {text_without_numbers}") number_sum = sum(int(number) for number in numbers) logger.debug(f"Sum of numbers: {number_sum}") text_gematria = calculate_gematria(strip_diacritics(text_without_numbers)) logger.debug(f"Gematria of text: {text_gematria}") total_sum = text_gematria + number_sum logger.debug(f"Calculated Gematria sum: {total_sum}") logger.debug(f"Exiting calculate_gematria_sum, returning: {total_sum}") return total_sum else: logger.debug("No input text or date words provided. Returning None.") return None def get_first_els_result_genesis(gematria_sum, tlang="en"): """Gets the first ELS result from Genesis (book 1) using cached processing.""" logger.debug(f"Entering get_first_els_result_genesis with gematria_sum: {gematria_sum}, tlang: {tlang}") results = cached_process_json_files( torah.process_json_files, 1, 1, # Only Genesis (book 1 to 1) gematria_sum, # Use gematria sum as step "1,-1", # Default rounds_combination 0, # length tlang, True, # strip_spaces True, # strip_in_braces True # strip_diacritics_chk ) if results: logger.debug(f"First ELS result from Genesis: {results[0]}") logger.debug(f"Exiting get_first_els_result_genesis, returning: {results[0]}") return results[0] else: logger.debug("No ELS results found in Genesis.") logger.debug(f"Exiting get_first_els_result_genesis, returning: None") return None def find_shortest_psalm_match(gematria_sum): """Finds the shortest Psalm entry in gematria.db.""" logger.debug(f"Entering find_shortest_psalm_match with gematria_sum: {gematria_sum}") with sqlite3.connect(DATABASE_FILE) as conn: cursor = conn.cursor() cursor.execute(''' SELECT words, chapter, verse FROM results WHERE gematria_sum = ? AND book = 'Psalms' ORDER BY LENGTH(words) ASC LIMIT 1 ''', (gematria_sum,)) result = cursor.fetchone() if result: logger.debug(f"Shortest Psalm match found: {result}") logger.debug(f"Exiting find_shortest_psalm_match, returning: {result}") return {"words": result[0], "chapter": result[1], "verse": result[2], "phrase_length": None} logger.debug("No matching Psalm found.") logger.debug(f"Exiting find_shortest_psalm_match, returning: None") return None def create_biblegateway_iframe(book_name, chapter, verse): """Creates an iframe HTML string for Bible Gateway.""" logger.debug(f"Entering create_biblegateway_iframe with book_name: {book_name}, chapter: {chapter}, verse: {verse}") encoded_book_name = quote_plus(book_name) url = f"https://www.biblegateway.com/passage/?search={encoded_book_name}+{chapter}&version=CJB" iframe = f'' logger.debug(f"Generated iframe: {iframe}") logger.debug(f"Exiting create_biblegateway_iframe, returning: {iframe}") return iframe # --- Gradio Interface --- with gr.Blocks() as app: gr.Markdown("# Daily Psalm Finder") with gr.Row(): name_input = gr.Textbox(label="Name (optional)") date_input = Calendar(label="Date", type="date") with gr.Row(): adjusted_sum_check = gr.Checkbox(label="Journal Sum + (Journal Sum / 137)", value=False) with gr.Row(): run_button = gr.Button("Find Psalm") with gr.Row(): iframe_output = gr.HTML(label="Psalm Display") def main_function(name, date, adjusted_sum_check): logger.debug(f"Entering main_function with name: '{name}', date: '{date}', adjusted_sum: {adjusted_sum_check}") if isinstance(date, str): date = datetime.strptime(date, "%Y-%m-%d") logger.debug(f"Converted date string to datetime: {date}") date_str = date.strftime("%Y-%m-%d") logger.debug(f"Formatted date string: {date_str}") date_words = date_to_words(date_str) logger.debug(f"Date words: {date_words}") initial_gematria_sum = calculate_gematria_sum(name, date_words) logger.debug(f"Initial Gematria Sum: {initial_gematria_sum}") if initial_gematria_sum is None: logger.debug("Initial Gematria sum is None. Returning error message.") return "Could not calculate initial Gematria sum." # Adjust gematria sum if checkbox is checked if adjusted_sum_check: adjusted_sum = initial_gematria_sum + (initial_gematria_sum / ADJUSTMENT_CONSTANT) initial_gematria_sum = math.ceil(adjusted_sum) # Use the adjusted sum logger.debug(f"Adjusted Gematria Sum (using formula): {initial_gematria_sum}") els_result = get_first_els_result_genesis(initial_gematria_sum, tlang='en') # Use adjusted sum here, and set target language to en if not els_result: logger.debug("No ELS result. Returning error message.") return "No ELS result found in Genesis." els_gematria_sum = calculate_gematria(els_result['result_text']) logger.debug(f"ELS Gematria sum: {els_gematria_sum}") psalm_match = find_shortest_psalm_match(els_gematria_sum) logger.debug(f"Psalm Match result: {psalm_match}") if not psalm_match: logger.debug("No Psalm match. Returning error message.") return "No matching Psalm found for the ELS result." logger.debug("Creating Bible Gateway iframe...") iframe_html = create_biblegateway_iframe("Psalms", psalm_match["chapter"], psalm_match["verse"]) logger.debug(f"Returning iframe HTML: {iframe_html}") return iframe_html run_button.click( main_function, inputs=[name_input, date_input, adjusted_sum_check], outputs=[iframe_output] ) if __name__ == "__main__": app.launch(share=False)