"""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]