import streamlit as st import pandas as pd from io import BytesIO from datetime import date from banco import SessionLocal from models import Equipamento def limpar_estado_consulta(): """ Remove do session_state qualquer dado relacionado ao módulo Consulta """ for key in list(st.session_state.keys()): if key.startswith("consulta_"): del st.session_state[key] def _coerce_date(x): """Garante que valores sejam datas (date) ou NaT para comparação.""" if pd.isna(x): return pd.NaT if isinstance(x, (pd.Timestamp, )): return x.date() if isinstance(x, date): return x try: return pd.to_datetime(x).date() except Exception: return pd.NaT def main(): # ===================================================== # 🧹 LIMPA ESTADO AO ENTRAR NO MÓDULO # ===================================================== if not st.session_state.get("_consulta_inicializado"): limpar_estado_consulta() st.session_state["_consulta_inicializado"] = True st.title("🔍 Consulta de Registros") db = SessionLocal() try: registros = db.query(Equipamento).all() if not registros: st.info("Nenhum registro encontrado.") return # ===================================================== # 🔄 CONVERTE REGISTROS EM DATAFRAME (TODOS OS CAMPOS) # ===================================================== df = pd.DataFrame([ { "ID": r.id, # Identificação "FPSO1": r.fpso1, "FPSO": r.fpso, "Data Coleta": r.data_coleta, # Responsáveis "Especialista": r.especialista, "Conferente": r.conferente, "OSM": r.osm, # Operacional "Modal": r.modal, "Quantidade Equip.": r.quant_equip, "MROB": r.mrob, # Métricas "Linhas OSM": r.linhas_osm, "Linhas MROB": r.linhas_mrob, "Linhas Erros": r.linhas_erros, # Erros "Erro Storekeeper": r.erro_storekeeper, "Erro Operação": r.erro_operacao, "Erro Especialista": r.erro_especialista, "Erro Outros": r.erro_outros, # Dados complementares "Inclusão / Exclusão": r.inclusao_exclusao, "PO": r.po, "Part Number": r.part_number, "Material": r.material, "Solicitante": r.solicitante, "Motivo": getattr(r, "motivo", None), "Requisitante": r.requisitante, "Nota Fiscal": r.nota_fiscal, "Impacto": r.impacto, "Dimensão": r.dimensao, "Observações": r.observacoes, "Dia Inclusão": r.dia_inclusao, # Auditoria "Data/Hora Input": r.data_hora_input, } for r in registros ]) # Normaliza a coluna de data para comparação correta if "Data Coleta" in df.columns: df["Data Coleta"] = df["Data Coleta"].apply(_coerce_date) # ===================================================== # 🔎 FILTROS # ===================================================== st.subheader("🔎 Filtros") col1, col2, col3 = st.columns(3) with col1: filtro_fpso = st.multiselect( "FPSO", sorted(df["FPSO"].dropna().unique()), key="consulta_fpso" ) filtro_dia = st.multiselect( "Dia de Inclusão (D1 / D2 / D3)", sorted(df["Dia Inclusão"].dropna().unique()), key="consulta_dia" ) with col2: filtro_modal = st.multiselect( "Modal", sorted(df["Modal"].dropna().unique()), key="consulta_modal" ) filtro_especialista = st.multiselect( "Especialista", sorted(df["Especialista"].dropna().unique()), key="consulta_especialista" ) # 🔵 FILTRO OSM filtro_osm = st.multiselect( "OSM", sorted(df["OSM"].dropna().unique()), key="consulta_osm" ) with col3: periodo = st.date_input( "Período de Coleta", value=None, key="consulta_periodo" ) # 🟩 NOVO: FILTRO DE NOTA FISCAL st.markdown("**Nota Fiscal**") nota_input_text = st.text_input( "Digite um ou mais números (separados por vírgula)", value="", key="consulta_nf_text" ) # Alternativamente (opcional) oferecer multiselect pelos valores existentes filtro_nf_multi = st.multiselect( "Ou selecione", sorted([str(x) for x in df["Nota Fiscal"].dropna().unique()]), key="consulta_nf_multi" ) mostrar_apenas_duplicadas = st.checkbox( "Mostrar apenas notas duplicadas", value=False, key="consulta_mostrar_dup_nf" ) # ===================================================== # 🔄 APLICA FILTROS # ===================================================== # Filtros simples if filtro_fpso: df = df[df["FPSO"].isin(filtro_fpso)] if filtro_modal: df = df[df["Modal"].isin(filtro_modal)] if filtro_especialista: df = df[df["Especialista"].isin(filtro_especialista)] if filtro_dia: df = df[df["Dia Inclusão"].isin(filtro_dia)] if filtro_osm: df = df[df["OSM"].isin(filtro_osm)] # Filtro de período (intervalo) if isinstance(periodo, (list, tuple)) and len(periodo) == 2 and all(periodo): data_inicio, data_fim = periodo df = df[ (df["Data Coleta"] >= data_inicio) & (df["Data Coleta"] <= data_fim) ] # ----------------------------------------------------- # Filtro de Nota Fiscal (texto e/ou multiselect) # ----------------------------------------------------- # Consolida as notas informadas via texto (separadas por vírgula) notas_texto = [] if nota_input_text.strip(): notas_texto = [x.strip() for x in nota_input_text.split(",") if x.strip()] # Concatena com o multiselect (transformando em string) notas_escolhidas = set([str(x) for x in filtro_nf_multi] + [str(x) for x in notas_texto]) if notas_escolhidas: # Comparar sempre como string para evitar problemas com zeros à esquerda ou tipos heterogêneos df = df[df["Nota Fiscal"].astype(str).isin(notas_escolhidas)] # ===================================================== # 🧭 SINALIZA DUPLICIDADE DE NOTA FISCAL # ===================================================== # Conta ocorrências por número (string) ignorando NaN nf_series = df["Nota Fiscal"].astype(str).fillna("") contagem_nf = nf_series.value_counts(dropna=False) # Duplicadas são as que tem contagem > 1 (e não vazias) notas_duplicadas = contagem_nf[(contagem_nf > 1) & (contagem_nf.index != "")] # Coluna booleana marcando duplicidade no DF atual df["Duplicidade Nota"] = df["Nota Fiscal"].astype(str).isin(notas_duplicadas.index) # Aviso resumido if len(notas_duplicadas) > 0: st.warning( f"⚠️ Foram encontradas **{int(notas_duplicadas.sum())}** ocorrências em **{len(notas_duplicadas)}** " f"números de Nota Fiscal duplicados no resultado." ) with st.expander("Ver lista de notas duplicadas"): dup_df = pd.DataFrame({ "Nota Fiscal": notas_duplicadas.index, "Ocorrências": notas_duplicadas.values }).sort_values(by="Ocorrências", ascending=False) st.dataframe(dup_df, use_container_width=True) # Mostrar apenas duplicadas, caso marcado if mostrar_apenas_duplicadas: df = df[df["Duplicidade Nota"] == True] # ===================================================== # 📊 RESULTADOS # ===================================================== st.subheader("📊 Resultados") st.caption("A coluna **Duplicidade Nota** indica se há mais de um registro com o mesmo número de Nota Fiscal no resultado atual.") st.dataframe(df, use_container_width=True) # ===================================================== # 📥 EXPORTAÇÃO EXCEL # ===================================================== buffer = BytesIO() with pd.ExcelWriter(buffer, engine="openpyxl") as writer: df.to_excel(writer, index=False, sheet_name="Consulta") buffer.seek(0) st.download_button( label="⬇️ Exportar para Excel", data=buffer, file_name="consulta_equipamentos.xlsx", mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ) finally: db.close()