Endpoint de consistência por intervalo

TLDR: Adicionar GET /v1/habits/consistency que retorna dias completos e porcentagem de consistência de um usuário num intervalo de datas.

Contexto

O frontend precisa mostrar, por semana (ou qualquer intervalo), quantos dias o usuário completou todos os hábitos e qual foi sua consistência geral no período. O campo consistency existente no HabitSerializer é por hábito individualmente; este endpoint é por usuário no intervalo.

Caso de uso principal: o frontend pede a semana inteira (01–07) mesmo estando no dia 02. O cálculo considera apenas execuções já decididas (não-pending), então no dia 02 com o dia 01 completo, a consistência é 100%.

Objetivos

  • Criar GET /v1/habits/consistency?start_date=YYYY-MM-DD&end_date=YYYY-MM-DD
  • Retornar completed_days, consistency (%), total_days, evaluated_until, expected_executions, completed_executions
  • Ignorar execuções futuras e pending do dia atual no cálculo de consistência
  • Validar o intervalo (start <= end, máximo 366 dias)

Fora de escopo

— (não registrado na spec original)

Mudanças

apps/habits/services.py

Adicionar ExecutionService.get_consistency_in_range(user, start_date, end_date): - evaluated_until = min(end_date, today) - decided = execuções com scheduled_date <= evaluated_until, excluindo (scheduled_date=today AND status='pending') - consistency = round(completed / expected * 100, 1) — 0.0 se expected = 0 - completed_days = dias em que total == done AND pending == 0 - total_days = (end_date - start_date).days + 1 (usa o intervalo planejado original)

apps/habits/serializers/consistency.py (novo)

ConsistencyRangeQuerySerializer: - Campos: start_date, end_date (DateField) - Validações: start <= end, intervalo <= 366 dias, end_date pode ser futuro

apps/habits/views.py

Adicionar ConsistencyRangeView(APIView): - Valida os params com ConsistencyRangeQuerySerializer - Chama ExecutionService.get_consistency_in_range - Sem cache (o valor muda ao longo do dia conforme as execuções são completadas)

apps/habits/urls.py

python path("consistency", ConsistencyRangeView.as_view(), name="consistency-range"), Inserir antes de executions/<str:date> para evitar conflito de matching.

apps/habits/tests/test_consistency_range.py (novo)

Testes Triple-A cobrindo: - Semana em execução: dia 01 completo, dia 02 todo pending → consistency=100, completed_days=1, total_days=7 - Semana em execução com o dia atual parcial (alguns decididos, 1 pending restante) - Intervalo totalmente no futuro → consistency=0.0, sem divisão por zero - Happy path com mix de dias no passado - Hábito semanal (weekly_days) — só os dias esperados entram - Intervalo de 1 dia - Validação: start > end → 400 - Validação: intervalo > 366 dias → 400 - Isolamento por usuário

apps/common/management/commands/seed.py

Atualizar para criar execuções de uma semana com mix de completed/pending/not-completed, permitindo teste manual via make seed.

Como verificar

  1. make seed + GET /v1/habits/consistency?start_date=...&end_date=... retorna resposta válida
  2. Semana em execução (hoje no meio da semana): somente dias passados com tudo completo contribuem para 100%
  3. python manage.py test apps.habits.tests.test_consistency_range — todos passam

Documentação