Spaces:
Running
Running
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() |