portal_publico/portal_api/nao_conformidades/modelos.py

89 lines
2.5 KiB
Python

"""Dataclasses puras (sem ORM) do resultado de parsing dos dois arquivos do
Sigsistem — espelham 1:1 os campos dos models Django `NCOcorrencia`/`NCAcao`/
`NCAcompanhamento` (ver portal_api/models.py), mas sem nenhuma dependência de
banco. A view (`NaoConformidadeImportacaoViewSet.create`) é quem faz o upsert
a partir daqui."""
from __future__ import annotations
from dataclasses import dataclass, field
from datetime import date
@dataclass
class AcompanhamentoExtraido:
data: date
autor: str
texto: str
eh_prorrogacao: bool
@dataclass
class AcaoExtraida:
codigo: int
data_emissao: date | None
tipo_acao: str
acao_texto: str
data_conclusao: date | None
prazo_prorrogado: date | None
justificativa_prorrogacao: str
executor: str
emissor_acao: str
indicado_autorizar: str
responsavel_autorizacao: str
data_finalizacao: date | None
dias_finalizacao: int | None
situacao: str
fase: str
acompanhamentos: list[AcompanhamentoExtraido] = field(default_factory=list)
@property
def vencimento_efetivo(self) -> date | None:
return self.prazo_prorrogado or self.data_conclusao
@property
def ultimo_acompanhamento(self) -> AcompanhamentoExtraido | None:
if not self.acompanhamentos:
return None
return max(self.acompanhamentos, key=lambda a: a.data)
@dataclass
class OcorrenciaExtraida:
codigo: int
data_emissao: date | None
assunto: str
tipo_ocorrencia: str
pessoas_relacionadas: str
origem: str
fornecedores_relacionados: str
clientes_relacionados: str
area: str
setor: str
riscos_relacionados: str
data_relato: date | None
relato: str
emissor_relato: str
representante_gerente: str
prazo_finalizar: date | None
indicado_analise: str
tipos_causa: str
descricao_analise: str
responsavel_analise: str
data_analise: date | None
acoes: list[AcaoExtraida] = field(default_factory=list)
# True quando alguma linha do export trouxe análise preenchida numa linha
# SEM código de ação (equivalente à aba "Análise sem Ação" da skill
# original) — checado por linha durante o parsing, não pelo agregado
# `descricao_analise` (que só reflete a primeira linha da ocorrência).
tem_linha_analise_sem_acao: bool = False
@property
def sem_analise(self) -> bool:
return not self.descricao_analise.strip()
@dataclass
class ResultadoProcessamento:
ocorrencias: list[OcorrenciaExtraida]
avisos: list[str]