import gradio as gr
import json
import os
import calendar
from datetime import datetime
import copy
DATA_FILE = "agenda_data.json"
DEFAULT_DATA = {
"templates": {
"fisse": [
{"id": "affitto", "nome": "Affitto", "importo": 800},
{"id": "bollette", "nome": "Bollette", "importo": 150},
{"id": "spesa", "nome": "Spesa", "importo": 400},
{"id": "nido", "nome": "Nido", "importo": 382}
],
"extra": []
},
"mesi": {}
}
def load_data():
if os.path.exists(DATA_FILE):
try:
with open(DATA_FILE, "r", encoding="utf-8") as f:
return json.load(f)
except:
pass
return copy.deepcopy(DEFAULT_DATA)
def save_data(d):
with open(DATA_FILE, "w", encoding="utf-8") as f:
json.dump(d, f, indent=2, ensure_ascii=False)
def get_mese_data(data, a, m):
key = f"{a}-{m:02d}"
if key not in data["mesi"]:
data["mesi"][key] = {"fisse_pagate": {}, "extra_pagate": {}}
return data["mesi"][key]
CSS = """
.calendar-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:6px}
.day-cell{background:white;border-radius:12px;min-height:92px;padding:6px;border:2px solid black}
.day-number{background:black;color:white;border-radius:6px;padding:2px 6px;font-weight:bold;font-size:13px;display:inline-block}
@media (max-width:768px){
.calendar-grid{grid-template-columns:repeat(7,minmax(42px,1fr));gap:3px}
.day-cell{min-height:58px!important;padding:2px!important;border-radius:8px!important}
.day-number{font-size:10px!important}
}
"""
def build_choices(data, a, m, tipo):
md = get_mese_data(data, a, m)
key_p = "fisse_pagate" if tipo=="fisse" else "extra_pagate"
choices, values = [], []
for t in data["templates"][tipo]:
choices.append((f"{t['nome']} - {t['importo']} euro", t["id"]))
if md[key_p].get(t["id"], False):
values.append(t["id"])
return choices, values
def render_calendar(a,m):
cal = calendar.monthcalendar(a,m)
html = '
'
for g in ["L","M","M","G","V","S","D"]:
html += f'
{g}
'
for week in cal:
for day in week:
if day==0:
html += '
'
else:
html += f'
{day}
'
html += '
'
return html
with gr.Blocks(css=CSS, title="Agenda Giulia") as demo:
data_state = gr.State(load_data())
anno = gr.State(datetime.now().year)
mese = gr.State(datetime.now().month)
gr.HTML("Agenda Giulia - Fix Mobile
")
cal_html = gr.HTML()
mese_label = gr.HTML()
with gr.Row():
btn_prev = gr.Button("Prec")
btn_next = gr.Button("Succ")
with gr.Row():
with gr.Column():
gr.HTML("FISSE - spunta = pagata, NON si cancella. Mese prossimo torna da spuntare
")
fisse_checks = gr.CheckboxGroup(label="Spese fisse")
with gr.Row():
nf_nome = gr.Textbox(label="Nome fissa")
nf_imp = gr.Number(label="Euro")
btn_add_fissa = gr.Button("Aggiungi")
with gr.Column():
gr.HTML("EXTRA - spunta = pagata, resta
")
extra_checks = gr.CheckboxGroup(label="Spese extra")
with gr.Row():
ne_nome = gr.Textbox(label="Nome extra")
ne_imp = gr.Number(label="Euro")
btn_add_extra = gr.Button("Aggiungi")
totale_html = gr.HTML()
gr.HTML("Spuntando segni come pagata quel mese ma NON la cancelli. Mese prossimo torna da spuntare.
")
def refresh(data, a, m):
a=int(a); m=int(m)
get_mese_data(data, a, m)
cal = render_calendar(a,m)
label = f"{calendar.month_name[m]} {a}
"
f_ch, f_v = build_choices(data,a,m,"fisse")
e_ch, e_v = build_choices(data,a,m,"extra")
tot = sum([t["importo"] for t in data["templates"]["fisse"]+data["templates"]["extra"]])
md = get_mese_data(data,a,m)
pag = sum([t["importo"] for t in data["templates"]["fisse"] if md["fisse_pagate"].get(t["id"])])
tot_h = f"TOTALE {calendar.month_name[m]}: {tot} euro | Pagato: {pag} euro
"
return data, cal, label, gr.CheckboxGroup(choices=f_ch, value=f_v), gr.CheckboxGroup(choices=e_ch, value=e_v), tot_h
def toggle(data,a,m,sel,tipo):
md=get_mese_data(data,a,m)
key="fisse_pagate" if tipo=="fisse" else "extra_pagate"
for t in data["templates"][tipo]:
md[key][t["id"]] = t["id"] in sel
save_data(data)
return refresh(data,a,m)
def add(data,a,m,nome,imp,tipo):
if nome and imp and imp>0:
nid=nome.lower().replace(" ","_")+f"_{len(data['templates'][tipo])}"
data["templates"][tipo].append({"id":nid,"nome":nome.strip(),"importo":float(imp)})
save_data(data)
return refresh(data,a,m)
def cambia(data,a,m,delta):
m=int(m)+delta; a=int(a)
if m>12: m=1; a+=1
if m<1: m=12; a-=1
return refresh(data,a,m)
demo.load(fn=refresh, inputs=[data_state,anno,mese], outputs=[data_state,cal_html,mese_label,fisse_checks,extra_checks,totale_html])
btn_prev.click(lambda d,a,m: cambia(d,a,m,-1), [data_state,anno,mese], [data_state,cal_html,mese_label,fisse_checks,extra_checks,totale_html])
btn_next.click(lambda d,a,m: cambia(d,a,m,1), [data_state,anno,mese], [data_state,cal_html,mese_label,fisse_checks,extra_checks,totale_html])
fisse_checks.change(lambda d,a,m,s: toggle(d,a,m,s,"fisse"), [data_state,anno,mese,fisse_checks], [data_state,cal_html,mese_label,fisse_checks,extra_checks,totale_html])
extra_checks.change(lambda d,a,m,s: toggle(d,a,m,s,"extra"), [data_state,anno,mese,extra_checks], [data_state,cal_html,mese_label,fisse_checks,extra_checks,totale_html])
btn_add_fissa.click(lambda d,a,m,n,i: add(d,a,m,n,i,"fisse"), [data_state,anno,mese,nf_nome,nf_imp], [data_state,cal_html,mese_label,fisse_checks,extra_checks,totale_html])
btn_add_extra.click(lambda d,a,m,n,i: add(d,a,m,n,i,"extra"), [data_state,anno,mese,ne_nome,ne_imp], [data_state,cal_html,mese_label,fisse_checks,extra_checks,totale_html])
if __name__ == "__main__":
demo.launch()