DICOM Operations

Status: planejamento — nenhum código escrito Criado em: 2026-04-17 Categoria: Roadmap > DICOM Relação com outros docs: feature de power-user do super admin (ver SUPER_ADMIN_REDESIGN.md), mas com complexidades técnicas próprias que justificam doc separado

1. Por que fazer

O sistema hoje trata DICOM como imutável: chega, indexa, exibe, laudam. Nenhuma operação de manipulação de conteúdo. Na prática, clínicas precisam dessas operações com alguma frequência:
  • Paciente cadastrado errado no aparelho — nome “MARIA SIVLA” em vez de “MARIA SILVA”. Precisa corrigir.
  • Paciente duplicado — mesmo paciente entrou como João A na Unidade Centro e João A. na Zona Sul. Estudos não aparecem juntos no histórico. Precisa merge.
  • Estudo em outro PACS — clínica quer puxar estudo de outro hospital (referência) para comparar. Hoje precisa enviar manualmente.
  • Enviar estudo para outro PACS — médico pede para enviar estudo para RIS externo. Hoje precisa download manual + upload.
O Orthanc suporta todas essas operações nativamente. Falta expor no produto com UI, audit, permissões e sincronização com nossa camada de metadados (PostgreSQL).

2. Operações propostas (MVP)

2.1 Modify

Alterar tags DICOM de um estudo, série ou instância. Casos típicos:
  • Corrigir PatientName, PatientID, PatientBirthDate
  • Corrigir StudyDescription, StudyDate
  • Anonimizar estudo (remover tags identificáveis — já tem endpoint Orthanc /anonymize)
NÃO alterar:
  • StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID — quebra referências internas
  • PixelData — não é uso de modify, é outra coisa (pós-processamento)

2.2 Merge de pacientes

Consolidar dois ou mais PatientIDs no sistema, quando são a mesma pessoa cadastrada com dados diferentes. Fluxo:
  1. Usuário aponta “paciente A = paciente B”
  2. Sistema atualiza todos os estudos do paciente B para apontar para paciente A
  3. Paciente B é desativado (soft delete) ou removido
  4. Estudos modificam tag PatientID e PatientName nos DICOMs (via Orthanc modify)

2.3 C-GET / Query-Retrieve (pull de outro PACS)

Pedir estudo específico de outro PACS (pareamento DICOM via Orthanc SCU). Fluxo:
  1. Operador informa: AET do PACS destino + StudyInstanceUID ou dados do paciente
  2. Orthanc executa C-FIND para localizar, depois C-MOVE (ou C-GET) para puxar
  3. Estudo chega via Orthanc e é indexado normalmente pelo nosso pipeline

2.4 C-MOVE (push para outro PACS)

Enviar estudo do nosso Orthanc para outro PACS externo. Fluxo:
  1. Operador seleciona estudo + AET de destino
  2. Orthanc executa C-MOVE
  3. Log de sucesso/erro

3. Escopo — o que NÃO entra no MVP

  • Alterar PixelData — requer ferramenta de desenho, anotação, blur. Outra feature.
  • Segmentação / AI overlays — produto separado.
  • Worklist (MWL) — gerenciar lista de trabalho do aparelho. Outra feature.
  • Forward automático — “todo estudo da modality X replica para PACS Y” automaticamente. Fase posterior.

4. Decisões pendentes

4.1 Quem pode executar cada operação?

Proposta inicial:
OperaçãoSuper AdminTenant AdminDoutor
Modify (corrigir tag)✅ (do tenant dele)
Merge pacientes✅ (aprova)
Anonymize
C-GET (pull externo)
C-MOVE (push externo)
Alternativa conservadora: só super admin executa tudo; tenant admin só solicita e super admin aprova. Mais seguro, mais operacional. Decisão pendente.

4.2 Reversibilidade — preservar original?

Opção A — Preservar: antes de modify/merge, Orthanc copia o estudo original para “trash” ou anonimiza criando novo. Reversível. Ocupa 2x storage. Opção B — Sobrescrever: modify altera in-place (Orthanc reescreve o DICOM). Não reversível. 1x storage. Opção C — Híbrida: preserva apenas se operador marcar “sensível”; default é sobrescrever. Proposta: A (preservar) — storage é barato, erro é caro. Retenção de 90 dias antes de hard-delete do original. Decisão pendente.

4.3 Audit trail — nível de detalhe?

Mínimo: quem, quando, o quê (op type), estudo afetado. Médio: + diff de tags (antes/depois) em JSON. Completo: + quem autorizou (caso de workflow de aprovação), + motivo (text field obrigatório), + IP do operador. Proposta: completo — medicina exige alta rastreabilidade. Tabela nova: dicom_operations_audit (id, user_id, tenant_id, operation_type, study_id, series_id?, instance_id?, changes_json, reason, authorized_by_user_id?, ip_address, created_at). Decisão pendente.

4.4 Sincronização com PostgreSQL após modify

Quando o DICOM muda (ex.: PatientName corrigido), nossos metadados em PG ficam stale:
  • studies.patient_name
  • patients.name
  • talvez studies.patient_uuid (se o PatientID mudou)
Proposta: re-enfileirar o estudo para o worker Python pós-operação (pacs:requeue --study-id X). Worker reindexa tudo. Problema: se a operação modificou o PatientID, cria novo patients em vez de atualizar o existente. Precisa de merge explícito antes. Decisão pendente: ordem das operações (merge de pacientes → depois modify das tags nos DICOMs vs tudo em uma transação).

4.5 C-GET vs C-MOVE: qual o mecanismo primário?

  • C-GET: mais simples. Cliente inicia, servidor envia de volta na mesma conexão. Não precisa que o servidor remoto saiba como nos chamar.
  • C-MOVE: servidor remoto precisa saber o AET de quem pede (nosso Orthanc) + nosso IP + porta. Mais setup, mas é o padrão da indústria.
