File size: 9,546 Bytes
3045238
 
99b3ef5
3045238
 
a676448
3045238
e4ee4df
3045238
75d4ca3
3045238
287228a
 
 
e8bbf2e
420ea26
e8bbf2e
420ea26
 
 
57bd653
3045238
75d4ca3
 
 
 
 
 
 
 
 
 
 
 
7a778e3
75d4ca3
e8bbf2e
75d4ca3
7a778e3
57bd653
 
 
 
 
 
 
7a778e3
57bd653
084e807
75d4ca3
e8bbf2e
084e807
75d4ca3
 
 
 
 
 
 
57bd653
084e807
 
 
 
8992d21
4428eed
 
 
 
 
 
 
 
 
 
8992d21
75d4ca3
084e807
401d60b
420ea26
 
 
e4ee4df
4428eed
084e807
4428eed
420ea26
084e807
7a778e3
 
420ea26
e4ee4df
420ea26
 
57bd653
 
084e807
4428eed
 
 
 
57bd653
e4ee4df
57bd653
 
 
4428eed
57bd653
 
3045238
084e807
4428eed
 
 
 
 
 
 
 
 
 
 
 
 
420ea26
3045238
 
 
4428eed
084e807
 
 
 
 
 
e4ee4df
 
 
 
4428eed
e4ee4df
084e807
4428eed
084e807
 
 
 
4428eed
 
 
 
 
 
084e807
4428eed
 
 
084e807
 
 
e4ee4df
084e807
 
 
 
 
 
 
 
 
 
287228a
 
 
 
084e807
287228a
084e807
287228a
 
084e807
287228a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4ef0b13
e8bbf2e
3045238
e4ee4df
e8bbf2e
 
4428eed
084e807
287228a
 
 
e4ee4df
e8bbf2e
287228a
4ef0b13
57bd653
3045238
287228a
084e807
 
 
 
 
 
 
 
 
 
 
 
420ea26
084e807
 
1b969ef
 
3045238
 
287228a
4ef0b13
287228a
4ef0b13
287228a
084e807
 
 
 
 
 
4ef0b13
 
3045238
4428eed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
import logging
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)

import gradio as gr
from gradio_calendar import Calendar
from gematria import calculate_gematria, strip_diacritics
from datetime import datetime, timedelta
import json
import inflect

import os
import tempfile

# --- Helper Functions ---
def calculate_gematria_sum(text):
    """Calculates the gematria sum of a given text."""
    if text:
        text_gematria = calculate_gematria(strip_diacritics(text))
        return text_gematria
    return None

def number_to_ordinal_word(number):
    ordinal_dict = {
        1: "first", 2: "second", 3: "third", 4: "fourth", 5: "fifth",
        6: "sixth", 7: "seventh", 8: "eighth", 9: "ninth", 10: "tenth",
        11: "eleventh", 12: "twelfth", 13: "thirteenth", 14: "fourteenth",
        15: "fifteenth", 16: "sixteenth", 17: "seventeenth", 18: "eighteenth",
        19: "nineteenth", 20: "twentieth", 21: "twentyfirst", 22: "twentysecond",
        23: "twentythird", 24: "twentyfourth", 25: "twentyfifth",
        26: "twentysixth", 27: "twentyseventh", 28: "twentyeighth",
        29: "twentyninth", 30: "thirtieth", 31: "thirtyfirst"
    }
    return ordinal_dict.get(number, "")

def date_to_words(date_string):
    """Converts a date in YYYY-MM-DD format to English words."""
    inf_engine = inflect.engine()
    date_obj = datetime.strptime(date_string, "%Y-%m-%d")
    return format_date(date_obj, inf_engine)

def month_year_to_words(date_string):
    """Converts a month and year in YYYY-MM format to English words."""
    inf_engine = inflect.engine()
    date_obj = datetime.strptime(date_string, "%Y-%m")
    return format_date(date_obj, inf_engine)

def format_date(date_obj, inf_engine):
    """Formats day-month-year into English words with ordinal day."""
    year = date_obj.year
    if 1100 <= year <= 1999:
        year_words = f"{inf_engine.number_to_words(year // 100, andword='')} hundred"
        if year % 100 != 0:
            year_words += f" {inf_engine.number_to_words(year % 100, andword='')}"
    else:
        year_words = inf_engine.number_to_words(year, andword='')
    year_formatted = year_words.replace(',', '')

    month = date_obj.strftime("%B")

    # Tag auslesen und in Ordinalform umwandeln
    day = date_obj.day
    day_ordinal = number_to_ordinal_word(day)
    return f"{day_ordinal} {month} {year_formatted}"

def format_year_to_words(year):
    """Formats a year as English words."""
    inf_engine = inflect.engine()
    if 1100 <= year <= 1999:
        year_words = f"{inf_engine.number_to_words(year // 100, andword='')} hundred"
        if year % 100 != 0:
            year_words += f" {inf_engine.number_to_words(year % 100, andword='')}"
    else:
        year_words = inf_engine.number_to_words(year, andword='')
    return year_words.replace(',', '')

