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()