Proposta: C-MOVE como primário, C-GET como fallback. Decisão pendente.

4.6 Pareamento com PACS externos: por tenant ou global?

Se a Clínica Alfa tem acordo com Hospital X (C-MOVE entre eles), a configuração (AET do Hospital X, IP, porta) vai:
  • Por tenant: cada clínica gerencia seus pares. Mais autonomia, mais flexível.
  • Global: pareamentos são cadastrados pelo super admin, tenants apenas usam. Mais controle, menos risco.
Proposta: por tenant, mas com ponto de configuração na tela de super admin (global também pode). Decisão pendente.

4.7 Operações assíncronas — UX

C-GET/C-MOVE podem demorar (estudo com 2000 imagens). Modify de estudo grande também.
  • Síncrona com loading: UI trava até terminar. Simples, mas ruim com estudos grandes.
  • Async com polling: UI dispara e volta; job ID retornado; frontend pollinga a cada 5s.
  • Async com WebSocket: UI dispara; notificação em tempo real quando termina.
Proposta: async com polling (v1), WebSocket em v2 se demanda aparecer. Decisão pendente.

4.8 Recuperação de operações falhas

O que acontece se:
  • Modify falha no meio (algumas tags alteradas, outras não)?
  • C-MOVE envia 500 de 2000 imagens e cai?
Proposta: status failed + rollback automático onde possível (modify com cópia do original), retry manual para network ops (C-MOVE/C-GET). Decisão pendente.

4.9 Workflow de aprovação — tenant admin solicita, super admin aprova?

Para ops sensíveis (merge, modify de tags “fortes” como PatientID):
  • Sem workflow: tenant admin executa direto (se tiver permissão)
  • Com workflow: tenant admin cria “pedido” → fica pendente → super admin aprova/rejeita
Proposta: sem workflow no MVP; adicionar na v2 se for demanda real. Decisão pendente.

4.10 Limites de tags modificáveis

Lista positiva (whitelist) de tags que podem ser alteradas:
  • ✅ PatientName, PatientID, PatientBirthDate, PatientSex
  • ✅ StudyDescription, StudyDate, AccessionNumber
  • ✅ SeriesDescription, Modality (controverso)
  • ❌ UIDs (quebram referências)
  • ❌ PixelData (não é “modify”, é outra coisa)
  • ❓ SOPClassUID, SOPInstanceUID, Manufacturer, etc.
Decisão pendente: whitelist exata; default deny ou default allow.

5. Dependências

Depende dePor quê
SUPER_ADMIN_REDESIGN.mdA UI dessas operações vive no painel super admin
Orthanc com REST API /studies/{id}/modify, /merge, /move, /queryJá disponível
Worker Python capaz de reindexar 1 estudoComando pacs:requeue --study-id X
Storage com retenção configurávelPara “preservar original” por N dias (§4.2)
Pareamento DICOM — AETitles de outros PACSPrecisa cadastro + teste

6. Riscos e considerações

Alterar tags de laudo/estudo é tema sensível. Qualquer operação precisa de:
  • Identificação do operador (user_id)
  • Motivo obrigatório (text field)
  • Log imutável
  • Backup do original (pelo menos temporário)
Laudos não podem ser alterados por esse fluxo — laudo tem seu próprio sistema de correção (ReportRevision).

6.2 Conflito com ingestão

Se um estudo está sendo processado pelo worker (primeira indexação) e um modify acontece nele simultaneamente, comportamento indefinido. Precisa:
  • Lock de estudo durante operação
  • Ou política “não permitir modify em estudos com indexação pendente”

6.3 Consistência multi-tenant

Modify de um estudo do Tenant A não pode afetar Tenant B. Validação de scope em toda operação.

6.4 Performance em estudos grandes

C-MOVE de estudo com 2000 imagens (ex.: CT torax-abdomen) pode demorar 5-10 minutos. UX precisa suportar isso.

7. Plano de fases

Fase 0 — Decisões (AGORA)

Fechar §4.

Fase 1 — Modify (simples)

Alteração de tags “fracas” (PatientName, StudyDescription) com whitelist limitada. Audit + reindexação.

Fase 2 — Merge de pacientes

Combinar PatientIDs + modify dos DICOMs em batch.

Fase 3 — C-MOVE para PACS externos

Pareamento + send. Estudos cadastrados manualmente.

Fase 4 — C-GET / Query-Retrieve (pull)

Buscar estudos em PACS externo e puxar para o nosso.

Fase 5 — Modify avançado

Tags “fortes” (PatientID, etc.) com workflow de aprovação e preservação do original.

8. Discussões abertas

  1. Precisa de hardware especial para SCU? — C-MOVE/C-GET exige que nosso Orthanc seja SCU configurado. Performance em NVMe vs HDD?
  2. TLS DICOM — alguns PACS externos exigem DICOM sobre TLS. Orthanc suporta via plugin. Entra no MVP ou deixa pra quem pedir?
  3. Rate limiting — quantos C-MOVE simultâneos por tenant?
  4. Dry-run — antes de executar merge de pacientes, mostrar preview (“X estudos serão afetados”)? Sempre ou opt-in?
  5. “Desfazer” modify — janela de 24h para reverter via UI? Ou só restauração manual via script de DB?
  6. Histórico de operações por estudo — ao abrir um estudo, aba “Operações DICOM” mostra tudo que foi feito ali?

9. Apêndice — Mapa de impacto (a preencher após decisões)

9.1 Backend

pendente

9.2 Frontend

pendente

9.3 Endpoints Orthanc usados

pendente

9.4 Novas tabelas / colunas

pendente