Files
TextNLPClassifierApp/specs/004-deterministic-content-selection/data-model.md
T

138 lines
5.4 KiB
Markdown

# Data Model: Deterministic Content Selection
**Branch**: `004-deterministic-content-selection` | **Date**: 2026-08-20 | **Spec**: [spec.md](spec.md)
---
## 1. Domain Entities & Value Types
```mermaid
classDiagram
class ExtractorName {
<<enumeration>>
TRAFILATURA = "trafilatura"
NEWSPAPER4K = "newspaper4k"
READABILITY = "readability"
}
class CandidateStatus {
<<enumeration>>
USABLE
DEGRADED
UNAVAILABLE
}
class ExtractorCandidate {
+ExtractorName name
+str raw_text
+str error
+CandidateStatus status
+List~str~ tokens
+Set~Tuple~ shingles
+int shingle_count
+float coverage
+float support
+float score
}
class ArticleSelectionResult {
+int article_index
+ExtractorName selected_extractor
+str selection_reason
+int active_candidates_count
+int consensus_shingles_count
+Dict~ExtractorName, ExtractorCandidate~ candidates
}
class BatchProcessingResult {
+int total_articles
+int processed_count
+Dict~str, int~ selection_distribution
+str input_file
+str output_file
}
ExtractorCandidate --> ExtractorName
ExtractorCandidate --> CandidateStatus
ArticleSelectionResult --> ExtractorName
ArticleSelectionResult --> ExtractorCandidate
BatchProcessingResult --> ArticleSelectionResult
```
---
## 2. Entity Descriptions & Fields
### `ExtractorName` (Enum / Literal)
Enumeração estrita com os três motores de extração suportados:
- `"trafilatura"`
- `"newspaper4k"`
- `"readability"`
### `CandidateStatus` (Enum)
Classificação do estado de cada extrator em um dado artigo:
- `USABLE`: Campo de texto contém string não-vazia após normalização e campo `error` é nulo/vazio.
- `DEGRADED`: Campo de texto contém string não-vazia após normalização, porém campo `error` não é nulo.
- `UNAVAILABLE`: Campo de texto é ausente, nulo, tipo diferente de string ou vazio após normalização.
### `ExtractorCandidate` (Dataclass)
Representação estruturada de um candidato durante o cálculo:
| Campo | Tipo | Descrição |
|---|---|---|
| `name` | `ExtractorName` | Identificador do motor de extração (`trafilatura`, `newspaper4k`, `readability`). |
| `raw_text` | `str \| None` | Texto bruto obtido do campo correspondente no JSON (`trafilatura.text`, `newspaper4k.text`, `readability.cleaned_text`). |
| `error` | `str \| None` | Mensagem de erro do motor, se houver (`trafilatura.error`, etc.). |
| `status` | `CandidateStatus` | Estado de viabilidade do candidato (`USABLE`, `DEGRADED`, `UNAVAILABLE`). |
| `tokens` | `list[str]` | Sequência ordenada de tokens alfanuméricos minúsculos após normalização NFKC. |
| `shingles` | `set[tuple[str, ...]]` | Conjunto de n-grams consecutivos de 5 tokens (ou 1 n-gram se $1 \le \text{tokens} \le 4$). |
| `shingle_count` | `int` | Quantidade total de shingles gerados (`len(shingles)`). |
| `coverage` | `float` | Proporção de shingles do consenso presentes no candidato ($[0.0, 1.0]$). |
| `support` | `float` | Proporção de shingles do candidato que pertencem ao consenso ($[0.0, 1.0]$). |
| `score` | `float` | Pontuação $F_1$ baseada em cobertura e suporte ($[0.0, 1.0]$). |
---
### `ArticleSelectionResult` (Dataclass)
Resultado detalhado da avaliação para um único artigo:
| Campo | Tipo | Descrição |
|---|---|---|
| `article_index` | `int` | Posição ordinal do artigo no array `articles` original (0-indexed). |
| `selected_extractor` | `ExtractorName` | Vencedor da seleção determinística (`trafilatura`, `newspaper4k`, `readability`). |
| `selection_reason` | `str` | Justificativa rastreável da escolha (ex: `"highest_score"`, `"technical_tie_smallest_shingles"`, `"no_consensus_median_shingles"`, `"single_usable_candidate"`, `"fallback_all_unavailable"`). |
| `active_candidates_count` | `int` | Número de candidatos que formaram o conjunto ativo avaliado. |
| `consensus_shingles_count` | `int` | Quantidade de shingles no conjunto de consenso. |
| `candidates` | `dict[ExtractorName, ExtractorCandidate]` | Dicionário com o detalhamento de cada um dos 3 motores. |
---
### `BatchProcessingResult` (Dataclass)
Sumário da execução do lote:
| Campo | Tipo | Descrição |
|---|---|---|
| `total_articles` | `int` | Total de artigos encontrados no arquivo de entrada. |
| `processed_count` | `int` | Total de artigos processados e enriquecidos com sucesso. |
| `selection_distribution` | `dict[str, int]` | Contagem de seleções por motor (`{"trafilatura": X, "newspaper4k": Y, "readability": Z}`). |
| `input_file` | `str` | Caminho do arquivo lido. |
| `output_file` | `str` | Caminho do arquivo gerado de forma atômica. |
---
## 3. JSON Schema Mapping
### Entrada
- Raiz: Objeto contendo chave `articles: list[dict]`.
- Cada item em `articles`:
- `trafilatura` (objeto opcional): `{ "text": str | null, "error": str | null, ... }`
- `newspaper4k` (objeto opcional): `{ "text": str | null, "error": str | null, ... }`
- `readability` (objeto opcional): `{ "cleaned_text": str | null, "error": str | null, ... }`
### Saída
- Mesma estrutura exata da entrada, preservando 100% dos dados anteriores e ordem da lista `articles`.
- Em cada item de `articles`, adição/atualização da chave:
```json
"selected_extractor": "newspaper4k" | "readability" | "trafilatura"
```