From 2f2b07456a905755b6333077efe44c0fd23976dc Mon Sep 17 00:00:00 2001 From: yaMimis Date: Wed, 11 Nov 2026 11:53:53 -0300 Subject: [PATCH] =?UTF-8?q?organiza=C3=A7=C3=A3o=20de=20skill=20e=20dos=20?= =?UTF-8?q?botoes=20do=20painel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DATA.md | 6 +-- docs/DECISOES.md | 2 +- docs/RODAR_EM_QUALQUER_PC.md | 69 --------------------------- docs/ROTEIRO_DEMO.md | 7 +-- iniciar.bat | 6 --- iniciar.ps1 | 10 +++- skills/regras_negocio/painel/SKILL.md | 3 +- src/app/estilo.css | 42 ++++++++++++---- src/app/main.py | 8 ---- 9 files changed, 50 insertions(+), 103 deletions(-) delete mode 100644 docs/RODAR_EM_QUALQUER_PC.md delete mode 100644 iniciar.bat diff --git a/DATA.md b/DATA.md index 768db41..747e6a7 100644 --- a/DATA.md +++ b/DATA.md @@ -186,9 +186,9 @@ Por tempo (`data_emprestimo`) e por cliente (`DECISOES.md`, seção 4): **Abertas:** -- [ ] Campo da referência de mercado: desenho proposto pela Maria (campo opcional `bc_credito_pessoal_aa` na entrada e `contexto_mercado` na saída, frase escrita pelo código, conversão para nominal; seção 9). Falta a Yasmin passar o valor da view e mostrar o campo, e avisar todos da mudança de formato. -- [ ] Registro de decisões (seção 13): a Yasmin faz as rotas da API, a conexão com o banco e o uso da tabela no painel. Confirmar com ela que a justificativa é obrigatória, que as taxas vão em fração ao ano e que o JSON gravado é só a resposta do `prever()` e o parecer (sem a solicitação, que tem gênero e idade). -- [ ] Todos: quem já tem o banco montado aplica `db/05_decisoes_analista.sql` uma vez (seção 13). +- [x] Campo da referência de mercado: desenho proposto pela Maria (campo opcional `bc_credito_pessoal_aa` na entrada e `contexto_mercado` na saída, frase escrita pelo código, conversão para nominal; seção 9). Feito (Yasmin, 09/11): a API busca a taxa do mês na `bc_credito_pessoal_mensal` (ou na view, pelo contrato) e o painel mostra o bloco "Referência de mercado (Brasil)" no parecer. +- [x] Registro de decisões (seção 13): feito (Yasmin, 09/11). Rotas `POST /decisoes`, `GET /decisoes` e `GET /decisoes/atuais`; justificativa obrigatória; taxas em fração ao ano; o JSON gravado é só a resposta do `prever()` e o parecer (sem a solicitação, que tem gênero e idade); revisão manual grava nulos. +- [x] Todos: quem já tem o banco montado aplica `db/05_decisoes_analista.sql` uma vez (seção 13). O `iniciar.ps1` aplica sozinho a cada subida. - [ ] Samuel (08 e 09/11): `carteira_auditoria.csv` e `equidade.json`; o backtest do retorno realizado usa a `vw_retorno_realizado` como está, com as recuperações já dentro dos pagamentos (seção 14). - [ ] Marcos e Samuel (14/11): ROI em euros da carteira. - [ ] Marcos (14/11): datasheet dos dados. diff --git a/docs/DECISOES.md b/docs/DECISOES.md index 8b6cc92..99ac157 100644 --- a/docs/DECISOES.md +++ b/docs/DECISOES.md @@ -232,7 +232,7 @@ Resíduo = juro cobrado − juro justo, controlando por PD, faixa, país × ano, - [X] Baseline (adiantado de 01/11 para 31/10) - [X] Preencher os resultados da v3 - [X] Maria: proposta da v1.2 aceita (acima do justo → `revisar_preco`; `REGRAS_PARECER.md` v1.2) -- [ ] Yasmin: aprovar o formato para as telas +- [X] Yasmin: formato do `prever()` aprovado para as telas (API e painel usam os contratos de `genai/src/checagem.py`) - [X] Marcos: Euribor e taxas do BC até 01/11; view das coortes - [X] 03/11: XGBoost + calibração + SHAP - [X] **04/11: entregar o `prever()`** diff --git a/docs/RODAR_EM_QUALQUER_PC.md b/docs/RODAR_EM_QUALQUER_PC.md deleted file mode 100644 index 0210ee5..0000000 --- a/docs/RODAR_EM_QUALQUER_PC.md +++ /dev/null @@ -1,69 +0,0 @@ -# Rodar o Auditor em qualquer PC da equipe - -## Docker e Terraform: para que serve cada um - -| | Docker Compose (`iniciar.bat`) | Terraform (`infra/`) | -| --- | --- | --- | -| O que faz | sobe banco, API e painel **no seu PC** | cria **uma máquina na Google Cloud** que roda o mesmo Docker Compose | -| Quando usar | no dia a dia, nos testes e como plano B da demo | para mostrar o sistema na nuvem e a "infraestrutura como código" | -| Custo | nenhum | gasta o crédito grátis da Google enquanto a máquina existir | -| Guia | este arquivo | [`infra/README.md`](../infra/README.md) | - -**Quem roda a aplicação é sempre o Docker.** O `docker-compose.yml` descreve os 3 containers. -O Terraform não substitui o Docker: ele cria o *computador* (rede, IP, firewall e VM) onde o Docker vai rodar. -Em uma frase: **"Docker empacota a aplicação; Terraform cria a infraestrutura onde ela roda."** - -## 1. Uma vez por PC - -| O quê | De onde | -| --- | --- | -| Docker Desktop | docker.com → *Download Docker Desktop* (Windows: aceite o WSL 2). Abra e espere ficar verde. | -| Git | git-scm.com | -| Projeto | `git clone https://github.com/yaMimis/Auditor-Project.git` | -| `.env` | peça à Yasmin (tem a `GOOGLE_API_KEY` e a `CLIENTE_ID_SAL`). Nunca vai para o GitHub. | -| `modelos_salvos\pacote_prever_v1.joblib` | peça ao Samuel (Drive) | -| 4 CSVs de custo do dinheiro em `data\raw\` | peça ao Marcos (Drive), nomes abaixo | - -Os 4 CSVs (são pequenos, séries mensais do Banco Central e do Banco Central Europeu): - -- `ECB_Euribor_Serie-Mensal-12-Meses_csv.csv` (ou sem o `_csv`) -- `ECB_Euribor_Serie-Mensal-3-Meses.csv` -- `BC-Selic_Acumulada_Mes_Anuzalizada_base252.csv` -- `bcdata_sgs_20742.csv` (ou `bcdata.sgs.20742.csv`): a taxa média do crédito pessoal, que vira a "referência de mercado" no parecer - -Na primeira vez, o `iniciar` carrega esses arquivos no banco sozinho. Os mesmos 4 arquivos vão para a nuvem (passo 14 do `infra/README.md`). - -Os arquivos da aba "Auditoria da carteira" (`docs\resultados\`, gerados pelo Samuel) já vêm com o `git clone`. - -A base grande do Bondora (`LoanData`) **não** é necessária para rodar o sistema. Ela só serve para refazer -os dados e o modelo (passos 2 a 5 do README). - -## 2. Todo dia - -**Clique duplo no `iniciar.bat`** (ou `.\iniciar.ps1` no PowerShell). Ele: - -1. confere se o Docker está ligado e se as portas 5432, 8000 e 8501 estão livres; -2. confere o `.env` e o modelo; -3. sobe o banco e aplica a tabela de decisões; -4. constrói a imagem só se ela não existir; -5. indexa as normas e carrega a referência do BC, se faltarem; -6. sobe API e painel e abre http://localhost:8501. - -| Situação | Comando | -| --- | --- | -| Subir | `iniciar.bat` ou `.\iniciar.ps1` | -| Veio código novo no `git pull` | `.\iniciar.ps1 -Build` | -| Desligar (os dados ficam) | `.\iniciar.ps1 -Parar` | -| Ver o que deu errado | `docker compose logs api --tail 40` | - -## 3. Problemas comuns - -| Mensagem | O que fazer | -| --- | --- | -| "O Docker não está rodando" | Abra o Docker Desktop e espere ficar verde | -| "A porta X está com o container Y" | `docker stop Y` (o script mostra o comando) e rode de novo | -| "A porta X está ocupada pelo programa Y" | Feche o programa (o script mostra o comando) | -| "Sem a referência de mercado do BC" | Coloque os 4 CSVs em `data\raw\` e rode de novo | -| "não está assinado" / ExecutionPolicy | Use o `iniciar.bat`, ou antes: `Set-ExecutionPolicy Bypass -Scope Process` | -| "Falta modelos_salvos\...joblib" | Peça o arquivo ao Samuel; até lá, use "Caso de exemplo" | -| API demora na 1ª vez | Normal: ela baixa o modelo de busca nas normas (~0,5 GB) uma única vez | diff --git a/docs/ROTEIRO_DEMO.md b/docs/ROTEIRO_DEMO.md index a36c8dc..2a48c97 100644 --- a/docs/ROTEIRO_DEMO.md +++ b/docs/ROTEIRO_DEMO.md @@ -8,19 +8,20 @@ Demo ao vivo de cerca de **8 minutos**, no dia 15/11, seguida da mesma sequênci ## 1. Antes de começar (no dia, 30 min antes) -- [ ] Docker Desktop aberto; rodar `.\iniciar.ps1` e esperar o "Pronto". +- [ ] Docker Desktop aberto; rodar `.\iniciar.ps1`, ou `docker compose up -d` e esperar o "Pronto". - [ ] Em http://localhost:8000/: `modelo_carregavel: true` e `banco_disponivel: true`. - [ ] Fazer **uma auditoria de aquecimento** pelo formulário: a 1ª chamada ao Gemini e à busca nas normas é a mais lenta. - [ ] Preencher "Analista responsável" no topo da seção de auditoria. - [ ] Navegador em tela cheia, zoom em 90% a 100%, abas abertas: painel (8501) e documentação da API (8000/docs). - [ ] Vídeo gravado aberto em outra janela, pronto para o plano B. +- [ ] Print do `terraform plan` ("Plan: 5 to add") num slide, para mostrar a infraestrutura na nuvem sem depender da internet da sala. ## 2. Sequência | # | Tempo | Na tela | Frase-chave | Prova | |---|---|---|---|---| | 1 | 0:00 | Capa do painel | "Para cada contrato: qual o risco, se o juro é justo para esse risco e se clientes de mesmo risco pagam diferente. Quem decide é o analista." | Ideia e enquadramento | -| 2 | 0:45 | Aba **Revisão de contrato** → formulário (3 etapas) → **Executar auditoria** | "Gênero e idade ficam registrados só para auditoria: não entram no modelo nem no parecer." | Governança: atributos sensíveis só auditam | +| 2 | 0:45 | Aba **Revisão de contrato** → formulário (3 etapas, cada uma só abre depois de "Confirmo que revisei") → **Executar auditoria** (libera com as 3 caixas) | "Ninguém audita sem conferir os dados. E gênero e idade ficam registrados só para auditoria: não entram no modelo nem no parecer." | Governança: atributos sensíveis só auditam | | 3 | 1:45 | Cartões, régua e motivos do resultado | "O juro justo vem do fluxo de caixa: custo do dinheiro da época, perda esperada e margem da safra, com margem de incerteza do bootstrap." | Modelo e juro justo | | 4 | 2:45 | Parecer "Gerado por IA", fontes e referência de mercado do BC | "A IA só escreve. Recomendação e números vêm do código, e cada número do texto é conferido; se falhar, entra o template." | GenAI com checagem | | 5 | 3:45 | **Caso de exemplo → Acima do justo · 98ffaaa4-0b86** (faixa D: cobrado 39,0%, justo 34,0%, margem 31,7%–36,8%) | "Indício a investigar, não conclusão." | Linguagem de indício | @@ -58,4 +59,4 @@ Demo ao vivo de cerca de **8 minutos**, no dia 15/11, seguida da mesma sequênci | Versão | Data | Mudança | |---|---|---| -| 1.0 | 10/11/2026 | Primeira versão do roteiro. | +| 1.0 | 11/11/2026 | Primeira versão do roteiro. | diff --git a/iniciar.bat b/iniciar.bat deleted file mode 100644 index 200100e..0000000 --- a/iniciar.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -rem Clique duplo para subir o Auditor de Preco Justo (banco, API e painel). -rem Mesmo efeito de .\iniciar.ps1; repassa opcoes, ex.: iniciar.bat -Build -cd /d "%~dp0" -powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0iniciar.ps1" %* -pause diff --git a/iniciar.ps1 b/iniciar.ps1 index c40862c..f680c10 100644 --- a/iniciar.ps1 +++ b/iniciar.ps1 @@ -53,7 +53,9 @@ if ($Parar) { exit 0 } -# Portas livres? (5432 banco, 8000 API, 8501 painel). Outro container ou programa nelas impede a subida. +# Portas livres? 8000 (API) e 8501 (painel) precisam estar livres para o navegador. +# A 5432 so gera aviso: a API fala com o banco pela rede interna do Docker, entao um PostgreSQL +# instalado no Windows (comum em PC de laboratorio) nao atrapalha. $nossos = @("auditor_postgres", "auditor_api", "auditor_streamlit") $programas_docker = @("com.docker.backend", "wslrelay", "vpnkit", "com.docker.proxy", "dockerd", "docker") foreach ($porta in 5432, 8000, 8501) { @@ -63,7 +65,11 @@ foreach ($porta in 5432, 8000, 8501) { if ($con) { $proc = Get-Process -Id $con.OwningProcess -ErrorAction SilentlyContinue if ($proc -and ($programas_docker -notcontains $proc.ProcessName)) { - Parar "A porta $porta esta ocupada pelo programa '$($proc.ProcessName)'. Feche-o com: Stop-Process -Id $($proc.Id) e rode de novo." + if ($porta -eq 5432) { + Aviso "A porta 5432 tambem tem um '$($proc.ProcessName)' do Windows. Tudo bem: a API usa o banco do Docker pela rede interna." + } else { + Parar "A porta $porta esta ocupada pelo programa '$($proc.ProcessName)'. Feche-o com: Stop-Process -Id $($proc.Id) e rode de novo." + } } } } diff --git a/skills/regras_negocio/painel/SKILL.md b/skills/regras_negocio/painel/SKILL.md index 7d0bc2a..86456b9 100644 --- a/skills/regras_negocio/painel/SKILL.md +++ b/skills/regras_negocio/painel/SKILL.md @@ -84,8 +84,7 @@ Tema do Streamlit (`.streamlit/config.toml`): `base="dark"`, `primaryColor="#D4A - **[PRONTO]** as três abas, formulário, casos reais, valores legíveis dos motivos, decisão na sessão. - **[A CONFIRMAR] (Marcos)** rótulos dos códigos de escolaridade, moradia e verificação (`CODIGOS` em `componentes.py`, tirados do dicionário do Bondora). -- **[PENDENTE] (Yasmin)** mostrar "a.a. nominal" ao lado dos juros. -- **[PENDENTE] (depende de outras frentes)** `juro_pares_mediana` como referência ao lado do juro justo ("Mediana dos pares: X%", oculto quando `null`; até 09/11); `contexto_mercado` do BC no parecer; decisões gravadas no banco e marcação "já decidido" na fila; dados reais da carteira (08/11 e 09/11). +- **[PRONTO] (09/11)** "a.a. nominal" ao lado dos juros; mediana dos pares ao lado do juro justo; `contexto_mercado` do BC no parecer; decisões gravadas no banco com "Decisão atual" na fila; dados reais da carteira. ## Estado em 10/11/2026 diff --git a/src/app/estilo.css b/src/app/estilo.css index f5e7694..9181457 100644 --- a/src/app/estilo.css +++ b/src/app/estilo.css @@ -94,10 +94,29 @@ body { background-color: var(--dark-bg); color: var(--text-light); font-family: [data-testid="stButton"] button:hover, [data-testid="stDownloadButton"] button:hover { transform: translateY(-3px); border-color: var(--gold-light) !important; box-shadow: 0 12px 28px rgba(200, 154, 88, 0.12); } +/* botão principal (Revisar preço, Revisar, Abrir caso...): dourado da paleta */ [data-testid="stButton"] button[data-testid="stBaseButton-primary"], [data-testid="stButton"] button[kind="primary"] { - background: linear-gradient(135deg, #d7b9b8 0%, #c8a2a0 100%) !important; - color: #171315 !important; border: 1px solid #e5c5c3 !important; font-weight: 700 !important; + background: linear-gradient(135deg, #c89a58 0%, #e4bd7d 100%) !important; + color: #171315 !important; border: 1px solid #e4bd7d !important; font-weight: 700 !important; +} +[data-testid="stButton"] button[data-testid="stBaseButton-primary"] p, +[data-testid="stButton"] button[kind="primary"] p { color: #171315 !important; } +[data-testid="stButton"] button[data-testid="stBaseButton-primary"]:hover, +[data-testid="stButton"] button[kind="primary"]:hover { + filter: brightness(1.06); box-shadow: 0 12px 28px rgba(200, 154, 88, 0.25); +} + +/* EXECUTAR AUDITORIA DE PREÇO: branco com borda dourada */ +.st-key-audit_execute button, .st-key-audit_execute button[kind="primary"], +.st-key-audit_execute button[data-testid="stBaseButton-primary"] { + background: #ffffff !important; color: #171315 !important; + border: 2px solid var(--gold) !important; font-weight: 700 !important; letter-spacing: .06em; +} +.st-key-audit_execute button p, .st-key-audit_execute button [data-testid="stIconMaterial"] { color: #171315 !important; } +.st-key-audit_execute button:hover:not(:disabled) { + background: #fbf6ee !important; border-color: var(--gold-light) !important; filter: none; + box-shadow: 0 12px 28px rgba(200, 154, 88, 0.28); } .audit-workspace-label { color: var(--gold-light); font-size: .68rem; font-weight: 700; letter-spacing: .16em; text-transform: uppercase; margin: .15rem 0 .4rem 0; } @@ -176,18 +195,23 @@ body { background-color: var(--dark-bg); color: var(--text-light); font-family: } [data-testid="stTabs"] [data-baseweb="tab"] { height: 104px !important; min-height: 104px; width: 100%; justify-content: center; align-items: center; - padding: .55rem .35rem !important; border-radius: 10px !important; - border: 1px solid rgba(200, 154, 88, 0.34) !important; - background: linear-gradient(145deg, #202126 0%, #17181c 100%) !important; - color: var(--text-light) !important; transition: transform .2s ease, border-color .2s ease; + padding: .55rem .35rem !important; border-radius: 16px !important; + border: 1.5px solid rgba(200, 154, 88, 0.55) !important; + background: linear-gradient(145deg, #25262c 0%, #1a1b20 100%) !important; + color: var(--gold-light) !important; box-shadow: 0 10px 26px rgba(0, 0, 0, 0.32); + transition: transform .2s ease, border-color .2s ease, box-shadow .2s ease; } [data-testid="stTabs"] [data-baseweb="tab"] p { - font-size: .74rem !important; font-weight: 700 !important; letter-spacing: .1em; color: inherit !important; + font-size: .82rem !important; font-weight: 700 !important; letter-spacing: .12em; color: inherit !important; +} +[data-testid="stTabs"] [data-baseweb="tab"]:hover { + transform: translateY(-3px); border-color: var(--gold-light) !important; + box-shadow: 0 14px 30px rgba(200, 154, 88, 0.18); } -[data-testid="stTabs"] [data-baseweb="tab"]:hover { transform: translateY(-3px); border-color: var(--gold-light) !important; } [data-testid="stTabs"] [aria-selected="true"] { background: linear-gradient(135deg, #c89a58 0%, #e4bd7d 100%) !important; - color: #171315 !important; border: 1px solid #e4bd7d !important; + color: #171315 !important; border: 1.5px solid #f0d29b !important; + box-shadow: 0 14px 34px rgba(200, 154, 88, 0.35); } [data-testid="stTabs"] [data-baseweb="tab-highlight"], [data-testid="stTabs"] [data-baseweb="tab-border"] { display: none !important; } diff --git a/src/app/main.py b/src/app/main.py index c7ec96a..2f507d7 100644 --- a/src/app/main.py +++ b/src/app/main.py @@ -87,7 +87,6 @@ def md(bloco: str) -> None: st.session_state.setdefault("audit_active_step", "personal") st.session_state.setdefault("audit_last_result", None) st.session_state.setdefault("audit_last_contexto", {}) -st.session_state.setdefault("audit_last_payload", None) st.session_state.setdefault("audit_last_error", None) st.session_state.setdefault("historico", []) # contratos auditados nesta sessão st.session_state.setdefault("decisoes", []) # decisões registradas pelo analista @@ -683,12 +682,6 @@ def render_revisao(r: dict, ctx: dict) -> None: st.markdown("
", unsafe_allow_html=True) bloco_decisao(r) - with st.expander("Dados técnicos da consulta"): - if st.session_state.audit_last_payload and not ctx.get("ilustrativo"): - st.markdown("**Enviado à API**") - st.json(st.session_state.audit_last_payload) - st.markdown("**Resposta da API**") - st.json(r) # ------------------------------------------ @@ -701,7 +694,6 @@ def render_revisao(r: dict, ctx: dict) -> None: if modo == "Novo contrato (formulário)": if formulario(): payload = montar_payload() - st.session_state.audit_last_payload = payload with st.spinner("Calculando risco, juro justo e parecer..."): resposta, erro = chamar_api("/prever", payload) if erro: