Skip to content

Location breakdowns and id references in aggregated V2 endpoints - #1122

Merged
idormenco merged 1 commit into
mainfrom
feat/location-aggregates
Oct 18, 2026
Merged

idormenco merged 1 commit into
mainfrom
feat/location-aggregates

Conversation

@idormenco

Copy link
Copy Markdown
Contributor

Summary

Reworks the aggregated V2 endpoints for form submissions (:aggregatedV2), incident reports and citizen reports (:aggregated-submissionsV2). This changes the V2 response shape. The web app still uses V1, which is unchanged.

Response shape

submissionsAggregate: {
  ...form info and totals...,
  locations:   [{ id, level, parentId, level1..level5, submissionCount,
                  totalNumberOfQuestionsAnswered, totalNumberOfFlaggedAnswers, pollingStationsCount }],
  aggregates:  { <questionId>: { question, answersHistogram, submissionsHistogram, pollingStationsHistogram,
                                 answerIds | freeTextIds, noteIds, attachmentIds,
                                 locationAggregates: { <locationId>: { same fields, without question } } } },
  texts:       [{ id, submissionId, responderId, value }],
  notes:       [{ id, questionId, submissionId, text, timeSubmitted, monitoringObserverId }],
  attachments: [{ id, questionId, submissionId, fileName, presignedUrl, ... }]
}
  • Locations: one per Level1, Level1/Level2, … Level1..Level5 path, keyed by full path because names can repeat under different parents. parentId links each location to the one a level up. Submissions without a value at a level are left out of that level.
  • Per-location aggregates: each question has an aggregate for every location. It references only the texts, notes and attachments of that location's submissions.
  • New per-option counts: single select, multi select and rating aggregates add submissionsHistogram (distinct submissions) and pollingStationsHistogram (distinct polling stations) per option or value. Citizen reports have no polling stations, so they get the submissions histogram only.
  • Ids instead of inline data: texts, notes and attachments are listed once at the root with numeric ids. Question aggregates reference them by id, so large forms don't repeat question definitions or texts in every breakdown.

Query params on :aggregated-submissionsV2

  • Incident and citizen report V2 endpoints accept the same query params as V1. They're turned into filter rules and ANDed with the request filter.
  • Level and polling station number params are exact matches. V1 used ILIKE.
  • V1 incident reports always excluded reports outside polling stations. V2 includes them unless a location param is set.
  • The V1 :aggregated-submissions endpoints are marked [Obsolete].

Implementation

  • New migration AddLocationAggregates:
    • extends AggregateAnswers with the new histograms;
    • adds helper functions to number items, extract texts, reference by id and build the location breakdown;
    • recreates the three aggregate functions with Texts and Locations columns.
  • Down restores the previous functions exactly.
  • New V2 read models FormSubmissionsAggregateV2 and CitizenReportFormSubmissionsAggregateV2. The new fields on the shared answer aggregate types are omitted from the JSON when not set.

Testing

  • Unit tests added for FilterRules and for the V2 requests' query param handling. All affected test projects pass.
  • Against a local database (largest form: 3,047 submissions, 91 locations, 2,481 texts, 813 notes, 4,404 attachments):
    • location totals add up at each level, and every parent is valid;
    • per-question Level1 answer counts match the overall counts;
    • per-option submission and polling-station counts match counts computed directly from the raw submissions;
    • every referenced id exists and belongs to its question;
    • the down migration restores the previous functions.
  • Citizen reports: the combined query params plus filter return the same counts as direct SQL.
  • Incident reports had no local data; they share the form submissions code path.
  • The database call for the largest form takes about 2.5 s.

- Form submission, incident report and citizen report V2 aggregates now list the
  Level1..Level5 locations of the submissions at the root, and every question
  aggregate holds its breakdown per location id.
- Single select, multi select and rating aggregates count the distinct
  submissions and polling stations per option / value.
- Texts, notes and attachments are listed once at the root with ids, and the
  question aggregates reference them by id.
- V2 responses use dedicated read models, so V1 output is unchanged.
- :aggregated-submissionsV2 (incident and citizen reports) accepts the V1 query
  params and applies them alongside the filter; :aggregated-submissions is
  marked obsolete.
@vercel

vercel Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
votemonitor Ready Ready Preview Oct 17, 2026 12:16pm UTC

Request Review

@idormenco
idormenco merged commit dd8b86b into main Oct 18, 2026
5 checks passed
@idormenco
idormenco deleted the feat/location-aggregates branch October 18, 2026 08:13

This branch was successfully deployed

1 active deployment
Preview — 7b6f7d87 Deployed Oct 17, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant