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