daily_psalm / app.py
neuralworm's picture
simple calculation for Sura
5d23d59
raw
history blame
19.8 kB
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 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(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 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)
# --- 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=False)
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"):
gr.Markdown("# Daily Quran Sura 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=False)
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")
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]
)
if __name__ == "__main__":
app.launch(share=False)