daily_psalm / app.py
neuralworm's picture
enabled 1/137 mod by default
b83218c
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
# Import quran and bible inside functions to avoid "referenced before assignment" error
from utils import date_to_words, custom_normalize, translate_date_to_words
from gematria import calculate_gematria, strip_diacritics
from urllib.parse import quote_plus
from gradio_calendar import Calendar
from deep_translator import GoogleTranslator
# --- 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
# --- Available languages for dropdowns ---
# Using display names as keys and language codes as values
LANGUAGES = {
'English': 'en',
'Arabic': 'ar',
'German': 'de',
'Spanish': 'es',
'French': 'fr',
'Hebrew': 'iw', # Note: Google Translator uses 'iw' for Hebrew
'Hindi': 'hi',
'Italian': 'it',
'Japanese': 'ja',
'Korean': 'ko',
'Dutch': 'nl',
'Portuguese': 'pt',
'Russian': 'ru',
'Chinese (Simplified)': 'zh-CN',
'Chinese (Traditional)': 'zh-TW',
}
# --- 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("Exiting get_first_els_result_genesis, returning: None")
return None
def get_first_els_result_matthew(gematria_sum, tlang="en"):
"""Gets the first ELS result from Matthew (book 40) using cached processing."""
logger.debug(f"Entering get_first_els_result_matthew with gematria_sum: {gematria_sum}, tlang: {tlang}")
import bible as bible_module
results = cached_process_json_files(
bible_module.process_json_files,
40, 40, # Only Matthew (book 40 to 40)
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_value
)
if results:
logger.debug(f"First ELS result from Matthew: {results[0]}")
logger.debug(f"Exiting get_first_els_result_matthew, returning: {results[0]}")
return results[0]
else:
logger.debug("No ELS results found in Matthew.")
logger.debug("Exiting get_first_els_result_matthew, returning: None")
return None
# This function has been removed as we now use get_first_els_result_genesis for Bible searches
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 find_shortest_sura_match(gematria_sum):
"""Finds the shortest Sura entry in abjad.db."""
logger.debug(f"Entering find_shortest_sura_match with gematria_sum: {gematria_sum}")
import quran as quran_module
return quran_module.find_shortest_sura_match(gematria_sum, db_file='abjad.db')
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'<iframe src="{url}" width="800" height="600"></iframe>'
logger.debug(f"Generated iframe: {iframe}")
logger.debug(f"Exiting create_biblegateway_iframe, returning: {iframe}")
return iframe
def create_quran_iframe(sura_name, chapter, verse):
"""Creates an iframe HTML string for Quran.com."""
logger.debug(f"Entering create_quran_iframe with sura_name: {sura_name}, chapter: {chapter}, verse: {verse}")
import quran as quran_module
return quran_module.create_quran_display_iframe(sura_name, chapter, verse)
def create_bible_iframe(book_title, book_id, chapter=None, verse=None):
"""Creates an iframe HTML string for BibleGateway."""
logger.debug(f"Entering create_bible_iframe with book_title: {book_title}, book_id: {book_id}, chapter: {chapter}, verse: {verse}")
import bible as bible_module
return bible_module.create_bible_display_iframe(book_title, book_id, chapter, verse)
# --- Gradio Interface ---
with gr.Blocks() as app:
with gr.Tabs() as tabs:
with gr.TabItem("Daily Psalm"):
gr.Markdown("# Daily Psalm Finder")
with gr.Row():
psalm_name_input = gr.Textbox(label="Name (optional)")
psalm_date_input = Calendar(label="Date", type="date")
with gr.Row():
psalm_adjusted_sum_check = gr.Checkbox(label="Journal Sum + (Journal Sum / 137)", value=True)
psalm_language = gr.Dropdown(choices=list(LANGUAGES.keys()), value="English", label="Language",
interactive=True)
with gr.Row():
psalm_run_button = gr.Button("Find Psalm")
with gr.Row():
psalm_iframe_output = gr.HTML(label="Psalm Display")
with gr.TabItem("Daily Sura (Aya)"):
gr.Markdown("# Daily Quran Sura (Aya) Finder")
with gr.Row():
sura_name_input = gr.Textbox(label="Name (optional)")
sura_date_input = Calendar(label="Date", type="date")
with gr.Row():
sura_adjusted_sum_check = gr.Checkbox(label="Journal Sum + (Journal Sum / 137)", value=True)
sura_language = gr.Dropdown(choices=list(LANGUAGES.keys()), value="English", label="Language",
interactive=True)
with gr.Row():
sura_run_button = gr.Button("Find Sura")
with gr.Row():
sura_iframe_output = gr.HTML(label="Sura Display")
with gr.TabItem("Daily Revelation"):
gr.Markdown("# Daily Revelation Chapter Finder")
with gr.Row():
bible_name_input = gr.Textbox(label="Name (optional)")
bible_date_input = Calendar(label="Date", type="date")
with gr.Row():
bible_adjusted_sum_check = gr.Checkbox(label="Journal Sum + (Journal Sum / 137)", value=True)
bible_language = gr.Dropdown(choices=list(LANGUAGES.keys()), value="English", label="Language",
interactive=True)
with gr.Row():
bible_run_button = gr.Button("Find Bible Verse")
with gr.Row():
bible_iframe_output = gr.HTML(label="Bible Display")
def find_psalm(name, date, adjusted_sum_check, language_display):
# Convert display language name to language code
language_code = LANGUAGES.get(language_display, 'en')
logger.debug(f"Entering find_psalm with name: '{name}', date: '{date}', adjusted_sum: {adjusted_sum_check}, language: '{language_display}' (code: {language_code})")
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}")
# Get date in words and translate if needed
if language_display == "English":
date_words = date_to_words(date_str)
else:
try:
date_words = translate_date_to_words(date, lang=language_code)
except Exception as e:
logger.warning(f"Failed to translate date to {language_display}, falling back to English: {e}")
date_words = date_to_words(date_str)
logger.debug(f"Date words in {language_display}: {date_words}")
# If name is provided, translate it too
if name and language_display != "English":
try:
translator = GoogleTranslator(source='auto', target=language_code)
name = translator.translate(name)
logger.debug(f"Translated name in {language_display}: {name}")
except Exception as e:
logger.warning(f"Failed to translate name: {e}")
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
def find_sura(name, date, adjusted_sum_check, language_display):
# Convert display language name to language code
language_code = LANGUAGES.get(language_display, 'en')
logger.debug(f"Entering find_sura with name: '{name}', date: '{date}', adjusted_sum: {adjusted_sum_check}, language: '{language_display}' (code: {language_code})")
# Import quran module inside the function to avoid "referenced before assignment" error
import quran as quran_module
# Check if database is initialized for Quran
with sqlite3.connect(DATABASE_FILE) as conn:
cursor = conn.cursor()
cursor.execute("SELECT COUNT(*) FROM results WHERE book != 'Psalms'")
count = cursor.fetchone()[0]
if count == 0:
logger.warning("Quran database not initialized. Initializing database...")
# Initialize with a small number of entries for quick demonstration
quran_module.initialize_quran_database(DATABASE_FILE, max_phrase_length=1)
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}")
# Get date in words and translate if needed
if language_display == "English":
date_words = date_to_words(date_str)
else:
try:
date_words = translate_date_to_words(date, lang=language_code)
except Exception as e:
logger.warning(f"Failed to translate date to {language_display}, falling back to English: {e}")
date_words = date_to_words(date_str)
logger.debug(f"Date words in {language_display}: {date_words}")
# If name is provided, translate it too
if name and language_display != "English":
try:
translator = GoogleTranslator(source='auto', target=language_code)
name = translator.translate(name)
logger.debug(f"Translated name in {language_display}: {name}")
except Exception as e:
logger.warning(f"Failed to translate name: {e}")
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}")
# Use ELS search on the Quran text, not Genesis
els_result = quran_module.get_first_els_result_quran(initial_gematria_sum, tlang='en')
if not els_result:
logger.debug("No ELS result. Returning error message.")
return "No ELS result found in the Quran text."
els_gematria_sum = calculate_gematria(els_result['result_text'])
logger.debug(f"ELS Gematria sum from Quran: {els_gematria_sum}")
# Log detailed information about where the ELS was found
if 'start_sura' in els_result and 'end_sura' in els_result:
logger.info(f"ELS found spanning from Sura {els_result['start_sura']} ({els_result['start_sura_name']}) "
f"verse {els_result['start_verse']} to "
f"Sura {els_result['end_sura']} ({els_result['end_sura_name']}) "
f"verse {els_result['end_verse']}")
logger.info(f"ELS text: '{els_result['result_text']}' with step {els_result['step']}")
# Use the ELS result to find a matching Sura
try:
sura_match = find_shortest_sura_match(els_gematria_sum)
logger.debug(f"Sura Match result: {sura_match}")
if not sura_match:
logger.debug("No Sura match. Returning error message.")
return "No matching Sura found for the ELS result. Try with a different name or date."
except Exception as e:
logger.error(f"Error finding sura match: {str(e)}")
return f"Error finding matching Sura: {str(e)}. Make sure the database is properly initialized."
logger.debug("Creating Quran iframe...")
# Create HTML with ELS result information
els_info_html = ""
if 'start_sura' in els_result and 'end_sura' in els_result:
els_info_html = f"""
<div style="margin-bottom: 15px; padding: 10px; background-color: #f5f5f5; border-radius: 5px;">
<h3>ELS Search Result in Quran</h3>
<p><strong>Found text:</strong> {els_result['result_text']}</p>
<p><strong>Gematria value:</strong> {els_gematria_sum}</p>
<p><strong>Location:</strong> From Sura {els_result['start_sura']} ({els_result['start_sura_name']})
verse {els_result['start_verse']} to
Sura {els_result['end_sura']} ({els_result['end_sura_name']})
verse {els_result['end_verse']}</p>
<p><strong>Step size:</strong> {els_result['step']}</p>
</div>
"""
iframe_html = els_info_html + quran_module.create_quran_display_iframe(sura_match["book"], sura_match["chapter"], sura_match["verse"])
logger.debug(f"Returning iframe HTML with ELS info")
return iframe_html
# Connect the buttons to the functions
psalm_run_button.click(
find_psalm,
inputs=[psalm_name_input, psalm_date_input, psalm_adjusted_sum_check, psalm_language],
outputs=[psalm_iframe_output]
)
sura_run_button.click(
find_sura,
inputs=[sura_name_input, sura_date_input, sura_adjusted_sum_check, sura_language],
outputs=[sura_iframe_output]
)
def find_bible(name, date, adjusted_sum_check, language_display):
# Convert display language name to language code
language_code = LANGUAGES.get(language_display, 'en')
logger.debug(f"Entering find_bible with name: '{name}', date: '{date}', adjusted_sum: {adjusted_sum_check}, language: '{language_display}' (code: {language_code})")
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}")
# Get date in words and translate if needed
if language_display == "English":
date_words = date_to_words(date_str)
else:
try:
date_words = translate_date_to_words(date, lang=language_code)
except Exception as e:
logger.warning(f"Failed to translate date to {language_display}, falling back to English: {e}")
date_words = date_to_words(date_str)
logger.debug(f"Date words in {language_display}: {date_words}")
# If name is provided, translate it too
if name and language_display != "English":
try:
translator = GoogleTranslator(source='auto', target=language_code)
name = translator.translate(name)
logger.debug(f"Translated name in {language_display}: {name}")
except Exception as e:
logger.warning(f"Failed to translate name: {e}")
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}")
# Check if Bible database exists
import bible as bible_module
bible_db = "bible.db"
if not os.path.exists(bible_db):
logger.info(f"Bible database {bible_db} not found. Initializing...")
bible_module.initialize_bible_database(bible_db, max_phrase_length=1)
# Use ELS search on Matthew (book 40) first
els_result = get_first_els_result_matthew(initial_gematria_sum, tlang='en')
if not els_result:
logger.debug("No ELS result. Returning error message.")
return "No ELS result found in Matthew."
els_gematria_sum = calculate_gematria(els_result['result_text'])
logger.debug(f"ELS Gematria sum from Matthew: {els_gematria_sum}")
# Use the ELS result to find a matching verse in John
try:
bible_match = bible_module.find_shortest_bible_match(els_gematria_sum, db_file=bible_db)
logger.debug(f"Bible Match result: {bible_match}")
if not bible_match:
logger.debug("No Bible match in John. Returning error message.")
return "No matching verse found in John for the ELS result. Try with a different name or date."
book_title = bible_match["book"]
chapter = bible_match["chapter"]
verse = bible_match["verse"]
except Exception as e:
logger.error(f"Error finding Bible match: {str(e)}")
return f"Error finding matching Bible verse: {str(e)}. Make sure the database is properly initialized."
logger.debug("Creating Bible iframe...")
# Create HTML with ELS result information
els_info_html = f"""
<div style="margin-bottom: 15px; padding: 10px; background-color: #f5f5f5; border-radius: 5px;">
<h3>Bible Search Result</h3>
<p><strong>ELS text from Matthew:</strong> {els_result['result_text']}</p>
<p><strong>Gematria value:</strong> {els_gematria_sum}</p>
<p><strong>Matching verse in John:</strong> {bible_match["words"]}</p>
<p><strong>Location:</strong> {bible_match["book"]} {bible_match["chapter"]}:{bible_match["verse"]}</p>
</div>
"""
# Create iframe with the specific chapter and verse
bible_iframe = els_info_html + create_bible_iframe(book_title, 43, chapter, verse)
if bible_iframe:
logger.debug(f"Created Bible iframe: {bible_iframe[:100]}...")
else:
logger.debug("Failed to create Bible iframe.")
return "Failed to create Bible display."
logger.debug(f"Returning Bible iframe for {book_title} {chapter}:{verse}")
return bible_iframe
bible_run_button.click(
find_bible,
inputs=[bible_name_input, bible_date_input, bible_adjusted_sum_check, bible_language],
outputs=[bible_iframe_output]
)
if __name__ == "__main__":
app.launch(share=False)