""" sessao.py — a fonte única da SD entre o upload e o download. O problema que esta camada resolve: a SD é carregada uma vez, vive em memória, é editada e reavaliada — e o exportador recebe ESSE objeto, não um caminho de arquivo. Sem isto, a correção feita no formulário nunca chegaria ao JSON. O que a sessão sabe: os dados (frontmatter, como CommentedMap do ruamel para o writeback preservar comentários), a prosa, e o resultado da última análise do exportador — a prévia do payload e a lista de pendências. Pendência vazia é a única condição para exportar, e ela é reavaliada no servidor a cada POST: o botão desabilitado é conforto de tela. Estado em memória de processo, uma SD por vez: é ferramenta local de uso individual. Reiniciar o servidor descarta a sessão — o SD.md de origem continua intacto no disco, e nada é gravado sem ação explícita. """ from __future__ import annotations import sys from dataclasses import dataclass, field from datetime import datetime from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "scripts")) from exporta_sd import Pendencia, analisar # noqa: E402 from regras_sd import Canonico # noqa: E402 # --------------------------------------------------------------------------- # Estados — os que a tela precisa distinguir, e nenhum a mais # --------------------------------------------------------------------------- SEM_ARQUIVO = "sem_arquivo" COM_PENDENCIAS = "com_pendencias" PRONTA = "pronta" EXPORTADA = "exportada" ROTULO_ESTADO = { SEM_ARQUIVO: "Arquivo não enviado", COM_PENDENCIAS: "Com pendências", PRONTA: "Pronta para exportar", EXPORTADA: "JSON exportado", } @dataclass class Sessao: nome_arquivo: str dados: dict # CommentedMap do ruamel — FONTE ÚNICA, mutada pelo formulário corpo: str estado: str = COM_PENDENCIAS pendencias: list[Pendencia] = field(default_factory=list) previa: dict | None = None caminho_origem: Path | None = None ultimo_erro: str | None = None exportada_em: str | None = None def reavaliar(self, canon: Canonico) -> None: """Roda o exportador em modo de análise sobre os dados COMO ESTÃO. É grátis e instantâneo; roda a cada edição. Uma edição depois da exportação volta a sessão para pronta/pendente: o que foi exportado era outra versão dos dados, e a tela não pode dizer "exportada" sobre esta. """ self.previa, self.pendencias = analisar(self.dados, self.corpo, canon) self.estado = PRONTA if not self.pendencias else COM_PENDENCIAS self.exportada_em = None @property def pode_exportar(self) -> bool: return not self.pendencias def marcar_exportada(self) -> None: self.estado = EXPORTADA self.exportada_em = datetime.now().isoformat(timespec="seconds") def rotulo_estado(self) -> str: return ROTULO_ESTADO.get(self.estado, self.estado)