From 17b849b89a896c0dbf5b7ab79e9fc5fc91fabae1 Mon Sep 17 00:00:00 2001 From: Silas Brito Date: Mon, 13 Jul 2026 20:12:56 -0300 Subject: [PATCH] fix: resolve pointer exhaustion on upload rerun and TypeError float len on PDF export --- app.py | 42 ++++++++++++++++++++++++------------------ report_gen.py | 20 ++++++++++++++++---- 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/app.py b/app.py index 8e4ff83..36e1b6b 100644 --- a/app.py +++ b/app.py @@ -262,29 +262,35 @@ uploaded_file = st.sidebar.file_uploader( if uploaded_file is not None: try: - # Read content and compute hash for deduplication logic - file_bytes = uploaded_file.read() + # getvalue() reads the whole stream without pointer exhaustion across script reruns + file_bytes = uploaded_file.getvalue() file_hash = compute_hash(file_bytes) - # Check if already processed in database - is_processed = db.is_file_processed(file_hash) - - # Parse CSV - df_parsed = parse_nbu_csv(file_bytes) - - if not df_parsed.empty: - # Perform SQLite UPSERT operation for each parsed job - db.save_jobs(df_parsed) + # Only process if this file hash was not just processed in this session + if st.session_state.get('last_processed_file') != file_hash: + # Check historical database logs + is_processed = db.is_file_processed(file_hash) - if not is_processed: + # Parse CSV + df_parsed = parse_nbu_csv(file_bytes) + + if not df_parsed.empty: + # Perform SQLite UPSERT + db.save_jobs(df_parsed) + + # Mark processed in DB and session state db.mark_file_processed(file_hash, uploaded_file.name) - st.toast(f"Relatório '{uploaded_file.name}' importado e sincronizado no banco!", icon="✅") + st.session_state['last_processed_file'] = file_hash + + if not is_processed: + st.toast(f"Relatório '{uploaded_file.name}' importado e sincronizado no banco!", icon="✅") + else: + st.toast(f"Dados do relatório '{uploaded_file.name}' atualizados!", icon="🔄") + + time.sleep(0.6) + st.rerun() else: - st.info(f"O relatório '{uploaded_file.name}' já foi importado anteriormente. Os dados foram atualizados no banco.") - st.rerun() - else: - st.error("O arquivo fornecido está vazio ou mal formatado.") - + st.error("O arquivo fornecido está vazio ou mal formatado.") except Exception as e: st.error(f"Erro ao processar arquivo: {str(e)}") diff --git a/report_gen.py b/report_gen.py index 5f2bac4..1944a16 100644 --- a/report_gen.py +++ b/report_gen.py @@ -2,6 +2,18 @@ from fpdf import FPDF import datetime import hashlib +def clean_str(val, default="N/A"): + """ + Safely casts database records to strings, checking for None and float NaN. + """ + if val is None: + return default + # check if float NaN + if isinstance(val, float) and val != val: + return default + s = str(val).strip() + return s if s else default + class NetBackupMitigationPDF(FPDF): """ Sleek, brand-aligned A4 layout representing the Veritas dark tech design system. @@ -151,12 +163,12 @@ def generate_mitigation_pdf(jobs_list, filter_name="Consolidated"): fill_row = False for j in failures_list: jid = j['job_id'] - client = j['client'] or 'N/A' - policy = j['policy'] or 'N/A' + client = clean_str(j['client']) + policy = clean_str(j['policy']) code = j['exit_code'] reex = 'Reexecutado' if j['is_rerun_success'] == 1 else 'Pendente' - status = j.get('status', 'Pendente') - action = j.get('action_taken', '') or 'Nenhuma nota registrada.' + status = clean_str(j.get('status'), 'Pendente') + action = clean_str(j.get('action_taken'), 'Nenhuma nota registrada.') # String safety trims if len(client) > 20: client = client[:18] + '..'