Pular para conteúdo

Normalização

Pequeno conjunto de utilitários que todo projeto OTIMUS usa. Sem eles, a comparação de nomes é quebrada (acentos, case, espaços extras).

_normalize — texto para comparação

import unicodedata

def _normalize(s: str) -> str:
    """Remove acentos e converte para UPPERCASE.

    Uso: comparações de nomes de médicos, procedimentos, convênios.
    """
    if not s:
        return ""
    nfkd = unicodedata.normalize("NFKD", s)
    ascii_text = "".join(c for c in nfkd if not unicodedata.combining(c))
    return ascii_text.upper().strip()

Antes / depois:

Input Output
"Joana Silva" "JOANA SILVA"
"Saúde Caixa" "SAUDE CAIXA"
" Priscila " "PRISCILA"
"Dr. João Pãulô" "DR. JOAO PAULO"

_only_digits — extrair dígitos de CPF/telefone

def _only_digits(s: str) -> str:
    return "".join(c for c in (s or "") if c.isdigit())

Antes / depois:

Input Output
"123.456.789-00" "12345678900"
"+55 (99) 9 9999-9999" "5599999999999"
"" / None ""

Datas — helpers bidirecionais

def _yyyymmdd_to_ddmmyyyy(s: str) -> str:
    y, m, d = s.split("-")
    return f"{d}-{m}-{y}"

def _ddmmyyyy_to_yyyymmdd(s: str) -> str:
    d, m, y = s.split("-")
    return f"{y}-{m}-{d}"

_normalize_date — aceita várias entradas

LLM às vezes devolve 15/04/2026, outras vezes 15-04-2026, outras 2026-04-15. Unificar para ISO:

import re

def _normalize_date(date_str: str) -> str:
    """Converte datas em várias formas para YYYY-MM-DD (ISO)."""
    if not date_str:
        return date_str
    s = date_str.replace("/", "-")
    # DD-MM-YYYY -> YYYY-MM-DD
    if re.match(r"^\d{2}-\d{2}-\d{4}$", s):
        dd, mm, yyyy = s.split("-")
        return f"{yyyy}-{mm}-{dd}"
    # Já é ISO?
    if re.match(r"^\d{4}-\d{2}-\d{2}$", s):
        return s
    return date_str  # deixa como veio; parseador downstream pode explodir

Timezone

Sempre em America/Sao_Paulo:

import zoneinfo, datetime

_TZ_BR = zoneinfo.ZoneInfo("America/Sao_Paulo")

def _now_br() -> datetime.datetime:
    return datetime.datetime.now(_TZ_BR)

def _today_br() -> datetime.date:
    return _now_br().date()

Usar em qualquer operação que envolva "hoje" ou "agora" — incluindo filtros de slot passado, data_inicio da consulta, etc.

Hora — canonizar

hora = (hora_exame or "").strip()[:5]   # "14:00:00" -> "14:00"

Slice fixo em [:5] pega HH:MM. Se o OTIMUS ou o LLM mandar segundos, é descartado com segurança.

Normalização de nome próprio do paciente

Nos projetos atuais não normalizamos o nome que vai para o cadastro (deixamos como o paciente digitou). Isso preserva acentos e capitalização correta no prontuário.

# certo
nome = input_paciente.strip()

# errado (virá tudo em caixa alta no OTIMUS)
nome = _normalize(input_paciente)

Validação de CPF (básica)

Projetos atuais não validam CPF (só garantem 11 dígitos). Se quiser adicionar:

def _valid_cpf(cpf: str) -> bool:
    cpf = _only_digits(cpf)
    if len(cpf) != 11 or cpf == cpf[0] * 11:
        return False
    # ... algoritmo módulo 11
    return True

Em produção, preferimos não validar algoritmicamente: CPF "estranhos" devem falhar no OTIMUS e transferir para humano.

Onde colocar

Todos esses helpers ficam em scheduling.py (ou agendamento.py) como funções privadas (_nome). Não criar módulo separado — overhead desnecessário.