Sistema de Análise de Tendências de Indicadores de Exames de Saúde
Propósito
Unificar relatórios de exames de saúde de múltiplos anos (PDF digital, scan, foto) em séries temporais padronizadas de indicadores, gerando relatório de análise de tendências + painel de gerenciamento de saúde interativo. Processamento 100% local, anonimização automática, extensão dinâmica (basta executar o pipeline novamente para novos relatórios).
Palavras-chave
- Relatórios de exames de saúde anuais / Relatórios de exames de saúde de múltiplos anos
- Digitalização de dados de exames / Parse de relatórios de exames
- Tendência de indicadores de exames / Análise de tendências de indicadores
- Comparação de indicadores de exames anuais / Comparação ano a ano / Variação ano a ano
- Análise de exames laboratoriais / Interpretação de exames / Análise de laudos
- Scan de relatório de exames / OCR de relatórios escaneados
- Foto de relatório de exames / Relatório fotografado / Relatório tirado com celular
- Painel de gerenciamento de saúde / Dashboard de saúde / Visualização de dados de saúde
- Tendência de glicemia / Variação de glicemia / Ácido úrico elevado / Tendência de lipídios / Tendência de pressão arterial
- Indicadores anormais de exames / Interpretação de indicadores anormais
- Adicionar novo relatório de exames / Integrar novo ano de exames
- Anonimização de dados de exames / Dataset anônimo / Versão anônima / Dataset compartilhável
Preparação do Ambiente
# Dependências (pular se já instaladas): pymupdf é obrigatório; Pillow (composição de fotos), pyobjc-framework-Vision/Quartz (OCR, apenas macOS) são opcionais
export REPORT_DIR=/caminho/para/diretório/de/exames # Diretório com os relatórios (PDFs ou pastas anuais com JPGs)
export WORK_DIR=/caminho/para/diretório/de/trabalho # Diretório para dados gerados (cria automaticamente data/ e output/)
export PY=/caminho/para/python # Recomendado usar interpretador python com as dependências
Passo 0 (obrigatório no início de cada nova sessão):
$PY scripts/check_deps.py # Verificação de dependências: o que falta, qual script afeta, como instalar
$PY scripts/check_deps.py --json # Saída legível por máquina, para automação
Código de saída 0 = dependências obrigatórias presentes, pronto para trabalhar; 1 = faltam dependências obrigatórias (fluxo principal não funciona, siga as instruções); dependências opcionais ausentes afetam apenas o formato de entrada correspondente, sem bloquear o fluxo principal de PDF digital.
Fluxo de Execução
Todos os scripts suportam --help; parâmetros têm prioridade sobre variáveis de ambiente; executar sem parâmetros = usar o comportamento padrão das variáveis de ambiente.
- Identificar formato de entrada: percorre
REPORT_DIR*.pdfcom camada de texto na raiz → Digital (parse_reports.py)*.pdfsem camada de texto na raiz → Scan (vision_ocr.py+extract.py)- Subdiretório anual
YYYY_exames/*.JPG→ Foto (make_year_pdfs.pycomposição → OCR →extract.py)
- Parse de PDF digital (método de coordenadas, limites de coluna em
references/parse_and_criteria.md):$PY scripts/parse_reports.py # → data/reports_raw.json $PY scripts/parse_reports.py --year 2027 --dry-run # Teste para um ano, sem escrever no disco - Composição de foto em PDF + OCR + Extração (se existirem diretórios JPG / scans):
$PY scripts/make_year_pdfs.py --year 2015-2020 # Múltiplos JPGs de um ano → PDF único (correção automática de rotação para fotos horizontais) $PY scripts/vision_ocr.py <pdf> data/ocr_2022.json # Scan/foto OCR → JSON com coordenadas $PY scripts/extract.py --year 2022 # Resultado OCR → indicadores (estratégia automática) $PY scripts/extract.py --years 2015-2020 # OCR de múltiplos anos em formato dict (abreviações + faixa de referência inferida)extract.pyé parametrizado por ano, sem necessidade de alterar código para novos anos: coloquedata/ocr_<ano>.jsone execute com--year <ano>. Estratégia padrãoauto(se o JSON de entrada tem dict no nível superior →abbr, se é lista →row), pode forçar com--strategy. - Normalização + Construção do dataset (mapeamento de nomes/unidades, filtragem de dados sujos, injeção automática de OCR, geração de versão anônima):
Gera$PY scripts/build_dataset.pydata/dataset_std.json(versão completa, controlada localmente) +data/anonymized/anonymized_dataset_anon_shareable.json(versão anônima, subdiretório independente + permissão somente leitura 444 + compartilhável) - Análise de tendências (julgamento de anormalidade com base na faixa de referência do ano, variação significativa ≥20% ou ≥30% da largura da faixa de referência, melhora/piora, foco nos últimos três anos):
$PY scripts/trend_analysis.py # Padrão: últimos três anos $PY scripts/trend_analysis.py --focus-years 2020,2023,2026 - Gerar entregáveis:
$PY scripts/report_generator.py # output/relatorio_analise_tendencias_exames.md (padrão **ativa** anonimização do texto) $PY scripts/build_dashboard.py # output/health_dashboard.html (arquivo único offline)
Guia de Integração de Novos Relatórios
Ao adicionar um novo relatório anual, siga o fluxo correspondente ao formato (veja detalhes em references/guia_integracao_novos_relatorios.md):
- Colocar o relatório: coloque
*.pdfna raiz deREPORT_DIR, ou crie um diretório anualYYYY_exames/com JPGs - Identificar formato: verifique a camada de texto do PDF (
len(get_text()) > 0= Digital)- Digital → execute
parse_reports.pydiretamente; se houver anomalia nos limites de coluna, imprima uma amostra de coordenadas de 1 página primeiro - Scan/Foto →
make_year_pdfs.py→vision_ocr.py→extract.py --year <ano>(não crie scripts separados por ano)
- Digital → execute
- Complementar dicionário (se houver indicador não catalogado): adicione uma linha em
NAME_MAPnoindicator_dict.py(logs emdata/unrecognized)$PY scripts/indicator_dict.py --search ácido úrico # Verificar se já está catalogado (inclui alias) $PY scripts/indicator_dict.py --validate # Auto-verificação da integridade do dicionário (alias órfãos/chaves duplicadas) $PY scripts/indicator_dict.py --count # Total atual de indicadores padrão - Reexecutar pipeline: build_dataset → trend_analysis → report_generator → build_dashboard
- Validação: confirme que o novo ano aparece em
trend_analysis.json["years"]e na linha do tempo do dashboard; verifique 3 indicadores-chave com os valores do relatório; gere e verifique a versão anônima
Regras Principais
- Restrição de privacidade rígida (tolerância zero): processamento 100% local, o skill não contém nenhuma chamada de rede externa; ao finalizar, deve gerar e verificar a versão anônima (sem nome/CPF/telefone/endereço/hospital); compartilhar apenas
data/anonymized/anonymized_dataset_anon_shareable.json(isolado em diretório separado da versão completadataset_std.json, com permissão somente leitura 444, evitando compartilhamento acidental da versão completa) - Verificação de anonimização antes de escrever em disco:
_write_anon_verified()embuild_dataset.pyé o único ponto de saída para a versão anônima – primeiro verifica em memória, só escreve em disco se aprovada. Após escrita em disco, a versão anônima é marcada como somente leitura 444. Se a verificação ocorresse após a escrita, quando houvesse PII o arquivo já estaria no disco e imutável, e o[FALHA]seria apenas um alerta posterior. É proibido contornar esta saída chamando_write_anon()diretamente;--skip-verifyé apenas para depuração, e o artefato não pode ser compartilhado - Anonimização do texto do relatório:
mask_identity_textemreport_generator.pyé ativado por padrão (mascara nome da instituição, nome do médico, telefone, CPF); use--no-maskapenas para conferência local do texto original, jamais compartilhe o artefato - Adicionar novo relatório anual: coloque em
REPORT_DIR→ reexecute os passos 2→6; indicadores não catalogados são registrados emdata/unrecognized, basta adicionar uma linha emNAME_MAPnoindicator_dict.py - Dados de OCR para visualização: indicadores extraídos por OCR são usados apenas para exibição em gráficos (marcados com Ⓞ cinza), não participam do julgamento de variação significativa, para não contaminar a estatística
- Foco da tendência: a tendência geral e a comparação primeiro/último são baseadas apenas nos anos focais (padrão: últimos três anos, pode ser sobrescrito com
--focus-years); dados históricos servem apenas como plano de fundo - Consistência de critérios: o julgamento de anormalidade usa a faixa de referência do próprio relatório do ano (adapta-se automaticamente a mudanças de reagente, por exemplo, bilirrubina direta a partir de 2025: 0.0–4.0 → 1.7–6.8)
Entregáveis
output/relatorio_analise_tendencias_exames.md— tabela de resumo de indicadores, destaque de anormalidades, interpretação autoritativa, recomendações em camadas (estilo de vida/dieta/exercício/sinais para consulta médica), metodologiaoutput/health_dashboard.html— visão geral da saúde, filtro por melhora/piora, gráficos de linha com faixa de referência, alternância de período temporal (arquivo único, sem dependências externas)data/anonymized/anonymized_dataset_anon_shareable.json— dataset anonimizado compartilhável
Lista de Scripts
| Script | Propósito | Parâmetros Principais |
|---|---|---|
check_deps.py | Verificação de dependências (Passo 0) | --json --quiet |
parse_reports.py | Parse de PDF digital | --report-dir --data-dir --year --out --dry-run |
make_year_pdfs.py | JPG anual → PDF | --report-dir --year --out-dir --dry-run |
vision_ocr.py | OCR de scan/foto | pdf out (argumentos posicionais) --dpi --quiet |
extract.py | Resultado OCR → indicadores (parametrizado) | --year --strategy --ocr-json --out --data-dir --quiet |
build_dataset.py | Normalização + versão anônima | --work-dir --data-dir --out --skip-verify --dry-run |
trend_analysis.py | Julgamento de tendências | --work-dir --focus-years --out --dry-run |
report_generator.py | Relatório Markdown | --work-dir --out --no-mask --dry-run |
build_dashboard.py | Dashboard HTML | --work-dir --data-dir --out --quiet |
indicator_dict.py | Dicionário de indicadores (importado) | --list --search --count --validate --category (apenas quando executado diretamente) |
Código de saída unificado em três estados: 0 sucesso / 1 falha em tempo de execução / 2 parâmetros ou pré-condições não atendidos.
Documentos de Referência
| Documento | Conteúdo |
|---|---|
references/guia_integracao_novos_relatorios.md | Passos completos e solução de problemas para integração de novos formatos/anos |
references/parse_and_criteria.md | Coordenadas dos limites de coluna do PDF digital, definição de critérios |
references/metodologia_ocr.md | Estratégia OCR para scan/foto, manutenção do mapeamento de abreviações |
evals/ | 8 testes de regressão (incluindo 3 linhas de defesa de privacidade), executar com eval_loop.py --run |
Exemplo de Uso (Sessão Completa)
Usuário: "Adicione o relatório de exames de 2027 e atualize a análise de tendências."
- Passo 0:
python3 scripts/check_deps.py→ dependências obrigatórias presentes - Verificar
REPORT_DIR: novo arquivoexames_270510004567.pdf(digital, camada de texto completa) - Amostra de limites de coluna: imprimir coordenadas de 1 página, consistente com
references/parse_and_criteria.md→ sem ajustes - Executar pipeline:
export REPORT_DIR=exames WORK_DIR=/caminho/para/trabalho python3 scripts/parse_reports.py python3 scripts/build_dataset.py python3 scripts/trend_analysis.py python3 scripts/report_generator.py python3 scripts/build_dashboard.py - Logs de parse: novo ano com 98 indicadores, 1 nome não reconhecido →
indicator_dict.py --searchconfirmou não catalogado, adicionar 1 linha emNAME_MAP, reexecutar build_dataset - Validação:
trend_analysis.json["years"]contém 2027; linha do tempo do dashboard mostra 2027; verificar valores de glicemia/lipídios/IMC consistentes com o relatório;grepna versão anônima sem informações de identidade - Entrega: informar caminhos do relatório e dashboard ao usuário; explicar mudanças e recomendações para cada indicador anormal em 2027
Disciplina de Custo (Obrigatória na Execução)
- Amostra primeiro: antes de analisar um novo formato, imprimir uma amostra de coordenadas de 1 página para confirmar os limites das colunas, depois processar todo o arquivo (evitar dump completo)
- Compressão de saída: comandos de depuração devem usar
| head -N/python -cpara extração precisa, proibido usar cat em arquivos grandes JSON - Isolamento de subagente: leitura de arquivos grandes/detecção de formato deve ser delegada ao subagente Explore, a thread principal recebe apenas a conclusão
- A validação deve fazer apenas uma captura de tela final; iterações intermediárias usar verificações DOM/JS
Changelog
| Versão | Data | Mudanças |
|---|---|---|
| 1.1.0 | 2026-08-29 | Reforma de engenharia centralizada: ① Todos os 10 scripts suportam --help + argparse + código de saída tri-state (antes 0/10, o pior); ② Novo script check_deps.py para verificação de dependências como Passo 0; ③ Scripts extract_2015_2020.py / extract_2022.py com ano hardcoded foram mesclados em extract.py --year parametrizado (novos anos não precisam mais alterar código, formato de saída idêntico ao dos scripts antigos campo a campo); ④ Nova seção ## Palavras-chave com 12 entradas + evals/ com 8 testes de regressão (incluindo 3 casos de teste de privacidade); ⑤ Correção de privacidade: versão anônima alterada para "verificar primeiro em memória, só escrever em disco se aprovada" (antes escrevia primeiro e depois verificava, quando havia PII o arquivo com dados de identificação já estava no disco e somente leitura, [FALHA] era apenas um alerta posterior); ⑥ Corrigido código morto em trend_analysis.py que perdia itens de medição (measurement_items era resetado após append, corrigido para deduplicar por chave) |
| 1.0.3 | 2026-08-23 | Correção P2 M-1 (segurança do skill): Isolamento forte da versão anônima – anonymized_dataset.json → data/anonymized/anonymized_dataset_anon_shareable.json (subdiretório independente + permissão somente leitura 444 + nome _anon_shareable), evitando mistura acidental com a versão completa; build_dataset.py adiciona _write_anon() (escrita atômica tmp+replace + chmod 444); caminho da declaração de privacidade no report_generator sincronizado |
| 1.0.2 | 2026-08-17 | Correção P1: build_dataset.py adiciona verify_anonymized no final com verificação automática (varredura de regex para CPF/telefone/email/nome de hospital, com erro e saída se encontrado) |
| 1.0.1 | 2026-08-17 | Correção P0: report_generator.py adiciona função mask_identity_text para anonimização (máscara de telefone/CPF/nome de médico + generalização de instituição), anonimização da camada de texto do resumo/conclusão; declaração de privacidade alinhada com a saída real |
| 1.0.0 | 2026-08-16 | Versão inicial: Sistema de Análise de Tendências de Indicadores de Exames de Saúde |