Build aggregation summary metrics data model
epic-bufdir-data-aggregation-ui-task-004 — Define the AggregationSummaryData model that the AggregationSummaryWidget will render: participant counts (total, deduplicated), activity hours broken down by Bufdir category, geographic distribution by region and chapter, unmapped activity type list with counts, and deduplication anomaly summary. Ensure the model is serialisable from the BLoC AggregationComplete payload.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Place all model classes in lib/features/bufdir/aggregation/models/. Use a separate file per class for clarity, with a barrel export from models.dart. Align the fromJson field names exactly with the column names returned by the generate_bufdir_report RPC — coordinate with the backend task owner (task-003 / task-006) to confirm the exact JSON shape before finalising the parser. Use List
[]) for list fields to safely handle null or absent arrays. For Map
Testing Requirements
Unit tests using flutter_test. Test file: test/features/bufdir/aggregation/models/aggregation_summary_data_test.dart. Test cases: (1) fromJson with a complete valid payload — all fields populated correctly; (2) fromJson with missing optional fields — defaults to empty lists or zero counts without throwing; (3) toJson produces a map that fromJson can consume to produce an equal object (round-trip); (4) two AggregationSummaryData instances with identical fields are equal via Equatable; (5) deduplicatedParticipantCount > totalParticipantCount triggers assert in debug mode (use test with expectLater and throwsAssertionError); (6) activityHoursByCategory with multiple categories is parsed into the correct map. Achieve 100% line coverage on all model classes.
For NHF with 1,400 local chapters, rendering a geographic breakdown as a flat list in the AggregationSummaryWidget would produce an unscrollable wall of data that is unusable on a mobile screen. Coordinators need to verify geographic completeness before export, but the raw chapter list is too long to review.
Mitigation & Contingency
Mitigation: Design the geographic distribution section as a collapsible two-level hierarchy: regions are shown expanded by default with their aggregate count, individual chapters are collapsed under each region and expandable on tap. Show only non-zero regions by default with a 'show empty regions' toggle.
Contingency: If the hierarchical display is too complex to implement before the submission deadline, fall back to a region-only summary view with a total count of active chapters per region and a note that chapter-level detail is available in the full export file.
The aggregation BLoC must handle a multi-stage async pipeline with partial success states, warning accumulation across stages, and retry logic for individual failed stages. If the state model is too simplistic (e.g., loading/success/error), warning states and partial results will be lost on retry, confusing coordinators who see warnings disappear and reappear.
Mitigation & Contingency
Mitigation: Model the BLoC state as a rich sealed class hierarchy: AggregationIdle, AggregationRunning(stage, completedStages, accumulatedWarnings), AggregationComplete(result, warnings), AggregationFailed(failedStage, accumulatedWarnings, error). Accumulated warnings persist across retry attempts so coordinators see the full warning history.
Contingency: If state management complexity causes repeated bugs near the deadline, simplify to a single AggregationResult value object that captures success, warnings, and error as nullable fields, and drive both UI components from that single object rather than a live BLoC stream.