autocensup/ESPECIFICACOES.md
Rogério Lima 36d3386d2d feat: protect manual edits from API overwrite, fix pending situacao, sort CRUD lists
- Add Pessoa/Vinculo.atualizado_manualmente flag, set automatically on web CRUD
  save; all Gennera/txt import paths now skip records flagged this way instead
  of overwriting them
- Vinculo import: finalize step corrects leftover "situacao nao informada"
  vinculos to "Desvinculado", pulling carga horaria from censup_carga_horaria
- Generic CRUD list view: click a column header to sort (asc/desc), persists
  through pagination; FK columns sort by their related display field
- Rename GENNERA_API_BASE_URL setting to GENNERA_LOCAL_BASE_URL

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 15:39:36 -03:00

274 lines
9.7 KiB
Markdown

# Especificacoes do Projeto Autocensup
## Visao geral
O Autocensup e uma aplicacao web Django para apoiar a preparacao, conferencia e processamento de dados do Censo da Educacao Superior. O sistema centraliza cadastros de pessoas, cursos, vinculos, instituicoes, polos e tabelas auxiliares, permitindo importar dados locais e da API Gennera, revisar pendencias e operar os registros por ano de censo.
## Objetivos
- Manter uma base local de dados necessarios ao Censo Superior.
- Importar pessoas e vinculos a partir da API Gennera.
- Importar o Arquivo Aluno exportado do sistema Censup.
- Conferir vinculos por curso, situacao e pendencias cadastrais.
- Permitir ajustes cadastrais via telas CRUD autenticadas.
- Processar situacoes de vinculos em lote por matricula.
## Stack tecnica
- Linguagem: Python 3.12+.
- Framework web: Django 5.2.
- Banco de dados: PostgreSQL 16.
- Driver PostgreSQL: psycopg 3.
- Gerenciador de dependencias: uv.
- Infra local: Docker Compose para o servico `db`.
- Templates: Django Templates com Bootstrap/Tabler.
- Autenticacao: autenticacao padrao do Django, com views protegidas por login.
## Configuracao de ambiente
Variaveis principais:
- `DJANGO_DEBUG`: habilita ou desabilita modo debug.
- `DJANGO_SECRET_KEY`: chave secreta da aplicacao.
- `DJANGO_ALLOWED_HOSTS`: hosts permitidos.
- `POSTGRES_DB`, `POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_HOST`, `POSTGRES_PORT`: conexao PostgreSQL.
- `DATABASE_URL`: alternativa para configurar o banco por URL.
- `GENNERA_LOCAL_BASE_URL`: URL base da API Gennera.
- `API_TOKEN`: token Bearer usado nas chamadas a API Gennera.
Comandos basicos:
```bash
cp .env.example .env
UV_CACHE_DIR=/tmp/uv-cache uv sync
docker compose up -d db
UV_CACHE_DIR=/tmp/uv-cache uv run python manage.py migrate
UV_CACHE_DIR=/tmp/uv-cache uv run python manage.py loaddata ufs municipios
UV_CACHE_DIR=/tmp/uv-cache uv run python manage.py runserver
```
## Modulos principais
- `config/settings.py`: configuracao Django e leitura de variaveis de ambiente.
- `core/models.py`: modelos de dominio.
- `core/views.py`: dashboard, selecao de ano, importacoes, processamento em lote e CRUD generico.
- `core/importers.py`: importacao de arquivos, CSVs e API Gennera.
- `core/validation.py`: validacoes de curso, pessoa e vinculo.
- `core/year.py`: ano de censo selecionado na sessao.
- `templates/`: layout e telas da aplicacao.
- `docs/`: insumos e regras de leiaute do Censo Superior.
## Modelo de dados
### AnoCenso
Representa o ano de referencia do censo. O campo `ano` e chave primaria. A propriedade `ano_execucao` retorna `ano + 1`.
### UF, Pais e Municipio
Tabelas auxiliares usadas nos cadastros de pessoa. Municipio pertence a uma UF.
### Curso
Cadastro de cursos identificados por `codigo_mec`. Campos relevantes:
- descricao.
- situacao de funcionamento.
- indicador de licenciatura.
- indicador de EaD.
- carga horaria total.
- polo vinculado, quando aplicavel.
### FallbackCurso
Mapeia textos divergentes entre dados externos e cursos locais para uso cadastral/manual. A importacao de vinculos da API Gennera nao usa fallback por nome: o curso deve ser identificado exclusivamente por `course_code`.
### Instituicao e Polo
Representam instituicoes INEP e locais de oferta. Polos podem ser vinculados a cursos EaD.
### Pessoa
Cadastro do aluno/pessoa com CPF unico, dados de nascimento, nacionalidade, cor/raca, pais de origem, deficiencias, tipo de escola do ensino medio e anos de censo associados.
### Vinculo
Relaciona pessoa, curso e ano de censo. Regras estruturais:
- `ano_censo` referencia `AnoCenso`.
- `curso` referencia `Curso.codigo_mec`.
- uma pessoa pode ter apenas um vinculo para o mesmo curso no mesmo ano de censo.
- uma pessoa pode ter mais de um vinculo no mesmo ano de censo quando os cursos forem diferentes.
- a regra acima e garantida por restricao unica em pessoa, curso e ano de censo.
- vinculos sao filtrados pelo ano de trabalho selecionado em telas de consulta.
Situacoes suportadas:
- Nao informada.
- Cursando.
- Matricula trancada.
- Desvinculado do curso.
- Transferencia interna.
- Formado.
- Falecido.
## Funcionalidades web
### Dashboard
Rota: `/`
Exibe vinculos agregados por curso para o ano selecionado, com contadores por situacao e quantidade de registros pendentes.
### Selecao de ano de censo
Rota: `/ano-censo/`
Atualiza na sessao o ano de trabalho. Esse ano e usado para filtrar vinculos em dashboard, listagens e processamento de situacao.
### Importacao do Arquivo Aluno
Rota: `/importar/alunos/`
Recebe arquivo `.txt` do Censup e processa registros:
- `40`: cabecalho.
- `41`: dados cadastrais de pessoa.
- `42`: vinculo do aluno com curso.
O resultado informa pessoas criadas/atualizadas, vinculos criados/atualizados, registros ignorados e erros.
### Importacao de pessoas Gennera
Rota: `/importar/pessoas-gennera/`
Consulta a API Gennera por ano de referencia e cria pessoas ainda nao existentes na base local.
### Importacao de vinculos Gennera
Rota: `/importar/vinculos-gennera/`
Consulta a API Gennera por CPF e ano selecionado, criando ou atualizando vinculos locais conforme regras de situacao.
Endpoint esperado:
```text
GET /vinculo/?ano=<ano>&cpf=<cpf>
```
O curso do vinculo deve ser resolvido exclusivamente pelo campo `course_code`, correspondente a `Curso.codigo_mec`. Se `course_code` nao vier na resposta ou nao existir em cursos locais, o registro deve ser rejeitado com erro.
### Processamento de situacao em lote
Rota: `/processar/situacao/`
Recebe uma lista de matriculas e uma situacao alvo. Atualiza vinculos do ano selecionado e informa matriculas nao encontradas.
### Pendencias
Rota: `/pendencias/`
Lista registros com campos obrigatorios ou regras de consistencia pendentes, usando as validacoes de `core/validation.py`.
### CRUD generico
Rotas:
- `/cadastros/<slug>/`
- `/cadastros/<slug>/novo/`
- `/cadastros/<slug>/<pk>/`
- `/cadastros/<slug>/<pk>/editar/`
- `/cadastros/<slug>/<pk>/excluir/`
Cadastros disponiveis:
- anos do censo.
- UFs.
- paises.
- municipios.
- cursos.
- fallbacks de curso.
- instituicoes.
- polos.
- pessoas.
- vinculos.
## Regras de validacao
### Curso
- Codigo MEC e descricao sao obrigatorios.
- Situacao de funcionamento e obrigatoria.
- Carga horaria total deve ser maior que zero.
- Curso EaD deve ter polo vinculado.
### Pessoa
- Nome, CPF, data de nascimento, cor/raca, nacionalidade, pais de origem, indicador de deficiencia e tipo de escola do ensino medio sao obrigatorios.
- CPF deve conter 11 digitos numericos.
- Brasileira nata deve ter pais `BRA` e UF de nascimento.
- Brasileira naturalizada ou estrangeira nao pode ter pais `BRA` nem UF/municipio de nascimento.
- Municipio de nascimento exige UF e deve pertencer a UF informada.
- Quando o indicador de deficiencia for Sim, todos os tipos de deficiencia devem estar preenchidos e ao menos um deve estar marcado.
### Vinculo
- Ano do censo e situacao sao obrigatorios.
- Semestre de ingresso deve seguir o formato `01AAAA` ou `02AAAA`.
- Curso presencial exige turno do aluno.
- Curso EaD exige polo no vinculo ou no curso.
- Curso de licenciatura exige informacoes de Aluno PARFOR e segunda licenciatura/formacao pedagogica.
- Carga horaria total do aluno deve vir do curso.
- Vinculo formado exige carga horaria integralizada maior ou igual a carga horaria total.
- Indicadores de financiamento, apoio social, atividade extracurricular e acao afirmativa devem ter os tipos correspondentes preenchidos conforme a opcao principal.
## Regras de importacao de vinculos
- A decisao de situacao e feita por pessoa, curso e ano de censo.
- Registros retornados pela API para outro ano sao ignorados na decisao do ano selecionado.
- Se a carga horaria acumulada for maior ou igual a carga horaria total do curso, a situacao deve ser Formado.
- Vinculo ja Formado nao deve ser rebaixado por outra regra de importacao.
- Se houver registro no segundo semestre do ano selecionado, a situacao deve ser Cursando.
- Se houver registro apenas no primeiro semestre do ano selecionado, a situacao deve ser Desvinculado.
- Se nao houver vinculo retornado para pessoa que ja possui vinculo local no ano, a situacao local deve ser Desvinculado, exceto quando ja estiver Formado.
- A carga horaria acumulada retornada pela API deve atualizar a carga horaria integralizada mesmo quando a situacao Formado for preservada.
- O campo `semestre_ingresso` deve ser atualizado a partir do registro mais antigo do grupo de vinculos, considerando ano e semestre. O formato gravado e `AAYYYY`, por exemplo `012026` para primeiro semestre de 2026.
## Dados de apoio
Arquivos relevantes em `docs/`:
- `REGRAS_ARQUIVO_ALUNO.md`: regras do leiaute do Arquivo Aluno.
- `aluno_exportacao.txt`: exemplo ou insumo de arquivo aluno.
- `cursos.csv` e `cursos_locais.csv`: insumos de cursos.
- `leiaute_aluno_2025.xlsx`: leiaute oficial usado como referencia.
- `tabela_municipio_2025.xlsx`, `tabela_paises_2025.xlsx`, `tabela_uf_2025.xlsx`: tabelas auxiliares.
## Fluxo operacional sugerido
1. Conferir e atualizar cadastros de cursos.
2. Exportar `aluno_exportacao.txt` no sistema Censup.
3. Importar o Arquivo Aluno no Autocensup.
4. Importar pessoas e vinculos da API Gennera quando necessario.
5. Revisar dashboard e pendencias.
6. Processar ajustes de situacao por matricula, se aplicavel.
7. Corrigir cadastros pendentes via CRUD.
## Testes
Executar a suite:
```bash
UV_CACHE_DIR=/tmp/uv-cache uv run python manage.py test
```
Verificar migracoes pendentes:
```bash
UV_CACHE_DIR=/tmp/uv-cache uv run python manage.py makemigrations --check --dry-run
```
## Observacoes de schema
O campo `Vinculo.ano_censo` e uma `ForeignKey` para `AnoCenso`, mas usa explicitamente a coluna fisica `ano_censo` no banco por compatibilidade com dados existentes. Essa decisao evita perda de dados e mantem o ORM alinhado ao schema atual.