|
|
| 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():
|
|
|
|
|
|
|
|
|
| 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
|
|
|
|
|
|
|
|
|
| df = pd.DataFrame([
|
| {
|
| "ID": r.id,
|
|
|
|
|
| "FPSO1": r.fpso1,
|
| "FPSO": r.fpso,
|
| "Data Coleta": r.data_coleta,
|
|
|
|
|
| "Especialista": r.especialista,
|
| "Conferente": r.conferente,
|
| "OSM": r.osm,
|
|
|
|
|
| "Modal": r.modal,
|
| "Quantidade Equip.": r.quant_equip,
|
| "MROB": r.mrob,
|
|
|
|
|
| "Linhas OSM": r.linhas_osm,
|
| "Linhas MROB": r.linhas_mrob,
|
| "Linhas Erros": r.linhas_erros,
|
|
|
|
|
| "Erro Storekeeper": r.erro_storekeeper,
|
| "Erro Operação": r.erro_operacao,
|
| "Erro Especialista": r.erro_especialista,
|
| "Erro Outros": r.erro_outros,
|
|
|
|
|
| "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,
|
|
|
|
|
| "Data/Hora Input": r.data_hora_input,
|
| }
|
| for r in registros
|
| ])
|
|
|
|
|
| if "Data Coleta" in df.columns:
|
| df["Data Coleta"] = df["Data Coleta"].apply(_coerce_date)
|
|
|
|
|
|
|
|
|
| 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 = 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"
|
| )
|
|
|
|
|
| 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"
|
| )
|
|
|
| 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"
|
| )
|
|
|
|
|
|
|
|
|
|
|
| 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)]
|
|
|
|
|
| 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)
|
| ]
|
|
|
|
|
|
|
|
|
|
|
| notas_texto = []
|
| if nota_input_text.strip():
|
| notas_texto = [x.strip() for x in nota_input_text.split(",") if x.strip()]
|
|
|
|
|
| notas_escolhidas = set([str(x) for x in filtro_nf_multi] + [str(x) for x in notas_texto])
|
|
|
| if notas_escolhidas:
|
|
|
| df = df[df["Nota Fiscal"].astype(str).isin(notas_escolhidas)]
|
|
|
|
|
|
|
|
|
|
|
| nf_series = df["Nota Fiscal"].astype(str).fillna("")
|
| contagem_nf = nf_series.value_counts(dropna=False)
|
|
|
| notas_duplicadas = contagem_nf[(contagem_nf > 1) & (contagem_nf.index != "")]
|
|
|
|
|
| df["Duplicidade Nota"] = df["Nota Fiscal"].astype(str).isin(notas_duplicadas.index)
|
|
|
|
|
| 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)
|
|
|
|
|
| if mostrar_apenas_duplicadas:
|
| df = df[df["Duplicidade Nota"] == True]
|
|
|
|
|
|
|
|
|
| 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)
|
|
|
|
|
|
|
|
|
| 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()
|
|
|
|
|
|
|
|
|