derived PK: id 17 required 2 unique

Description

A derived, precomputed year-in-review record for a single peer mentor, aggregating their activity counts, hours, contacts reached, events attended, achievements earned, and engagement streaks for a given calendar year. Powers the Spotify Wrapped-style 'Annual Summary (Wrapped)' feature and the Advantage Calculator impact metrics.

20
Attributes
4
Indexes
6
Validation Rules
8
CRUD Operations

Data Structure

Name Type Description Constraints
id uuid Primary key — unique identifier for this summary record
PKrequiredunique
user_id uuid Foreign key referencing the peer mentor this summary belongs to
required
organization_id uuid Foreign key referencing the organization the user belonged to during this year. Enables org-scoped aggregate queries and multi-tenant isolation.
required
year integer The calendar year this summary covers (e.g. 2024). Combined with user_id this forms a natural unique key.
required
total_activities integer Total number of approved activity records logged by the user during the year
required
total_hours decimal Sum of all activity durations in hours for the year, rounded to 1 decimal place
required
total_contacts_reached integer Count of distinct contact IDs referenced across all activities in the year
required
total_events_attended integer Count of event registrations with confirmed attendance for the year
required
activity_type_breakdown json JSON object mapping each activity type key to its count for the year. Example: {"home_visit": 42, "phone_call": 28, "group_activity": 15}. Used to render the breakdown slide in the Wrapped screen.
required
top_activity_type string The activity type key with the highest count. Derived from activity_type_breakdown at compute time and stored for fast retrieval.
-
monthly_breakdown json JSON array of 12 objects, one per calendar month, each containing {month: 1-12, activity_count: int, hours: decimal}. Used to render the monthly trend slide.
required
longest_streak_days integer The longest consecutive streak of active days (days with at least one logged activity) in the year
required
achievements_earned_count integer Total number of achievement badges earned during the year. Denormalized from achievements table for fast rendering.
required
achievement_ids json JSON array of achievement IDs earned during the year. Used to render the achievements slide. Example: ["first-home-visit", "50-activities"]
required
impact_score decimal Calculated composite impact metric combining hours, contact reach, and activity diversity. Used by the Advantage Calculator for impact visualization.
-
share_token string A short random token used to construct a public shareable URL for the summary without exposing the user_id. Set when the user first shares their summary.
unique
computed_at datetime Timestamp of when the aggregation job last computed or recomputed this summary record
required
is_published boolean Whether this summary has been made accessible to the user in the app. False while year is still in progress or while feature flag is disabled for the org.
required
created_at datetime Record creation timestamp
required
updated_at datetime Record last-updated timestamp, set on every recompute
required

Database Indexes

idx_annual_summary_user_year
btree unique

Columns: user_id, year

idx_annual_summary_org_year
btree

Columns: organization_id, year

idx_annual_summary_share_token
btree unique

Columns: share_token

idx_annual_summary_year
btree

Columns: year

Validation Rules

user_id_must_reference_active_user error

Validation failed

year_must_be_valid_calendar_year error

Validation failed

numeric_fields_must_be_non_negative error

Validation failed

monthly_breakdown_must_have_12_entries error

Validation failed

activity_type_breakdown_must_be_valid_json_object error

Validation failed

share_token_must_be_unique_if_set error

Validation failed

Business Rules

one_summary_per_user_per_year
on_create

Exactly one annual_summary record may exist per (user_id, year) pair. The summary-aggregation-service uses an upsert pattern: insert on first compute, update on recompute. The unique index on (user_id, year) enforces this at the database level.

feature_flag_gates_publication
always

is_published must remain false until the feature flag for Annual Summary is enabled for the user's organization. The annual-summary-service checks the feature flag before returning data to the UI; wrapped-summary-screen never renders if the flag is off.

derived_data_only_from_approved_activities
on_create

total_activities and total_hours must only aggregate activities with status='approved'. Pending or rejected activities must be excluded. This ensures Wrapped metrics reflect verified contributions, consistent with Bufdir reporting.

org_tenant_isolation
always

All reads and aggregation queries must be scoped to the user's organization_id. The annual-summary-service and annual-summary-repository must enforce this to prevent cross-tenant data leakage in multi-tenant deployments.

share_token_set_on_first_share
on_update

share_token is null by default and is only generated and persisted when the user first triggers sharing via share-export-service. Once set, it is immutable.

Enforced by: Share Export Service
share_content_must_exclude_contact_identifiable_data
always

Any publicly shareable representation of the summary (via share_token URL or exported image) must strip all contact names, IDs, and relationship data. Only aggregated metrics (counts, hours, badges) may be shared. Enforced by share-export-service at render time.

Storage Configuration

Storage Type
primary_table
Location
main_db
Partitioning
No Partitioning
Retention
Permanent Storage