Gematria_Lab / app.py
eaglelandsonce's picture
Update app.py
d9c5784 verified
# gematria_gradio.py
# Hebrew Gematria Lab โ€” Gradio Edition
# Features:
# - Systems: Standard, Gadol (finals 500โ€“900), Ordinal (1โ€“22), Mispar Katan (reduced)
# - Cleans niqqud/cantillation, punctuation, spaces
# - Atbash & Albam transforms
# - Batch processing with optional target matching
# - CSV export
import re
import os
import unicodedata
import tempfile
import pandas as pd
import gradio as gr
# -----------------------------
# Unicode & Cleanup Utilities
# -----------------------------
HEBREW_LETTERS = "ืื‘ื’ื“ื”ื•ื–ื—ื˜ื™ื›ืœืžื ืกืขืคืฆืงืจืฉืชืšืืŸืฃืฅ"
FINAL_FORMS = {"ืš": "ื›", "ื": "ืž", "ืŸ": "ื ", "ืฃ": "ืค", "ืฅ": "ืฆ"}
COMBINING_MARKS_PATTERN = re.compile(r"[\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7]")
NON_HEBREW_PATTERN = re.compile(r"[^ื-ืชืšืืŸืฃืฅ]+")
def strip_niqqud(text: str) -> str:
return COMBINING_MARKS_PATTERN.sub("", text)
def normalize_text(text: str, keep_spaces: bool = False) -> str:
t = unicodedata.normalize("NFC", text or "")
t = strip_niqqud(t)
if not keep_spaces:
t = t.replace(" ", "")
t = NON_HEBREW_PATTERN.sub("", t)
return t
# -----------------------------
# Letter Value Tables
# -----------------------------
STANDARD_VALUES = {
"ื":1, "ื‘":2, "ื’":3, "ื“":4, "ื”":5, "ื•":6, "ื–":7, "ื—":8, "ื˜":9,
"ื™":10, "ื›":20, "ืœ":30, "ืž":40, "ื ":50, "ืก":60, "ืข":70, "ืค":80, "ืฆ":90,
"ืง":100, "ืจ":200, "ืฉ":300, "ืช":400,
"ืš":20, "ื":40, "ืŸ":50, "ืฃ":80, "ืฅ":90,
}
GADOL_VALUES = {
**STANDARD_VALUES,
"ืš":500, "ื":600, "ืŸ":700, "ืฃ":800, "ืฅ":900,
}
# Ordinal mapping
ORDINAL_MAP = {}
base_order = "ืื‘ื’ื“ื”ื•ื–ื—ื˜ื™ื›ืœืžื ืกืขืคืฆืงืจืฉืช"
for i, ch in enumerate(base_order, start=1):
ORDINAL_MAP[ch] = i
for f, b in FINAL_FORMS.items():
ORDINAL_MAP[f] = ORDINAL_MAP[b]
def mispar_katan_value(ch: str) -> int:
v = STANDARD_VALUES.get(ch, 0)
if v == 0:
return 0
return ((v - 1) % 9) + 1
# -----------------------------
# Transforms: Atbash & Albam
# -----------------------------
ATBASH_MAP = {}
pairs = list(zip(base_order, base_order[::-1]))
for a, b in pairs:
ATBASH_MAP[a] = b
ATBASH_MAP[b] = a
for f, b in FINAL_FORMS.items():
ATBASH_MAP[f] = ATBASH_MAP[b]
ALPH = list(base_order)
shift = 11
ALBAM_MAP = {}
for i, ch in enumerate(ALPH):
j = (i + shift) % len(ALPH)
ALBAM_MAP[ch] = ALPH[j]
ALBAM_MAP[ALPH[j]] = ch
for f, b in FINAL_FORMS.items():
ALBAM_MAP[f] = ALBAM_MAP[b]
def atbash(text: str) -> str:
t = normalize_text(text, keep_spaces=True)
return "".join(ATBASH_MAP.get(ch, ch) for ch in t)
def albam(text: str) -> str:
t = normalize_text(text, keep_spaces=True)
return "".join(ALBAM_MAP.get(ch, ch) for ch in t)
# -----------------------------
# Gematria Calculations
# -----------------------------
def digital_root(n: int) -> int:
if n == 0:
return 0
return 1 + (n - 1) % 9
def gematria_total(text: str, system: str = "standard") -> int:
t = normalize_text(text)
if system == "standard":
values = STANDARD_VALUES
return sum(values.get(ch, 0) for ch in t)
elif system == "gadol":
values = GADOL_VALUES
return sum(values.get(ch, 0) for ch in t)
elif system == "ordinal":
values = ORDINAL_MAP
return sum(values.get(ch, 0) for ch in t)
elif system == "katan":
return sum(mispar_katan_value(ch) for ch in t)
else:
raise ValueError("Unknown system")
def cross_system_table(text: str):
systems = ["standard", "gadol", "ordinal", "katan"]
rows = []
for s in systems:
total = gematria_total(text, s)
rows.append({
"System": s.title(),
"Total": total,
"Digital Root": digital_root(total)
})
return pd.DataFrame(rows)
# -----------------------------
# Gradio Callbacks
# -----------------------------
def calc_single(text, system, show_root):
cleaned = normalize_text(text, keep_spaces=True)
total = gematria_total(text, system)
root = digital_root(total) if show_root else None
df = cross_system_table(text)
main_line = f"Cleaned: `{cleaned}` โ€” Total ({system}): **{total}**"
if show_root:
main_line += f" โ€” Digital root: **{root}**"
return main_line, df
def do_batch(list_text, system, show_root, target_str, sort_by_total):
items = [line for line in (list_text or "").splitlines() if line.strip()]
data = []
for item in items:
cleaned = normalize_text(item, keep_spaces=True)
tot = gematria_total(item, system)
row = {"Original": item, "Cleaned": cleaned, f"Total ({system})": tot}
if show_root:
row["Digital Root"] = digital_root(tot)
data.append(row)
if not data:
df = pd.DataFrame(columns=["Original", "Cleaned", f"Total ({system})"] + (["Digital Root"] if show_root else []))
else:
df = pd.DataFrame(data)
if sort_by_total:
df = df.sort_values(by=[f"Total ({system})", "Cleaned"]).reset_index(drop=True)
# Optional highlight info text
highlight_info = ""
target_val = None
try:
target_val = int(target_str) if (target_str or "").strip() else None
except:
target_val = None
if target_val is not None:
matches = (df[f"Total ({system})"] == target_val).sum()
highlight_info = f"Target **{target_val}** โ€” matches: **{matches}**"
# Write CSV temp file for download
tmpdir = tempfile.mkdtemp(prefix="gematria_")
csv_path = os.path.join(tmpdir, "gematria_batch.csv")
df.to_csv(csv_path, index=False, encoding="utf-8-sig")
return df, highlight_info, csv_path
def do_transforms(text):
a = atbash(text or "")
b = albam(text or "")
rows = []
def totals_row(label, txt):
return {
"Form": label,
"Text": normalize_text(txt, keep_spaces=True),
"Standard": gematria_total(txt, "standard"),
"Gadol": gematria_total(txt, "gadol"),
"Ordinal": gematria_total(txt, "ordinal"),
"Katan": gematria_total(txt, "katan"),
}
rows.append(totals_row("Original", text or ""))
rows.append(totals_row("Atbash", a))
rows.append(totals_row("Albam", b))
return a, b, pd.DataFrame(rows)
# -----------------------------
# UI
# -----------------------------
with gr.Blocks(title="Hebrew Gematria Lab (Gradio)") as demo:
gr.Markdown("# Hebrew Gematria Lab โ€” Gradio")
gr.Markdown(
"Explore Hebrew words across **Standard**, **Gadol**, **Ordinal**, and **Mispar Katan** systems. "
"Use **Atbash** and **Albam** transforms, batch-process lists, and export to CSV."
)
with gr.Tabs():
with gr.Tab("Calculator"):
with gr.Row():
text_in = gr.Textbox(label="Enter Hebrew text", value="ื“ืงืจ")
with gr.Row():
system = gr.Dropdown(choices=["standard", "gadol", "ordinal", "katan"], value="standard", label="Gematria system")
show_root = gr.Checkbox(value=True, label="Show digital root")
calc_btn = gr.Button("Compute")
result_line = gr.Markdown()
result_df = gr.Dataframe(headers=["System", "Total", "Digital Root"], interactive=False)
calc_btn.click(
fn=calc_single,
inputs=[text_in, system, show_root],
outputs=[result_line, result_df]
)
with gr.Tab("Batch & Matching"):
batch_tb = gr.Textbox(
label="Paste list (one item per line)",
lines=10,
value="ื“ืงืจ\nื“ืžืช\nืื—ื“\nืื”ื‘ื”\nืžื•ืช\nืžืœืืš\nืžืœืš\nืฉืœื•ื"
)
with gr.Row():
system_b = gr.Dropdown(choices=["standard", "gadol", "ordinal", "katan"], value="standard", label="Gematria system")
show_root_b = gr.Checkbox(value=True, label="Show digital root")
target_val_tb = gr.Textbox(label="Target total (optional)", placeholder="e.g., 304")
sort_chk = gr.Checkbox(value=True, label="Sort by total")
run_batch_btn = gr.Button("Run Batch")
batch_df = gr.Dataframe(interactive=False)
match_info = gr.Markdown()
download_csv = gr.File(label="Download CSV")
run_batch_btn.click(
fn=do_batch,
inputs=[batch_tb, system_b, show_root_b, target_val_tb, sort_chk],
outputs=[batch_df, match_info, download_csv]
)
with gr.Tab("Transforms"):
t_input = gr.Textbox(label="Hebrew text", value="ืฉืœื•ื")
t_btn = gr.Button("Transform")
atbash_out = gr.Textbox(label="Atbash", interactive=False)
albam_out = gr.Textbox(label="Albam", interactive=False)
trans_df = gr.Dataframe(interactive=False)
t_btn.click(
fn=do_transforms,
inputs=[t_input],
outputs=[atbash_out, albam_out, trans_df]
)
with gr.Tab("Reference"):
gr.Markdown(
"""
**Systems**
- **Standard (Mispar Hechrechi):** ืช=400; finals = base.
- **Gadol:** finals extended ืš=500, ื=600, ืŸ=700, ืฃ=800, ืฅ=900.
- **Ordinal:** ื=1 โ€ฆ ืช=22 (finals use base).
- **Mispar Katan:** letters reduced to 1โ€“9, then summed.
**Transforms**
- **Atbash:** mirror pairs ืโ†”ืช, ื‘โ†”ืฉ, ื’โ†”ืจ, โ€ฆ
- **Albam:** shift by 11 (ืโ†”ืœ, ื‘โ†”ืž, ื’โ†”ื , โ€ฆ, ื›โ†”ืช).
**Cleanup**
- Removes niqqud/cantillation & non-Hebrew marks; normalizes to NFC.
"""
)
ex_df = pd.DataFrame(
[
("ื“ืงืจ (pierce)", "Standard", 304),
("ื“ืžืช (damat)", "Standard", 444),
("ืื—ื“ (one)", "Standard", 13),
("ืื”ื‘ื” (love)", "Standard", 13),
],
columns=["Word", "System", "Total"]
)
gr.Dataframe(value=ex_df, interactive=False)
gr.Markdown(
"Tip: Use **Batch & Matching** to hunt for words/phrases that equal a target total (e.g., 304 for ื“ืงืจ)."
)
if __name__ == "__main__":
demo.launch()