Skip to main content
Glama
README.md
# ENEM Extractor

Extrai automaticamente cada questão de um PDF de prova do ENEM e salva como uma
imagem PNG individual, organizada por ano e dia de aplicação.

Usa [PyMuPDF](https://pymupdf.readthedocs.io/) para localizar o texto `QUESTÃO NN`
por coordenadas na página e [Pillow](https://python-pillow.org/) para gerar as imagens.

## Requisitos

- Python 3.10+
- Dependências em `requirements.txt` (`PyMuPDF`, `Pillow`)

## Instalação

### Instalação rápida (recomendada)

Um único comando prepara tudo com [uv](https://docs.astral.sh/uv/): ele baixa um
Python 3.10+ isolado (sem mexer no Python do sistema — ideal para Ubuntu antigo),
cria o `.venv` e instala as dependências. Não precisa de Docker nem de rebuild a
cada alteração.

```bash
git clone <repo> && cd ExtractImagemEnem
./setup.sh
uv run enem-extractor
```

### Instalação manual

```bash
python -m venv .venv
source .venv/bin/activate      # Windows: .venv\Scripts\activate
pip install -r requirements.txt
# ou, como pacote editável (habilita o comando `enem-extractor`):
pip install -e .
```

## Uso

1. Coloque os PDFs das provas na pasta `provas/`.
2. Nomeie cada arquivo seguindo o padrão do INEP, começando por **ano** e contendo o **dia** (`D1`/`D2`):

   ```
   provas/2023_PV_impresso_D1_CD4.pdf
   provas/2023_PV_impresso_D2_CD4.pdf
   ```

3. Rode o extrator a partir da raiz do projeto:

   ```bash
   # instalação rápida (uv): usa o Python do .venv automaticamente
   uv run enem-extractor
   # instalação manual (venv ativo com `pip install -e .`):
   enem-extractor
   # ou, direto pelo módulo:
   python -m enem_extractor.main
   ```

   Por padrão o modo é **auto-detectado** pelo nome de cada arquivo (arquivos com
   `ampliada`/`superampliada` no nome usam o extractor ampliado; os demais, o
   normal). Use `--mode` para **forçar** o mesmo modo em todos os PDFs de `provas/`:

   ```bash
   uv run enem-extractor                  # auto-detecta por arquivo (padrão)
   uv run enem-extractor --mode ampliada  # força ampliada em todos de provas/
   uv run enem-extractor --mode normal    # força normal em todos de provas/
   ```

As imagens são salvas em:

```
imagens/<ano>/<dia>/questao_NNN.png
```

> No dia 2 (`D2`) a numeração das questões continua a partir de 91, seguindo a
> convenção do ENEM.

## Uso como serviço / worker

O pacote expõe uma entrada única `extract`, ideal para ser chamada por um worker:

```python
from enem_extractor import extract

# auto-detecção do tipo pelo nome do arquivo
result = extract("provas/2025_PV_impresso_D1_CD9_ampliada.pdf")

# forçando modo e pasta de saída
result = extract(pdf_path, output_dir="/tmp/out", mode="normal")

# result == {
#     "pdf": "...",
#     "mode": "ampliada",          # 'normal' | 'ampliada'
#     "output_dir": "imagens/2025/D1",
#     "images": ["imagens/2025/D1/page_1_question_1.png", ...],
# }
```

- `mode`: `"auto"` (padrão) detecta pelo nome (`ampliada`/`superampliada` → extractor
  ampliada; caso contrário normal). Use `"normal"` ou `"ampliada"` para forçar.
- `output_dir`: se omitido, deriva `imagens/<ano>/<dia>` do nome do arquivo.
- Erros: `FileNotFoundError` se o PDF não existir; `ValueError` se `mode` for inválido.

## Uso via MCP (Model Context Protocol)

O projeto inclui um servidor MCP que expõe as provas e a extração para clientes
como o Claude Code.

Instale o extra opcional:

```bash
uv sync --extra mcp
# ou, com pip:
pip install -e ".[mcp]"
```

O servidor é iniciado pelo console script `enem-extractor-mcp` (via stdio). O
arquivo `.mcp.json` (versionado) já o registra para o Claude Code:

```json
{
  "mcpServers": {
    "enem-extractor": {
      "command": "uv",
      "args": ["run", "--extra", "mcp", "enem-extractor-mcp"],
      "env": { "PROVAS_DIR": "provas" }
    }
  }
}
```

Ao abrir o projeto no Claude Code:

- **`@`** lista as provas disponíveis como *resources* (`prova://<arquivo>.pdf`,
  além do agregado `provas://list`).
- **Tools** disponíveis para o agente:
  - `list_provas()` — varre `PROVAS_DIR` ao vivo e devolve as provas com o modo
    detectado.
  - `extract_prova(name, mode="auto", output_dir=None)` — extrai a prova
    (`name` é o arquivo listado ou um caminho) e devolve
    `{pdf, mode, output_dir, images}`.

A pasta das provas é configurável pela variável `PROVAS_DIR` (default `provas`).

Notas:
- `extract_prova` é **síncrona** — provas grandes (dezenas de páginas) bloqueiam
  até concluir.
- Provas novas na pasta aparecem no `@` **após reiniciar** o servidor; o tool
  `list_provas` sempre enxerga a pasta ao vivo.

## Estrutura

```
.
├── src/enem_extractor/
│   ├── service.py     # entrada única: extract() detecta o tipo e roteia
│   ├── catalog.py     # catálogo de provas (list/resolve) para o MCP
│   ├── mcp_server.py  # servidor MCP (resources + tools)
│   ├── main.py        # CLI: varre provas/ e chama o service
│   ├── normal.py      # extração da prova padrão (layout de duas colunas)
│   └── ampliada.py    # extração da prova ampliada (coluna única)
├── tests/             # testes (unittest, sem PyMuPDF/Pillow)
├── provas/            # coloque os PDFs aqui (ignorados no Git)
├── .mcp.json          # registro do servidor MCP (Claude Code)
├── requirements.txt
├── pyproject.toml
└── README.md
```

## Notas

- **Prova padrão** (`normal.py`): detecta as duas colunas da prova, numeração
  contínua entre páginas, renderização com zoom 2x.
- **Prova ampliada** (`ampliada.py`): layout de coluna única, para as versões de
  acessibilidade. Executável diretamente via `python -m enem_extractor.ampliada`.
- A pasta `imagens/` e os PDFs em `provas/` não são versionados (veja `.gitignore`).

## Licença

MIT — veja [LICENSE](LICENSE).