GeneraExcelAI / app.py
MatteoScript's picture
Update app.py
e402ae5 verified
import streamlit as st
from openai import OpenAI
import openpyxl
from openpyxl.styles import Font, PatternFill, Border, Side, Alignment
import os
import io
import traceback
import sys
from dotenv import load_dotenv
import json
load_dotenv()
st.set_page_config(layout="wide", page_title="Generatore Excel AI")
st.title("📊 Generatore Excel AI")
if "logged" not in st.session_state:
st.session_state.logged = False
if st.session_state.logged == False:
login_placeholder = st.empty()
with login_placeholder.container():
container = st.container(border=True)
username = container.text_input('Username')
password = container.text_input('Passowrd', type='password')
login = container.button(' Login ', type='primary')
if not login or username != os.getenv("LOGIN_USER") or password != os.getenv("LOGIN_PASSWORD"):
if login:
st.error('Password Errata')
st.stop()
st.session_state.logged = True
login_placeholder.empty()
API_KEY = os.getenv("API_HUGGINGFACE")
BASE_URL = "https://matteoscript-ai.hf.space/v1/"
MODEL_NAME = "gemini-2.5-flash-preview-05-20" # Sostituisci con il tuo modello preferito
if not API_KEY:
st.error("API Key Hugging Face non trovata. Assicurati che il file .env sia configurato correttamente con API_HUGGINGFACE.")
st.stop()
client = OpenAI(api_key=API_KEY, base_url=BASE_URL)
DEFAULT_EXCEL_FILENAME = "excel_ai.xlsx"
MAX_CORRECTION_ATTEMPTS = 5
PROMPT_FOR_SHEET_PLANNER = """
Sei un assistente AI specializzato nella pianificazione di documenti Excel multi-foglio.
Data una richiesta generale dell'utente e un numero di fogli desiderato, il tuo compito è proporre:
1. Un nome univoco e descrittivo per ciascun foglio (massimo 30 caratteri, evita caratteri speciali non ammessi nei nomi dei fogli Excel come / ? * [ ]).
2. Una breve descrizione (1-3 frasi) dello scopo, del contenuto principale di ciascun foglio e delle possibili interazioni/collegamenti con altri fogli. Questa descrizione verrà poi usata come base per generare il contenuto dettagliato del foglio.
La tua risposta DEVE essere un array JSON di oggetti. Ogni oggetto rappresenta un foglio e DEVE avere i campi "sheet_name" (stringa) e "sheet_purpose" (stringa).
Il numero di oggetti nell'array DEVE corrispondere esattamente al numero di fogli specificato dall'utente.
Esempio di richiesta utente: "Report vendite trimestrale (3 fogli): 1. Input dati grezzi vendite. 2. Riepilogo vendite per prodotto, che legga da input. 3. Riepilogo vendite per regione, che legga anch'esso da input."
Esempio di TUA risposta JSON:
[
{"sheet_name": "Input Vendite", "sheet_purpose": "Foglio per l'inserimento manuale o l'importazione dei dati grezzi di vendita, includendo data, ID prodotto, quantità, prezzo, regione."},
{"sheet_name": "Riep Prodotti", "sheet_purpose": "Analizza i dati dal foglio 'Input Vendite' per mostrare un riepilogo delle vendite per prodotto. Include totali, medie e forse grafici. Utilizza formule per aggregare i dati da 'Input Vendite'."},
{"sheet_name": "Riep Regioni", "sheet_purpose": "Analizza i dati dal foglio 'Input Vendite' per visualizzare le performance di vendita per regione. Potrebbe includere somme condizionali basate sui dati di 'Input Vendite'."}
]
Non includere ```json o ``` all'inizio o alla fine della tua risposta. Fornisci solo l'array JSON.
"""
BASE_PROMPT_NO_BACKTICKS = """
Sei un esperto generatore di SCRIPT PYTHON per MODIFICARE un oggetto Workbook openpyxl esistente (fornito in una variabile chiamata wb). Il tuo compito è aggiungere e popolare UN SINGOLO FOGLIO in questo Workbook, usando la libreria openpyxl.
IL TUO SCRIPT NON DEVE ASSOLUTAMENTE:
- Creare un nuovo Workbook (NON usare openpyxl.Workbook()). L'oggetto Workbook ti viene fornito e si chiama wb.
- Salvare il Workbook (NON usare wb.save()). Il salvataggio sarà gestito esternamente.
- Includere istruzioni di import globali. Le funzioni e classi necessarie (wb, Font, PatternFill, Border, Side, Alignment) saranno già disponibili nello scope.
IL TUO SCRIPT PYTHON DEVE:
- Aspettarsi che un oggetto openpyxl.Workbook (wb) sia già definito.
- Creare un nuovo foglio o accedere a uno esistente usando il TITOLO_FOGLIO_RICHIESTO:
sheet_title = "TITOLO_FOGLIO_RICHIESTO" # Questo sarà il nome fornito
if sheet_title in wb.sheetnames:
sheet = wb[sheet_title]
# Pulisci il foglio se deve essere sovrascritto (valori e stili)
for row_idx in range(1, sheet.max_row + 1):
for col_idx in range(1, sheet.max_column + 1):
cell = sheet.cell(row=row_idx, column=col_idx)
cell.value = None
cell.style = openpyxl.styles.Style() # Reset stile base
else:
sheet = wb.create_sheet(title=sheet_title)
- Popolare l'oggetto sheet con dati, header, formule e formattazione come da richiesta.
- Usare stili Material Design e font moderni.
- Includere FORMULE Excel, specialmente se la richiesta implica calcoli o aggregazioni.
CONTESTO DEI FOGLI PRECEDENTI (SE FORNITO):
Se nella richiesta utente qui sotto trovi una sezione "CONTESTO DEI FOGLI PRECEDENTI", essa conterrà gli script Python usati per generare i fogli precedenti.
Usa questo per creare FORMULE EXCEL CHE COLLEGANO IL FOGLIO CORRENTE AI FOGLI PRECEDENTI.
Esempio: sheet['B1'].value = "='DatiVendite'!E10"
Analizza gli script precedenti per nomi dei fogli e struttura dati. I nomi dei fogli nelle formule DEVONO CORRISPONDERE ESATTAMENTE.
REQUISITI CODICE GENERATO:
- SENZA commenti python (#).
- Massimo un ritorno a capo vuoto consecutivo.
- Gestione errori interni al try/except (usare 'pass', non 'raise e').
- NON impostare sola lettura.
- NON usare Alignment se non strettamente necessario.
- Genera dati di esempio realistici (5-10 righe) se non diversamente specificato.
- Applica formattazione ad header e celle chiave.
"""
PROMPT_FOR_SCRIPT_CORRECTION = """
Sei un esperto programmatore Python specializzato nella libreria openpyxl e nel debugging di script che manipolano file Excel.
Ti è stato fornito uno script Python che ha fallito durante l'esecuzione mentre tentava di aggiungere e popolare un foglio in un workbook openpyxl esistente ('wb').
Ti sono stati forniti anche l'errore esatto e il traceback.
IL TUO COMPITO È:
1. Analizzare attentamente lo script originale, l'errore e il traceback.
2. Identificare la causa dell'errore.
3. Correggere lo script Python. Lo script corretto deve ancora rispettare TUTTE le seguenti regole originali:
- DEVE operare su un oggetto Workbook openpyxl esistente chiamato 'wb'.
- NON DEVE creare un nuovo Workbook (NON usare openpyxl.Workbook()).
- NON DEVE salvare il Workbook (NON usare wb.save()).
- NON DEVE includere istruzioni di import globali (es. 'import openpyxl'). Le classi necessarie (wb, Font, PatternFill, Border, Side, Alignment) sono già nello scope.
- DEVE creare/accedere al foglio specificato da TITOLO_FOGLIO_RICHIESTO. Il nome del foglio è critico, non alterarlo a meno che l'errore non sia specificamente legato ad esso e la correzione sia ovvia.
- DEVE popolare il foglio come descritto in DESCRIZIONE_ORIGINALE_FOGLIO.
- Se era presente un CONTESTO_FOGLI_PRECEDENTI, lo script corretto deve ancora poter utilizzare quel contesto per eventuali formule inter-foglio.
- DEVE essere SENZA commenti python (#).
- DEVE avere al massimo un ritorno a capo vuoto consecutivo.
- DEVE gestire gli errori specifici del foglio internamente con un blocco try/except che termina con 'pass' (non 'raise e'). L'intero script dovrebbe essere avvolto in un try/except.
- NON DEVE usare la classe Alignment esplicitamente a meno che non sia fondamentale e parte della descrizione originale.
- DEVE mantenere l'obiettivo originale del foglio. Non rimuovere funzionalità a meno che non siano la causa diretta dell'errore e non ci sia una correzione ovvia.
4. Restituisci SOLO lo script Python CORRETTO E COMPLETO, senza alcuna spiegazione aggiuntiva, commenti o ```python.
INFORMAZIONI FORNITE:
- TITOLO_FOGLIO_RICHIESTO: "{sheet_name}"
- DESCRIZIONE_ORIGINALE_FOGLIO: "{sheet_purpose}"
- CONTESTO_FOGLI_PRECEDENTI (se applicabile):
{previous_scripts_context}
- SCRIPT_ORIGINALE_FALLITO:
---
{original_script}
---
- ERRORE_E_TRACEBACK:
---
{error_traceback}
---
Restituisci solo il codice Python corretto. Non aggiungere spiegazioni prima o dopo il codice. Assicurati che lo script sia completo e pronto per essere eseguito.
"""
def initialize_session_state():
defaults = {
"initial_excel_request": "Business Plan di lancio prodotto per un'azienda Loyalty specializzata nella GDO",
"num_sheets_requested": 1,
"ai_sheet_plan": None,
"workbook_object": None,
"generated_scripts_for_sheets": [],
"final_excel_file_path": None,
"final_excel_filename": DEFAULT_EXCEL_FILENAME,
"process_log": [],
"error_message": "",
"warning_message": "",
"generation_started": False
}
for key, value in defaults.items():
if key not in st.session_state:
st.session_state[key] = value
def log_message(message, level="info"):
log_entry = f"[{level.upper()}] {message}\n"
st.session_state.process_log.append(log_entry)
if level == "error":
st.session_state.error_message += message + "\n"
elif level == "warning":
st.session_state.warning_message += message + "\n"
def call_openai_for_sheet_plan(overall_request, num_sheets):
st.session_state.error_message = ""
st.session_state.warning_message = ""
try:
user_content = f"Richiesta generale dell'utente: \"{overall_request}\"\nNumero di fogli desiderato: {num_sheets}"
completion = client.chat.completions.create(
model=MODEL_NAME,
messages=[
{"role": "system", "content": PROMPT_FOR_SHEET_PLANNER},
{"role": "user", "content": user_content}
],
temperature=0.3,
response_format={"type": "json_object"}
)
response_content = completion.choices[0].message.content.strip()
log_message(f"Risposta grezza AI (piano fogli): {response_content}")
planned_sheets = json.loads(response_content)
if isinstance(planned_sheets, list) and all(isinstance(item, dict) and "sheet_name" in item and "sheet_purpose" in item for item in planned_sheets):
if len(planned_sheets) == num_sheets: return planned_sheets
else:
msg = f"AI ha proposto {len(planned_sheets)} fogli, richiesti {num_sheets}. Verifica."
log_message(msg, level="warning"); st.session_state.warning_message = msg
return planned_sheets
else:
msg = "Struttura JSON piano fogli AI non valida."
log_message(msg, level="error"); st.session_state.error_message = msg
return None
except json.JSONDecodeError as e:
msg = f"Errore decodifica JSON piano fogli: {e}. Risposta: {response_content}"
log_message(msg, level="error"); st.session_state.error_message = msg
return None
except Exception as e:
log_message(f"Errore OpenAI piano fogli: {e}", level="error"); st.session_state.error_message = f"Errore OpenAI pianificazione: {e}"
return None
def call_openai_for_sheet_script(sheet_purpose, sheet_name, prev_scripts_ctx):
st.session_state.error_message = ""
try:
user_content = (
f"TITOLO_FOGLIO_RICHIESTO: '{sheet_name}'.\n"
f"Descrizione per '{sheet_name}':\n{sheet_purpose}\n\n"
)
if prev_scripts_ctx:
user_content += f"CONTESTO_FOGLI_PRECEDENTI:\n{prev_scripts_ctx}\n"
completion = client.chat.completions.create(
model=MODEL_NAME,
messages=[
{"role": "system", "content": BASE_PROMPT_NO_BACKTICKS},
{"role": "user", "content": user_content}
],
temperature=0.1,
)
script = completion.choices[0].message.content.strip()
if script.startswith("```python"): script = script[9:]
if script.endswith("```"): script = script[:-3]
return script.strip()
except Exception as e:
st.session_state.error_message = f"Errore OpenAI script foglio '{sheet_name}': {e}"
log_message(st.session_state.error_message, level="error")
return None
def call_openai_for_script_correction(original_script, error_traceback, sheet_name, sheet_purpose, prev_scripts_ctx):
st.session_state.error_message = "" # Resetta per la chiamata di correzione
log_message(f"Tentativo di correzione AI per foglio '{sheet_name}'.")
try:
prompt = PROMPT_FOR_SCRIPT_CORRECTION.format(
sheet_name=sheet_name,
sheet_purpose=sheet_purpose,
previous_scripts_context=prev_scripts_ctx if prev_scripts_ctx else "Nessuno.",
original_script=original_script,
error_traceback=error_traceback
)
completion = client.chat.completions.create(
model=MODEL_NAME,
messages=[{"role": "user", "content": prompt}], # Il system prompt è inglobato per semplicità qui
temperature=0.2, # Leggermente più creativo per trovare soluzioni
)
corrected_script = completion.choices[0].message.content.strip()
if corrected_script.startswith("```python"): corrected_script = corrected_script[9:]
if corrected_script.endswith("```"): corrected_script = corrected_script[:-3]
log_message(f"Script corretto proposto dall'AI per '{sheet_name}'.")
return corrected_script.strip()
except Exception as e:
st.session_state.error_message = f"Errore OpenAI correzione script per '{sheet_name}': {e}"
log_message(st.session_state.error_message, level="error")
return None
def execute_single_sheet_script(script_code, workbook_obj, sheet_name_being_processed):
#st.session_state.error_message = "" # Non resettare qui, l'errore può venire da OpenAI
current_error_message_for_exec = ""
capture_out = io.StringIO()
original_stdout = sys.stdout
sys.stdout = capture_out
execution_success = False
script_globals = {
'wb': workbook_obj, 'openpyxl': openpyxl,
'Font': Font, 'PatternFill': PatternFill,
'Border': Border, 'Side': Side, 'Alignment': Alignment
}
detailed_error_for_correction = ""
try:
exec(script_code, script_globals)
execution_success = True
except Exception as e:
tb_str = traceback.format_exc()
current_error_message_for_exec = f"Errore esecuzione script per foglio '{sheet_name_being_processed}':\n{type(e).__name__}: {e}\nTraceback:\n{tb_str}"
detailed_error_for_correction = f"{type(e).__name__}: {e}\n{tb_str}"
# Non loggare qui come errore fatale subito, potrebbe essere corretto
finally:
sys.stdout = original_stdout
execution_log = capture_out.getvalue()
capture_out.close()
if not execution_success: # Solo se fallisce, aggiorna l'errore di session_state
st.session_state.error_message += current_error_message_for_exec + "\n"
return execution_success, execution_log, detailed_error_for_correction
def render_sidebar():
with st.sidebar:
st.header("🤖 Input per l'AI")
st.session_state.initial_excel_request = st.text_area(
"📝 Descrivi l'Excel",
value=st.session_state.initial_excel_request, height=200,
help="Sii descrittivo. Specifica se i fogli devono leggere dati da altri."
)
st.session_state.num_sheets_requested = st.number_input(
"🔢 Numero di fogli:", min_value=1, max_value=20,
value=st.session_state.num_sheets_requested, step=1
)
def render_main_content():
st.markdown("Definisci richiesta e numero Fogli, poi l'AI **pianificherà, genererà, collegherà** e tenterà di auto-correggere gli script dei fogli")
if st.sidebar.button("💡 Pianifica Fogli con AI", type="primary", use_container_width=True):
st.session_state.ai_sheet_plan = None
st.session_state.generated_scripts_for_sheets = []
st.session_state.process_log = []
st.session_state.error_message = ""
st.session_state.warning_message = ""
st.session_state.final_excel_file_path = None
st.session_state.generation_started = False
log_message("Avvio pianificazione fogli...")
with st.spinner("L'AI sta pianificando i fogli..."):
plan = call_openai_for_sheet_plan(st.session_state.initial_excel_request, st.session_state.num_sheets_requested)
if plan:
st.session_state.ai_sheet_plan = plan
log_message(f"Piano fogli ricevuto: {len(plan)} fogli.")
st.success(f"✅ L'AI ha pianificato {len(plan)} fogli! Rivedi/modifica descrizioni.")
else:
log_message("Fallimento pianificazione.", level="error")
st.error(f"Impossibile pianificare. {st.session_state.error_message}")
st.rerun()
if st.session_state.ai_sheet_plan:
st.markdown("---")
st.subheader("📋 Piano Fogli Proposto dall'AI (Modificabile)")
st.caption("Modifica descrizioni se necessario, specialmente per chiarire collegamenti.")
for i, sheet_def in enumerate(st.session_state.ai_sheet_plan):
with st.expander(f"Foglio {i+1}: **{sheet_def['sheet_name']}**", expanded=True):
new_purpose = st.text_area(
f"Scopo/Contenuto per '{sheet_def['sheet_name']}':",
value=sheet_def['sheet_purpose'], key=f"sheet_purpose_edit_{i}", height=120
)
if new_purpose != sheet_def['sheet_purpose']:
st.session_state.ai_sheet_plan[i]['sheet_purpose'] = new_purpose
if st.button("✨ Genera, Assembla e Auto-Correggi Excel ✨", type="primary", use_container_width=True, disabled=st.session_state.generation_started):
st.session_state.generation_started = True
# Conserva log pianificazione, pulisce log di vecchie generazioni
planning_logs = [log for log in st.session_state.process_log if "pianificazione" in log.lower() or "piano fogli ricevuto" in log.lower()]
st.session_state.process_log = planning_logs
st.session_state.error_message = ""
st.session_state.warning_message = ""
st.session_state.generated_scripts_for_sheets = [] # Visualizzazione finale
st.session_state.final_excel_file_path = None
st.session_state.workbook_object = openpyxl.Workbook()
if st.session_state.workbook_object.sheetnames:
st.session_state.workbook_object.remove(st.session_state.workbook_object.active)
log_message("Workbook inizializzato per generazione.")
generated_scripts_history_for_context = [] # Per passare contesto all'AI
all_sheets_successful = True
progress_bar = st.progress(0)
num_total_sheets = len(st.session_state.ai_sheet_plan)
for i, sheet_def in enumerate(st.session_state.ai_sheet_plan):
sheet_name = sheet_def["sheet_name"]
sheet_purpose = sheet_def["sheet_purpose"]
progress_text = f"Foglio {i+1}/{num_total_sheets}: '{sheet_name}'"
log_message(f"--- Inizio {progress_text} ---")
progress_bar.progress((i + 1) / num_total_sheets, text=f"Generando: {progress_text}")
previous_scripts_context_str = ""
if generated_scripts_history_for_context:
parts = ["'''\nScript fogli precedenti (riferimento/collegamenti):\n"]
for idx, prev_info in enumerate(generated_scripts_history_for_context):
parts.append(f"--- Script Foglio '{prev_info['name']}' (Indice {idx}) ---\n{prev_info['script']}\n")
parts.append("'''\n")
previous_scripts_context_str = "\n".join(parts)
current_script_for_sheet = None
correction_attempts = 0
sheet_successfully_processed = False
# Tentativo iniziale di generazione script
spinner_msg = f"🤖 AI genera script per '{sheet_name}' (contesto: {len(generated_scripts_history_for_context)} fogli)..."
with st.spinner(spinner_msg):
current_script_for_sheet = call_openai_for_sheet_script(sheet_purpose, sheet_name, previous_scripts_context_str)
if current_script_for_sheet:
log_message(f"Script iniziale per '{sheet_name}' generato.")
# Ciclo di esecuzione e correzione
while correction_attempts <= MAX_CORRECTION_ATTEMPTS and not sheet_successfully_processed:
exec_spinner_msg = f"⚙️ Esecuzione script per '{sheet_name}'"
if correction_attempts > 0:
exec_spinner_msg += f" (tentativo correzione {correction_attempts})"
with st.spinner(exec_spinner_msg):
success, exec_log, error_details_for_correction = execute_single_sheet_script(
current_script_for_sheet, st.session_state.workbook_object, sheet_name
)
if exec_log: log_message(f"Log esecuzione per '{sheet_name}':\n{exec_log}")
if success:
log_message(f"Foglio '{sheet_name}' aggiunto/modificato con successo.")
st.session_state.generated_scripts_for_sheets.append({"name": sheet_name, "script": current_script_for_sheet, "status": "Successo"})
generated_scripts_history_for_context.append({"name": sheet_name, "script": current_script_for_sheet})
sheet_successfully_processed = True
else:
log_message(f"⚠️ Fallimento esecuzione script per '{sheet_name}'. Errore: {error_details_for_correction}", level="warning")
correction_attempts += 1
if correction_attempts <= MAX_CORRECTION_ATTEMPTS:
log_message(f"Tentativo {correction_attempts}/{MAX_CORRECTION_ATTEMPTS} di auto-correzione AI per '{sheet_name}'.")
with st.spinner(f"🤖 AI tenta correzione per '{sheet_name}' (errore: {error_details_for_correction.splitlines()[0]}..."):
corrected_script_candidate = call_openai_for_script_correction(
current_script_for_sheet, error_details_for_correction,
sheet_name, sheet_purpose, previous_scripts_context_str
)
if corrected_script_candidate:
log_message(f"Nuovo script corretto proposto per '{sheet_name}'.")
current_script_for_sheet = corrected_script_candidate
# Loop rieseguirà execute_single_sheet_script
else:
log_message(f"AI non ha fornito script corretto per '{sheet_name}'. Tentativo {correction_attempts} fallito.", level="error")
st.session_state.generated_scripts_for_sheets.append({"name": sheet_name, "script": current_script_for_sheet, "status": f"Fallito dopo correzione (AI non ha corretto)", "error": error_details_for_correction})
break # Esce dal ciclo di correzione se AI non dà nulla
else:
log_message(f"Limite tentativi ({MAX_CORRECTION_ATTEMPTS}) di correzione raggiunto per '{sheet_name}'. Errore finale: {error_details_for_correction}", level="error")
st.session_state.generated_scripts_for_sheets.append({"name": sheet_name, "script": current_script_for_sheet, "status": f"Fallito (max tentativi)", "error": error_details_for_correction})
else: # Fallimento generazione script iniziale
log_message(f"⚠️ Fallimento generazione script iniziale per '{sheet_name}'. {st.session_state.error_message}", level="error")
st.session_state.generated_scripts_for_sheets.append({"name": sheet_name, "script": "# ERRORE GENERAZIONE SCRIPT", "status": "Fallimento Generazione", "error": st.session_state.error_message})
if not sheet_successfully_processed:
all_sheets_successful = False
break # Interrompe generazione se un foglio fallisce definitivamente
log_message(f"--- Fine Foglio {i+1}: '{sheet_name}' ---")
progress_bar.empty()
if all_sheets_successful and st.session_state.workbook_object:
try:
if os.path.exists(st.session_state.final_excel_filename): os.remove(st.session_state.final_excel_filename)
st.session_state.workbook_object.save(st.session_state.final_excel_filename)
st.session_state.final_excel_file_path = st.session_state.final_excel_filename
log_message(f"Workbook '{st.session_state.final_excel_filename}' salvato.")
st.success(f"✅ Excel '{st.session_state.final_excel_filename}' generato!")
except Exception as e:
log_message(f"❌ Errore salvataggio Workbook: {e}", level="error"); st.session_state.error_message = f"Errore salvataggio: {e}"
elif not all_sheets_successful:
st.error(f"Processo interrotto. Excel non salvato. {st.session_state.error_message}")
else:
st.warning("Nessun foglio elaborato o problema sconosciuto.")
st.session_state.generation_started = False
st.rerun()
if st.session_state.warning_message: st.warning(st.session_state.warning_message)
# Mostra errore principale solo se non c'è un file di successo e non siamo in fase di generazione attiva
if st.session_state.error_message and not st.session_state.final_excel_file_path and not st.session_state.generation_started:
st.error(f"Si sono verificati errori durante l'ultimo processo: {st.session_state.error_message}")
if st.session_state.generated_scripts_for_sheets:
st.markdown("---")
with st.expander("🔍 Vedi Script Generati per i Fogli (e Stato Esecuzione)"):
for item in st.session_state.generated_scripts_for_sheets:
# Modifica questa riga:
sheet_name_display = item.get('name', 'Nome Foglio Mancante')
status_display = item.get('status', 'Stato Sconosciuto')
script_display = item.get('script', '# Script non disponibile')
error_display = item.get('error', None)
st.subheader(f"Script per '{sheet_name_display}' - Stato: {status_display}")
st.code(script_display, language="python")
if error_display: # Controlla se la chiave 'error' esiste e ha un valore
st.error(f"Dettaglio Errore Finale per '{sheet_name_display}':\n{error_display}")
if st.session_state.process_log:
st.markdown("---")
with st.expander("📝 Log del Processo Dettagliato", expanded=False):
st.text_area("Log:", "".join(st.session_state.process_log), height=300, disabled=True)
render_download_section()
render_footer()
def render_download_section():
if st.session_state.final_excel_file_path and os.path.exists(st.session_state.final_excel_file_path):
st.markdown("---")
st.header("📥 Download File Excel Finale")
try:
with open(st.session_state.final_excel_file_path, "rb") as fp: excel_bytes = fp.read()
st.download_button(
label=f"💾 Scarica {st.session_state.final_excel_filename}", data=excel_bytes,
file_name=st.session_state.final_excel_filename,
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
use_container_width=True, type="primary"
)
except Exception as e:
st.error(f"Impossibile leggere il file per il download: {e}")
log_message(f"Errore lettura file per download: {e}", level="error")
elif st.session_state.ai_sheet_plan and not st.session_state.generation_started:
st.info("Una volta generato l'Excel, il download apparirà qui.")
def render_footer():
st.markdown("---")
st.markdown("💡 **Nota:** L'AI tenta di collegare i fogli e auto-correggere errori. Verifica sempre i risultati e le formule.")
def main():
initialize_session_state()
render_sidebar()
render_main_content()
if __name__ == "__main__":
main()