Skip to content

feat: exportar candidaturas em CSV/XLSX com agendamento recorrente #66

Description

@vitorhugo-dotnet

Contexto

Atualmente o Job Apply Tracker não possui exportação de dados. Precisamos permitir que o usuário extraia suas candidaturas manualmente e também configure exportações automáticas recorrentes.

A funcionalidade deve respeitar o isolamento por usuário: cada exportação pode conter somente os dados pertencentes ao usuário autenticado.

Objetivo

Implementar um módulo de exportação que permita:

  1. Exportar candidaturas manualmente.
  2. Escolher o formato do arquivo.
  3. Aplicar filtros antes da exportação.
  4. Configurar exportações recorrentes, como diariamente em um horário escolhido.
  5. Consultar o histórico e o resultado das exportações executadas.

Formatos iniciais

  • CSV (text/csv, UTF-8 com BOM para boa compatibilidade com Excel em português).
  • Excel/XLSX (application/vnd.openxmlformats-officedocument.spreadsheetml.sheet).

A arquitetura deve permitir a inclusão futura de JSON, PDF ou outros formatos sem duplicar toda a regra de negócio.

Exportação manual

Criar endpoint autenticado semelhante a:

POST /api/v1/exports/applications

Exemplo de payload:

{
  "format": "CSV",
  "filters": {
    "status": [],
    "applicationDateFrom": null,
    "applicationDateTo": null,
    "archived": null,
    "organization": null
  },
  "columns": []
}

O endpoint deve retornar o arquivo como attachment, com nome previsível, por exemplo:

applywell-applications-2026-07-16.csv
applywell-applications-2026-07-16.xlsx

Dados exportáveis

Incluir inicialmente os principais dados das candidaturas, quando existentes:

  • ID.
  • Nome da vaga.
  • Empresa/organização.
  • Recrutador.
  • Link da vaga.
  • Status.
  • Data da candidatura.
  • Data de criação e atualização.
  • Próximo passo/data de acompanhamento.
  • Indicador de entrevista.
  • Indicador de mensagem enviada ao recrutador.
  • Arquivada ou ativa.
  • Observações.

Dados sensíveis ou campos internos de autenticação nunca devem ser incluídos.

Exportação recorrente

Permitir que o usuário crie uma configuração de exportação automática.

Endpoint sugerido:

POST /api/v1/export-schedules
GET /api/v1/export-schedules
PUT /api/v1/export-schedules/{id}
DELETE /api/v1/export-schedules/{id}
PATCH /api/v1/export-schedules/{id}/enabled

Exemplo:

{
  "name": "Backup diário das candidaturas",
  "format": "XLSX",
  "frequency": "DAILY",
  "time": "20:00",
  "timezone": "America/Sao_Paulo",
  "enabled": true,
  "filters": {
    "archived": null
  },
  "destination": "GOOGLE_DRIVE"
}

Recorrências iniciais

  • Diária em horário definido.
  • Semanal em dia e horário definidos.
  • Mensal em dia e horário definidos.

Não armazenar apenas uma expressão cron fornecida diretamente pelo usuário. Persistir uma configuração de domínio validada e gerar internamente o agendamento, evitando cron inválido ou perigoso.

Destino da exportação recorrente

Para a primeira versão:

  • GOOGLE_DRIVE, aproveitando a integração já existente e armazenando os arquivos em uma pasta de exportações do usuário.

Deixar a modelagem preparada para destinos futuros:

  • E-mail.
  • Object storage.
  • Webhook.

O filesystem local do container não deve ser considerado armazenamento permanente.

Histórico de execuções

Persistir um registro para cada tentativa:

  • Usuário.
  • Configuração de exportação.
  • Data/hora de início e término.
  • Formato.
  • Quantidade de registros exportados.
  • Status: PENDING, RUNNING, SUCCESS, FAILED.
  • Local ou identificador do arquivo gerado.
  • Mensagem de erro sanitizada.

Endpoints sugeridos:

GET /api/v1/exports/history
GET /api/v1/exports/history/{id}
POST /api/v1/export-schedules/{id}/run-now

Requisitos técnicos

  • Reutilizar os mesmos filtros e regras de autorização usados na listagem de candidaturas.
  • Processar exportações agendadas de maneira assíncrona.
  • Impedir duas execuções simultâneas da mesma configuração.
  • Definir limite máximo de registros por exportação e comportamento para grandes volumes.
  • Usar paginação/streaming na leitura para evitar carregar toda a base em memória.
  • Sanitizar células para evitar CSV/Formula Injection em valores iniciados por =, +, - ou @.
  • Escapar corretamente delimitadores, quebras de linha e aspas no CSV.
  • Definir timezone explicitamente por configuração.
  • Registrar auditoria sem expor conteúdo sensível nos logs.
  • Aplicar rate limit na exportação manual.
  • Garantir idempotência ou lock distribuído nas execuções agendadas em ambientes com mais de uma instância.

Sugestão de arquitetura

Separar responsabilidades para não transformar um controller num monólito cerimonial:

  • ApplicationExportService: consulta e normaliza os dados.
  • ExportWriter: contrato por formato.
  • CsvExportWriter.
  • XlsxExportWriter.
  • ExportScheduleService.
  • ScheduledExportExecutor.
  • ExportDestination: contrato para destinos.
  • GoogleDriveExportDestination.

Para XLSX, avaliar Apache POI em modo streaming (SXSSFWorkbook) para reduzir consumo de memória.

Critérios de aceitação

  • Usuário autenticado consegue baixar suas candidaturas em CSV.
  • Usuário autenticado consegue baixar suas candidaturas em XLSX.
  • Filtros selecionados são respeitados na exportação.
  • Nenhum dado de outro usuário aparece no arquivo.
  • CSV abre corretamente no Excel e mantém acentos.
  • Valores potencialmente interpretados como fórmulas são neutralizados.
  • Usuário consegue criar, editar, ativar, desativar e excluir um agendamento.
  • Uma exportação diária é executada no horário e timezone configurados.
  • Arquivo recorrente é salvo no Google Drive conectado do usuário.
  • Falhas ficam registradas no histórico sem vazar dados sensíveis.
  • Usuário pode executar manualmente um agendamento com run-now.
  • Execuções duplicadas do mesmo agendamento são impedidas.
  • Testes unitários, de integração e E2E cobrem os fluxos principais.

Fora do escopo inicial

  • Importação/restauração dos dados exportados.
  • Criação de relatórios visuais em PDF.
  • Compartilhamento público dos arquivos.
  • Expressões cron arbitrárias informadas pelo usuário.
  • Envio recorrente por e-mail.

Esses itens podem ser tratados em issues separadas depois que a base da exportação estiver estável.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions