Annual Summaries
Data Entity
Description
Derived entity storing pre-computed yearly impact summaries for peer mentors, powering the Spotify Wrapped-style year-in-review experience. Each record aggregates a user's activities, contacts supported, events attended, hours contributed, and milestones achieved over a calendar year into a structured dataset optimized for animated slide-based presentation and social sharing.
Data Structure
| Name | Type | Description | Constraints |
|---|---|---|---|
id |
uuid |
Unique identifier for the annual summary record | PKrequiredunique |
user_id |
uuid |
Foreign key referencing the peer mentor or coordinator whose year is summarized | required |
year |
integer |
Calendar year this summary covers (e.g. 2025) | required |
organization_id |
uuid |
Organization context for tenant-scoped queries and org-specific multipliers | required |
total_activities |
integer |
Total number of logged activities during the year | required |
total_hours |
decimal |
Cumulative hours spent on peer mentor activities | required |
total_contacts_supported |
integer |
Distinct contacts the peer mentor interacted with during the year | required |
total_events_attended |
integer |
Number of events the user registered for and attended | required |
total_travel_km |
decimal |
Total kilometres travelled for peer mentor activities | - |
activity_breakdown |
json |
Activity counts grouped by type (e.g. home_visit, phone_call, group_session) for chart rendering | required |
monthly_distribution |
json |
Array of 12 monthly activity/hour totals for trend line visualization | required |
top_contacts |
json |
Array of most-frequently supported contact IDs with interaction counts (anonymized names for sharing) | - |
achievements_earned |
json |
Array of achievement/badge IDs earned during the year with award dates | - |
milestones |
json |
Key milestones reached during the year (e.g. first_activity, 100th_hour, first_event_organized) | - |
impact_metrics |
json |
Calculated impact values using org-specific multipliers: monetary equivalent, social hours value, cost savings | - |
busiest_month |
integer |
Month number (1-12) with highest activity count for highlight slide | - |
longest_streak_days |
integer |
Longest consecutive-day streak of logged activities | - |
slide_data |
json |
Pre-rendered slide configuration array for the Wrapped animation sequence, including titles, stats, and animation keys | - |
share_image_url |
text |
URL to a pre-rendered shareable summary image stored in cloud storage, with contact-identifiable info stripped | - |
status |
enum |
Generation lifecycle status of the summary | required |
data_sufficiency |
boolean |
Whether the user had enough activity data to generate a meaningful summary (minimum threshold met) | required |
generated_at |
datetime |
Timestamp when the summary aggregation was last computed | - |
viewed_at |
datetime |
Timestamp when the user first viewed their Wrapped summary | - |
shared_count |
integer |
Number of times the user shared this summary externally | required |
created_at |
datetime |
Record creation timestamp | required |
updated_at |
datetime |
Last modification timestamp | required |
Database Indexes
idx_annual_summaries_user_year
Columns: user_id, year
idx_annual_summaries_organization_year
Columns: organization_id, year
idx_annual_summaries_status
Columns: status
idx_annual_summaries_year
Columns: year
Validation Rules
valid_year_range
error
Validation failed
valid_user_reference
error
Validation failed
non_negative_metrics
error
Validation failed
valid_monthly_distribution_format
error
Validation failed
valid_status_transitions
error
Validation failed
busiest_month_within_range
error
Validation failed
generated_at_required_when_ready
error
Validation failed
Business Rules
one_summary_per_user_per_year
Each user may have at most one annual summary per calendar year, enforced by the unique index on (user_id, year)
data_sufficiency_threshold
A summary is only marked as ready if the user logged at least 5 activities and 3 hours during the year; otherwise data_sufficiency is false and the Wrapped experience shows a encouragement message instead
year_end_generation_window
Summaries are generated in a batch job during December 15 – January 15 window for the preceding year; on-demand regeneration is allowed after that window
strip_pii_from_shareable_content
Shared summary images and exported data must not contain contact names, addresses, or other personally identifiable information; only aggregate counts and anonymized statistics are permitted
tenant_isolation
Users may only view their own annual summaries; coordinators may view summaries of peer mentors within their local association scope for team-level reporting
immutable_after_share
Once a summary has been shared externally (shared_count > 0), the core metrics (totals, breakdown) should not be recalculated to maintain consistency with what was shared
organization_scoped_impact_multipliers
Impact metrics must be computed using the organization-specific hourly rate and multiplier values from organization_settings, falling back to platform defaults if none are configured