import streamlit as st import pandas as pd from sqlalchemy import create_engine import google.generativeai as genai import os from dotenv import load_dotenv import plotly.express as px # Carrega as senhas e configurações do arquivo .env load_dotenv() # ========================================== # CONFIGURAÇÃO DE PÁGINA E CABEÇALHO # ========================================== st.set_page_config(page_title="Câmara Municipal de Navegantes", page_icon="🔍", layout="wide") st.title("🔍 Câmara Municipal de Navegantes - Bem-vindo à Navegantes IA") # Subtítulo centralizado usando HTML/CSS dentro do Markdown st.markdown("

Inteligência Artificial a serviço da cidadania e da transparência

", unsafe_allow_html=True) # ========================================== # BARRA LATERAL (SIDEBAR) - CHAT AO LADO # ========================================== with st.sidebar: # 1. Logótipo da Câmara Municipal logo_path = os.path.join("assets", "logo_cmn.jpg") if os.path.exists(logo_path): st.image(logo_path, use_column_width=True) # 2. Controlos do Chat st.header("⚙️ Controles") if st.button("🗑️ Limpar Histórico de Chat", use_container_width=True, type="primary"): st.session_state.mensagens = [{"role": "assistant", "content": "Olá! O motor de análise está pronto. De qual parlamentar ou secretaria deseja visualizar a execução orçamentária?"}] st.rerun() st.divider() # 3. Seção de Prompts Sugeridos st.header("💡 Pergunte à IA da CMN") st.markdown("*Copie e cole estas perguntas no Chat ao lado e observe as respostas:*") st.info("🏆 **Visão Macro e Ranking**\n\nConsiderando todos os dados da prefeitura, faça um ranking dos 3 parlamentares que possuem o maior volume financeiro de 'saldo parado'. Qual recomendação institucional você daria à Câmara Municipal como um todo para otimizar essa execução junto ao Executivo?") st.success("🤝 **Plano de Ação e Articulação**\n\nTraga o relatório do Vereador Marcos Guarezi. Como o valor efetivamente pago está zerado, monte um plano de ação diplomático de 3 passos para o gabinete dele articular a execução técnica junto às secretarias competentes da prefeitura.") st.warning("📊 **Didática e Literacia Financeira**\n\nAnalise os dados do Vereador Wancarlos. Sabendo que o sistema está em implantação e que os ritos demoram, explique de forma didática a diferença entre o que ele tem empenhado e o que já foi pago. O que o gabinete deve observar agora?") # ========================================== # 1. INICIALIZAR O GEMINI # ========================================== GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY") genai.configure(api_key=GOOGLE_API_KEY) instrucoes_auditor = """ Você é uma Assistente Consultiva de Transparência Pública do município de Navegantes. Sua missão é traduzir dados orçamentários para o cidadão e para os parlamentares de forma elegante, clara e construtiva. DIRETRIZES RÍGIDAS DE CONDUTA: 1. FATOS ACIMA DE TUDO: Confie 100% nos números fornecidos nas tabelas. NUNCA invente dados. 2. TOM DIPLOMÁTICO: Abandone tons punitivos. Seja encorajador e institucional. Compreenda que a execução orçamentária possui ritos próprios. 3. MENSAGEM DE CONTEXTO: Inclua sempre uma linha sutil informando que "este sistema de rastreio por Inteligência Artificial está em implantação gradativa, visando auxiliar a transparência e a eficiência municipal". 4. SUGESTÕES PRÁTICAS: Ofereça caminhos construtivos. Sugira articulações entre os gabinetes e as secretarias. 5. FORMATAÇÃO: Respostas curtas, estruturadas em tópicos. Evite usar o símbolo de cifrão ($) repetidas vezes colado em números para não quebrar a formatação visual. """ modelo = genai.GenerativeModel( model_name="gemini-2.5-flash", system_instruction=instrucoes_auditor, generation_config={"temperature": 0.0} ) # ========================================== # 2. CARREGAR DADOS DAS VIEWS DO GOOGLE CLOUD # ========================================== @st.cache_data(ttl=3600) def carregar_dados_banco(): try: db_url = os.getenv("DATABASE_URL") engine = create_engine(db_url) df_vereadores = pd.read_sql_query('SELECT * FROM vw_auditoria_consolidada', engine) df_secretarias = pd.read_sql_query('SELECT * FROM vw_auditoria_secretarias', engine) df_matriz = pd.read_sql_query('SELECT * FROM vw_auditoria_vereador_secretaria', engine) return df_vereadores, df_secretarias, df_matriz except Exception as e: st.error(f"Erro ao conectar ao banco de dados: {e}") return None, None, None df_view, df_view_secretarias, df_matriz = carregar_dados_banco() # ========================================== # 3. MOTOR DE GRÁFICOS (PLOTLY) # ========================================== def renderizar_grafico_vereador(nome_vereador, df_v): dados = df_v[df_v['vereador'] == nome_vereador] if not dados.empty: df_plot = pd.DataFrame({ 'Fase da Execução': ['1. Destinado (Câmara)', '2. Empenhado', '3. Pago'], 'Valor (R$)': [ dados['total_destinado_camara'].values[0], dados['total_empenhado'].values[0], dados['total_pago'].values[0] ] }) fig = px.bar( df_plot, x='Fase da Execução', y='Valor (R$)', color='Fase da Execução', text_auto='.3s', title=f"Resumo Financeiro: {nome_vereador}", color_discrete_sequence=['#3b82f6', '#f59e0b', '#10b981'] ) st.plotly_chart(fig, use_container_width=True) def renderizar_grafico_secretarias(df_s): df_plot_s = df_s.sort_values(by='orcamento_atualizado', ascending=False) fig = px.bar( df_plot_s, x='secretaria', y='orcamento_atualizado', color='secretaria', text_auto='.3s', title="Ranking Orçamentário por Secretaria/Órgão (Orçamento Atualizado)", labels={'orcamento_atualizado': 'Valor Atualizado (R$)', 'secretaria': 'Secretaria'} ) fig.update_layout(xaxis_tickangle=-45) st.plotly_chart(fig, use_container_width=True) # ========================================== # 4. INTERFACE E LÓGICA DO AGENTE # ========================================== if df_view is not None and df_view_secretarias is not None and df_matriz is not None: if "mensagens" not in st.session_state: st.session_state.mensagens = [{"role": "assistant", "content": "Olá! O motor de análise está pronto. De qual parlamentar ou secretaria deseja visualizar a execução orçamentária?"}] for msg in st.session_state.mensagens: with st.chat_message(msg["role"]): st.markdown(msg["content"]) if pergunta := st.chat_input("Ex: Relatório da execução do Vereador Chapecó"): st.session_state.mensagens.append({"role": "user", "content": pergunta}) with st.chat_message("user"): st.markdown(pergunta) with st.chat_message("assistant"): termos_secretaria = ['secretaria', 'secretarias', 'ranking', 'por pasta', 'executivo'] solicitou_grafico_secretarias = any(termo in pergunta.lower() for termo in termos_secretaria) solicitou_ranking = "ranking" in pergunta.lower() vereador_alvo = None palavras_ignoradas = ['vereador', 'vereadora', 'professor', 'delegada', 'dr', 'dra', 'doutor', 'doutora'] for v in df_view['vereador']: partes_nome = str(v).lower().split() partes_uteis = [p for p in partes_nome if p not in palavras_ignoradas and len(p) > 3] if any(p in pergunta.lower() for p in partes_uteis): vereador_alvo = v break prompt_final = "" # Prioridade 1: Vereador Específico (Envia o Resumo + O Drill-Down da Matriz) if vereador_alvo: renderizar_grafico_vereador(vereador_alvo, df_view) dados_consolidados = df_view[df_view['vereador'] == vereador_alvo].to_markdown(index=False) # Filtragem blindada contra espaços e maiúsculas/minúsculas df_filtrado = df_matriz[df_matriz['vereador'].str.lower().str.strip() == str(vereador_alvo).lower().strip()] dados_detalhados = df_filtrado.to_markdown(index=False) prompt_final = f""" DADOS CONSOLIDADOS DO GABINETE: {dados_consolidados} DADOS DETALHADOS (GABINETE x SECRETARIAS): {dados_detalhados} PERGUNTA DO USUÁRIO: {pergunta} """ # Prioridade 2: Ranking de Secretarias elif solicitou_grafico_secretarias or solicitou_ranking: renderizar_grafico_secretarias(df_view_secretarias) prompt_final = f""" DADOS POR SECRETARIA (RANKING): {df_view_secretarias.to_markdown(index=False)} O usuário solicitou um ranking/gráfico das secretarias municipais. O gráfico já foi gerado. Redija um parecer comentando este ranking, destacando as pastas com maior Orçamento Atualizado e os maiores saldos parados. """ # Prioridade 3: Pergunta Genérica Macro else: prompt_final = f""" DADOS MACRO - POR VEREADOR: {df_view.to_markdown(index=False)} DADOS MACRO - POR SECRETARIA/ÓRGÃO: {df_view_secretarias.to_markdown(index=False)} MATRIZ COMPLETA DE CRUZAMENTO (VEREADOR x SECRETARIA): {df_matriz.to_markdown(index=False)} PERGUNTA DO USUÁRIO: {pergunta} """ with st.spinner("A redigir o parecer consultivo..."): try: resposta = modelo.generate_content(prompt_final) st.markdown(resposta.text) st.session_state.mensagens.append({"role": "assistant", "content": resposta.text}) except Exception as e: st.error(f"Erro ao consultar a IA: {e}")