import gradio as gr
import json, os, calendar, copy
from datetime import datetime
DATA_FILE = "agenda_data.json"
DEFAULT_DATA = {"templates": {"fisse": [{"id": "affitto", "nome": "Affitto", "importo": 800}, {"id": "bollette", "nome": "Bollette", "importo": 150}], "extra": []}, "mesi": {}}
def load_data():
if os.path.exists(DATA_FILE):
try: return json.load(open(DATA_FILE,"r",encoding="utf-8"))
except: pass
return copy.deepcopy(DEFAULT_DATA)
def save_data(d):
open(DATA_FILE,"w",encoding="utf-8").write(json.dumps(d,indent=2,ensure_ascii=False))
def get_mese_data(data,a,m):
k=f"{a}-{m:02d}"
if k not in data["mesi"]: data["mesi"][k]={"fisse_pagate": {}, "extra_pagate": {}}
return data["mesi"][k]
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}.day-number{font-size:10px!important}}"
def build_choices(data,a,m,tipo):
md=get_mese_data(data,a,m); key="fisse_pagate" if tipo=="fisse" else "extra_pagate"; ch=[]; vals=[]
for t in data["templates"][tipo]:
ch.append((f"{t['nome']} - {t['importo']} euro", t["id"]))
if md[key].get(t["id"],False): vals.append(t["id"])
return ch,vals
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: html+='
' if day==0 else f'
{day}
'
html+='
'; return html
with gr.Blocks(css=CSS) 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
"); 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, resta
"); fisse_checks=gr.CheckboxGroup(label="Fisse")
with gr.Row(): nf_nome=gr.Textbox(label="Nome"); 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="Extra")
with gr.Row(): ne_nome=gr.Textbox(label="Nome"); ne_imp=gr.Number(label="Euro"); btn_add_extra=gr.Button("Aggiungi")
totale_html=gr.HTML()
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: {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])
demo.launch()