eaglelandsonce commited on
Commit
28e98a4
ยท
verified ยท
1 Parent(s): 4680abc

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +297 -0
app.py ADDED
@@ -0,0 +1,297 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ pip install gradio pandas
2
+ python gematria_gradio.py
3
+ python
4
+ Copy
5
+ Edit
6
+ # gematria_gradio.py
7
+ # Hebrew Gematria Lab โ€” Gradio Edition
8
+ # Features:
9
+ # - Systems: Standard, Gadol (finals 500โ€“900), Ordinal (1โ€“22), Mispar Katan (reduced)
10
+ # - Cleans niqqud/cantillation, punctuation, spaces
11
+ # - Atbash & Albam transforms
12
+ # - Batch processing with optional target matching
13
+ # - CSV export
14
+
15
+ import re
16
+ import os
17
+ import unicodedata
18
+ import tempfile
19
+ import pandas as pd
20
+ import gradio as gr
21
+
22
+ # -----------------------------
23
+ # Unicode & Cleanup Utilities
24
+ # -----------------------------
25
+ HEBREW_LETTERS = "ืื‘ื’ื“ื”ื•ื–ื—ื˜ื™ื›ืœืžื ืกืขืคืฆืงืจืฉืชืšืืŸืฃืฅ"
26
+ FINAL_FORMS = {"ืš": "ื›", "ื": "ืž", "ืŸ": "ื ", "ืฃ": "ืค", "ืฅ": "ืฆ"}
27
+
28
+ COMBINING_MARKS_PATTERN = re.compile(r"[\u0591-\u05BD\u05BF\u05C1-\u05C2\u05C4-\u05C5\u05C7]")
29
+ NON_HEBREW_PATTERN = re.compile(r"[^ื-ืชืšืืŸืฃืฅ]+")
30
+
31
+ def strip_niqqud(text: str) -> str:
32
+ return COMBINING_MARKS_PATTERN.sub("", text)
33
+
34
+ def normalize_text(text: str, keep_spaces: bool = False) -> str:
35
+ t = unicodedata.normalize("NFC", text or "")
36
+ t = strip_niqqud(t)
37
+ if not keep_spaces:
38
+ t = t.replace(" ", "")
39
+ t = NON_HEBREW_PATTERN.sub("", t)
40
+ return t
41
+
42
+ # -----------------------------
43
+ # Letter Value Tables
44
+ # -----------------------------
45
+ STANDARD_VALUES = {
46
+ "ื":1, "ื‘":2, "ื’":3, "ื“":4, "ื”":5, "ื•":6, "ื–":7, "ื—":8, "ื˜":9,
47
+ "ื™":10, "ื›":20, "ืœ":30, "ืž":40, "ื ":50, "ืก":60, "ืข":70, "ืค":80, "ืฆ":90,
48
+ "ืง":100, "ืจ":200, "ืฉ":300, "ืช":400,
49
+ "ืš":20, "ื":40, "ืŸ":50, "ืฃ":80, "ืฅ":90,
50
+ }
51
+
52
+ GADOL_VALUES = {
53
+ **STANDARD_VALUES,
54
+ "ืš":500, "ื":600, "ืŸ":700, "ืฃ":800, "ืฅ":900,
55
+ }
56
+
57
+ # Ordinal mapping
58
+ ORDINAL_MAP = {}
59
+ base_order = "ืื‘ื’ื“ื”ื•ื–ื—ื˜ื™ื›ืœืžื ืกืขืคืฆืงืจืฉืช"
60
+ for i, ch in enumerate(base_order, start=1):
61
+ ORDINAL_MAP[ch] = i
62
+ for f, b in FINAL_FORMS.items():
63
+ ORDINAL_MAP[f] = ORDINAL_MAP[b]
64
+
65
+ def mispar_katan_value(ch: str) -> int:
66
+ v = STANDARD_VALUES.get(ch, 0)
67
+ if v == 0:
68
+ return 0
69
+ return ((v - 1) % 9) + 1
70
+
71
+ # -----------------------------
72
+ # Transforms: Atbash & Albam
73
+ # -----------------------------
74
+ ATBASH_MAP = {}
75
+ pairs = list(zip(base_order, base_order[::-1]))
76
+ for a, b in pairs:
77
+ ATBASH_MAP[a] = b
78
+ ATBASH_MAP[b] = a
79
+ for f, b in FINAL_FORMS.items():
80
+ ATBASH_MAP[f] = ATBASH_MAP[b]
81
+
82
+ ALPH = list(base_order)
83
+ shift = 11
84
+ ALBAM_MAP = {}
85
+ for i, ch in enumerate(ALPH):
86
+ j = (i + shift) % len(ALPH)
87
+ ALBAM_MAP[ch] = ALPH[j]
88
+ ALBAM_MAP[ALPH[j]] = ch
89
+ for f, b in FINAL_FORMS.items():
90
+ ALBAM_MAP[f] = ALBAM_MAP[b]
91
+
92
+ def atbash(text: str) -> str:
93
+ t = normalize_text(text, keep_spaces=True)
94
+ return "".join(ATBASH_MAP.get(ch, ch) for ch in t)
95
+
96
+ def albam(text: str) -> str:
97
+ t = normalize_text(text, keep_spaces=True)
98
+ return "".join(ALBAM_MAP.get(ch, ch) for ch in t)
99
+
100
+ # -----------------------------
101
+ # Gematria Calculations
102
+ # -----------------------------
103
+ def digital_root(n: int) -> int:
104
+ if n == 0:
105
+ return 0
106
+ return 1 + (n - 1) % 9
107
+
108
+ def gematria_total(text: str, system: str = "standard") -> int:
109
+ t = normalize_text(text)
110
+ if system == "standard":
111
+ values = STANDARD_VALUES
112
+ return sum(values.get(ch, 0) for ch in t)
113
+ elif system == "gadol":
114
+ values = GADOL_VALUES
115
+ return sum(values.get(ch, 0) for ch in t)
116
+ elif system == "ordinal":
117
+ values = ORDINAL_MAP
118
+ return sum(values.get(ch, 0) for ch in t)
119
+ elif system == "katan":
120
+ return sum(mispar_katan_value(ch) for ch in t)
121
+ else:
122
+ raise ValueError("Unknown system")
123
+
124
+ def cross_system_table(text: str):
125
+ systems = ["standard", "gadol", "ordinal", "katan"]
126
+ rows = []
127
+ for s in systems:
128
+ total = gematria_total(text, s)
129
+ rows.append({
130
+ "System": s.title(),
131
+ "Total": total,
132
+ "Digital Root": digital_root(total)
133
+ })
134
+ return pd.DataFrame(rows)
135
+
136
+ # -----------------------------
137
+ # Gradio Callbacks
138
+ # -----------------------------
139
+ def calc_single(text, system, show_root):
140
+ cleaned = normalize_text(text, keep_spaces=True)
141
+ total = gematria_total(text, system)
142
+ root = digital_root(total) if show_root else None
143
+ df = cross_system_table(text)
144
+ main_line = f"Cleaned: `{cleaned}` โ€” Total ({system}): **{total}**"
145
+ if show_root:
146
+ main_line += f" โ€” Digital root: **{root}**"
147
+ return main_line, df
148
+
149
+ def do_batch(list_text, system, show_root, target_str, sort_by_total):
150
+ items = [line for line in (list_text or "").splitlines() if line.strip()]
151
+ data = []
152
+ for item in items:
153
+ cleaned = normalize_text(item, keep_spaces=True)
154
+ tot = gematria_total(item, system)
155
+ row = {"Original": item, "Cleaned": cleaned, f"Total ({system})": tot}
156
+ if show_root:
157
+ row["Digital Root"] = digital_root(tot)
158
+ data.append(row)
159
+ if not data:
160
+ df = pd.DataFrame(columns=["Original", "Cleaned", f"Total ({system})"] + (["Digital Root"] if show_root else []))
161
+ else:
162
+ df = pd.DataFrame(data)
163
+ if sort_by_total:
164
+ df = df.sort_values(by=[f"Total ({system})", "Cleaned"]).reset_index(drop=True)
165
+
166
+ # Optional highlight info text
167
+ highlight_info = ""
168
+ target_val = None
169
+ try:
170
+ target_val = int(target_str) if (target_str or "").strip() else None
171
+ except:
172
+ target_val = None
173
+ if target_val is not None:
174
+ matches = (df[f"Total ({system})"] == target_val).sum()
175
+ highlight_info = f"Target **{target_val}** โ€” matches: **{matches}**"
176
+
177
+ # Write CSV temp file for download
178
+ tmpdir = tempfile.mkdtemp(prefix="gematria_")
179
+ csv_path = os.path.join(tmpdir, "gematria_batch.csv")
180
+ df.to_csv(csv_path, index=False, encoding="utf-8-sig")
181
+
182
+ return df, highlight_info, csv_path
183
+
184
+ def do_transforms(text):
185
+ a = atbash(text or "")
186
+ b = albam(text or "")
187
+ rows = []
188
+ def totals_row(label, txt):
189
+ return {
190
+ "Form": label,
191
+ "Text": normalize_text(txt, keep_spaces=True),
192
+ "Standard": gematria_total(txt, "standard"),
193
+ "Gadol": gematria_total(txt, "gadol"),
194
+ "Ordinal": gematria_total(txt, "ordinal"),
195
+ "Katan": gematria_total(txt, "katan"),
196
+ }
197
+ rows.append(totals_row("Original", text or ""))
198
+ rows.append(totals_row("Atbash", a))
199
+ rows.append(totals_row("Albam", b))
200
+ return a, b, pd.DataFrame(rows)
201
+
202
+ # -----------------------------
203
+ # UI
204
+ # -----------------------------
205
+ with gr.Blocks(title="Hebrew Gematria Lab (Gradio)") as demo:
206
+ gr.Markdown("# Hebrew Gematria Lab โ€” Gradio")
207
+ gr.Markdown(
208
+ "Explore Hebrew words across **Standard**, **Gadol**, **Ordinal**, and **Mispar Katan** systems. "
209
+ "Use **Atbash** and **Albam** transforms, batch-process lists, and export to CSV."
210
+ )
211
+
212
+ with gr.Tabs():
213
+ with gr.Tab("Calculator"):
214
+ with gr.Row():
215
+ text_in = gr.Textbox(label="Enter Hebrew text", value="ื“ืงืจ")
216
+ with gr.Row():
217
+ system = gr.Dropdown(choices=["standard", "gadol", "ordinal", "katan"], value="standard", label="Gematria system")
218
+ show_root = gr.Checkbox(value=True, label="Show digital root")
219
+ calc_btn = gr.Button("Compute")
220
+ result_line = gr.Markdown()
221
+ result_df = gr.Dataframe(headers=["System", "Total", "Digital Root"], interactive=False)
222
+
223
+ calc_btn.click(
224
+ fn=calc_single,
225
+ inputs=[text_in, system, show_root],
226
+ outputs=[result_line, result_df]
227
+ )
228
+
229
+ with gr.Tab("Batch & Matching"):
230
+ batch_tb = gr.Textbox(
231
+ label="Paste list (one item per line)",
232
+ lines=10,
233
+ value="ื“ืงืจ\nื“ืžืช\nืื—ื“\nืื”ื‘ื”\nืžื•ืช\nืžืœืืš\nืžืœืš\nืฉืœื•ื"
234
+ )
235
+ with gr.Row():
236
+ system_b = gr.Dropdown(choices=["standard", "gadol", "ordinal", "katan"], value="standard", label="Gematria system")
237
+ show_root_b = gr.Checkbox(value=True, label="Show digital root")
238
+ target_val_tb = gr.Textbox(label="Target total (optional)", placeholder="e.g., 304")
239
+ sort_chk = gr.Checkbox(value=True, label="Sort by total")
240
+ run_batch_btn = gr.Button("Run Batch")
241
+ batch_df = gr.Dataframe(interactive=False)
242
+ match_info = gr.Markdown()
243
+ download_csv = gr.File(label="Download CSV")
244
+
245
+ run_batch_btn.click(
246
+ fn=do_batch,
247
+ inputs=[batch_tb, system_b, show_root_b, target_val_tb, sort_chk],
248
+ outputs=[batch_df, match_info, download_csv]
249
+ )
250
+
251
+ with gr.Tab("Transforms"):
252
+ t_input = gr.Textbox(label="Hebrew text", value="ืฉืœื•ื")
253
+ t_btn = gr.Button("Transform")
254
+ atbash_out = gr.Textbox(label="Atbash", interactive=False)
255
+ albam_out = gr.Textbox(label="Albam", interactive=False)
256
+ trans_df = gr.Dataframe(interactive=False)
257
+
258
+ t_btn.click(
259
+ fn=do_transforms,
260
+ inputs=[t_input],
261
+ outputs=[atbash_out, albam_out, trans_df]
262
+ )
263
+
264
+ with gr.Tab("Reference"):
265
+ gr.Markdown(
266
+ """
267
+ **Systems**
268
+ - **Standard (Mispar Hechrechi):** ืช=400; finals = base.
269
+ - **Gadol:** finals extended ืš=500, ื=600, ืŸ=700, ืฃ=800, ืฅ=900.
270
+ - **Ordinal:** ื=1 โ€ฆ ืช=22 (finals use base).
271
+ - **Mispar Katan:** letters reduced to 1โ€“9, then summed.
272
+
273
+ **Transforms**
274
+ - **Atbash:** mirror pairs ืโ†”ืช, ื‘โ†”ืฉ, ื’โ†”ืจ, โ€ฆ
275
+ - **Albam:** shift by 11 (ืโ†”ืœ, ื‘โ†”ืž, ื’โ†”ื , โ€ฆ, ื›โ†”ืช).
276
+
277
+ **Cleanup**
278
+ - Removes niqqud/cantillation & non-Hebrew marks; normalizes to NFC.
279
+ """
280
+ )
281
+ ex_df = pd.DataFrame(
282
+ [
283
+ ("ื“ืงืจ (pierce)", "Standard", 304),
284
+ ("ื“ืžืช (damat)", "Standard", 444),
285
+ ("ืื—ื“ (one)", "Standard", 13),
286
+ ("ืื”ื‘ื” (love)", "Standard", 13),
287
+ ],
288
+ columns=["Word", "System", "Total"]
289
+ )
290
+ gr.Dataframe(value=ex_df, interactive=False)
291
+
292
+ gr.Markdown(
293
+ "Tip: Use **Batch & Matching** to hunt for words/phrases that equal a target total (e.g., 304 for ื“ืงืจ)."
294
+ )
295
+
296
+ if __name__ == "__main__":
297
+ demo.launch()