def perform_gematria_calculation_for_date_range(start_date, end_date):
    """Performs gematria calculation for each day in a date range and groups by sum."""
    logger.debug(f"Start Date: {start_date}, End Date: {end_date}")
    results = {}
    delta = timedelta(days=1)
    current_date = start_date

    processed_months = set()  # To track processed month-year combinations
    processed_years = set()   # To track processed years

    while current_date <= end_date:
        # 1) Full date calculation
        date_string = current_date.strftime("%Y-%m-%d")
        date_words = date_to_words(date_string)
        gematria_sum = calculate_gematria_sum(date_words)

        if gematria_sum not in results:
            results[gematria_sum] = []
        results[gematria_sum].append({"date": date_string, "date_words": date_words})

        # 2) Month+Year calculation (only once per unique month-year)
        month_year_key = current_date.strftime("%Y-%m")
        if month_year_key not in processed_months:
            processed_months.add(month_year_key)
            month_year_words = f"{current_date.strftime('%B')} {format_year_to_words(current_date.year)}"
            month_year_gematria_sum = calculate_gematria_sum(month_year_words)

            if month_year_gematria_sum not in results:
                results[month_year_gematria_sum] = []
            results[month_year_gematria_sum].append({
                "date": month_year_key,
                "date_words": month_year_words
            })

        # 3) Year-only calculation (only once per unique year)
        year_key = str(current_date.year)
        if year_key not in processed_years:
            processed_years.add(year_key)
            year_words = format_year_to_words(current_date.year)
            year_gematria_sum = calculate_gematria_sum(year_words)

            if year_gematria_sum not in results:
                results[year_gematria_sum] = []
            results[year_gematria_sum].append({
                "date": year_key,
                "date_words": year_words
            })

        current_date += delta

    return results

# --- Event Handlers ---
def generate_json_output(results, start_date, end_date, include_words, min_results):
    """
    Erzeugt ein Dictionary (später für gr.JSON) mit Filterung nach Mindestanzahl,
    wenn min_results nicht None ist.
    """
    output = {
        "DateRange": {
            "StartDate": start_date.strftime("%Y-%m-%d"),
            "EndDate": end_date.strftime("%Y-%m-%d")
        },
        "Results": []
    }

    for gematria_sum, entries in results.items():
        # Falls eine Mindestanzahl gefordert ist, filtern
        if min_results is not None and len(entries) < min_results:
            continue

        group = {
            "GematriaSum": gematria_sum,
            "Entries": []
        }
        for entry in entries:
            entry_data = {"date": entry["date"]}
            if include_words:
                entry_data["date_words"] = entry["date_words"]
            group["Entries"].append(entry_data)

        output["Results"].append(group)

    return output  # <-- Wichtig: Als Dictionary zurückgeben

def perform_calculation(start_date, end_date, include_words, min_results):
    """
    Führt die Gematria-Berechnung durch und gibt ein Dictionary zurück,
    das direkt an gr.JSON übergeben werden kann.
    """
    results = perform_gematria_calculation_for_date_range(start_date, end_date)
    return generate_json_output(results, start_date, end_date, include_words, min_results)

def download_json(json_data, start_date, end_date):
    """
    Nimmt das JSON-Dict (aus gr.JSON) und legt es als Datei ab,
    deren Pfad wir zurückgeben.
    Ältere Gradio-Versionen erwarten hier typischerweise einen String
    (also File-Pfad), keinen Tuple oder dict.
    """
    # Einen Dateinamen konstruieren – der taucht nur intern auf:
    filename = f"gematria_{start_date.strftime('%Y%m%d')}_{end_date.strftime('%Y%m%d')}.json"

    # Dictionary -> JSON-String
    json_string = json.dumps(json_data, indent=4, ensure_ascii=False)

    # Temporäre Datei erstellen
    # delete=False => Datei bleibt bestehen, bis wir sie manuell entfernen wollen
    temp = tempfile.NamedTemporaryFile(suffix=".json", delete=False)
    temp_path = temp.name  # Pfad merken

    try:
        # Schreiben als Bytes
        temp.write(json_string.encode("utf-8"))
        temp.flush()
    finally:
        # Datei-Handle schließen
        temp.close()

    # Jetzt returnen wir den Pfad
    # => Gradio wird damit umgehen können und bietet Download an
    return temp_path

# --- Main Gradio App ---
with gr.Blocks() as app:
    with gr.Row():
        start_date = Calendar(type="datetime", label="Start Date")
        end_date = Calendar(type="datetime", label="End Date")

    with gr.Row():
        include_date_words = gr.Checkbox(value=True, label="Include Date-Words in JSON")
        filter_results = gr.Checkbox(value=False, label="Filter to sums with at least")
        min_results_input = gr.Number(value=2, label="results", interactive=True, precision=0)

    calculate_btn = gr.Button("Calculate Gematria for Date Range")
    json_output = gr.JSON(label="JSON Output")
    download_btn = gr.Button("Download JSON")
    json_file = gr.File(label="Downloaded JSON")


    # Damit wir den Wert von filter_results berücksichtigen können:
    def handle_calculate(start_val, end_val, include_val, filter_val, min_val):
        if not filter_val:  # Checkbox nicht gesetzt
            # => Kein Filtern, also min_results=None
            return perform_calculation(start_val, end_val, include_val, None)
        else:
            # => Filter aktiviert, also min_results = min_val
            # min_val kann float sein; auf int casten, falls gewünscht
            return perform_calculation(start_val, end_val, include_val, int(min_val))

    # Button-Klick: wir rufen handle_calculate auf,
    # das ggf. min_results auf None setzt oder den Wert übernimmt.
    calculate_btn.click(
        handle_calculate,
        inputs=[start_date, end_date, include_date_words, filter_results, min_results_input],
        outputs=[json_output],
        api_name="calculate_gematria"
    )

    # Hier der "fix" im Download-Button
    download_btn.click(
        download_json,             # unsere neue Funktion
        inputs=[json_output, start_date, end_date],
        outputs=[json_file]        # gr.File-Element
    )

    filter_results.change(
        lambda checked: gr.update(interactive=checked),
        inputs=filter_results,
        outputs=min_results_input
    )

if __name__ == "__main__":
    app.launch(share=False)