12 KiB
Feature Specification: Fitbit/Garmin Data Sync
Feature Branch: 002-fitbit-garmin-sync
Created: 2025-12-24
Status: Implemented
User Scenarios & Testing (mandatory)
User Story 1 - Sync Activities from Garmin (Priority: P1)
As a user, I want to trigger a synchronization of my recent activities from my Garmin account so that my fitness data is stored and available within the application.
Why this priority: This is a core feature, enabling users to bring their primary workout data into the system.
Independent Test: This can be tested by a user triggering a sync and verifying their recent activities appear in their activity list.
Acceptance Scenarios:
- Given I have connected my Garmin account, When I trigger an activity sync, Then my activities from the last 30 days appear in my activity list.
- Given my Garmin account is not connected, When I attempt to trigger a sync, Then I see a message instructing me to connect my account first.
User Story 2 - View and Query Activities (Priority: P2)
As a user, I want to list my synced activities, filter them by criteria like activity type and date, and download the original activity file for my records.
Why this priority: Allows users to find, analyze, and export their workout data.
Independent Test: A user can navigate to the activity list, apply filters, and attempt to download a file.
Acceptance Scenarios:
- Given I have synced activities, When I view the activity list, Then I see a paginated list of my activities, sorted by most recent first.
- Given I have a list of activities, When I filter by "running" and a specific date range, Then I only see running activities within that range.
- Given an activity has its original file available, When I click "Download", Then the file is downloaded to my device.
User Story 3 - Sync Health Metrics from Garmin (Priority: P1)
As a user, I want to trigger a synchronization of my daily health metrics (like steps and HRV) from my Garmin account to track my wellness over time.
Why this priority: Provides users with a holistic view of their health beyond just workouts.
Independent Test: A user can trigger a metric sync and see updated data in their health dashboard or metric query views.
Acceptance Scenarios:
- Given I have connected my Garmin account, When I trigger a health metric sync, Then my metrics from the last 30 days are updated.
- Given data is synced, When I view my dashboard, Then I see my latest step count and HRV data.
User Story 4 - View and Query Health Metrics (Priority: P2)
As a user, I want to see a list of all the metric types I've synced, query my data for specific time ranges, and view a high-level summary.
Why this priority: Enables users to analyze trends and understand their health data.
Independent Test: A user can navigate to the metrics section, select a metric type and date range, and view the resulting data and summary.
Acceptance Scenarios:
- Given I have synced health metrics, When I visit the metrics page, Then I see a list of all available metric types (e.g., "Steps", "HRV").
- Given I have synced step data, When I query for "Steps" in the last week, Then I see a chart or list of my daily step counts for that period.
- Given I have synced data, When I view the health summary for the last month, Then I see accurate totals and averages for my key metrics.
Edge Cases
- What happens if the Garmin service is unavailable during a sync?
- How does the system handle a user revoking access from the Garmin side?
- What should be displayed for a day with no data for a specific metric?
- How does the system handle a sync that is interrupted mid-way?
Requirements (mandatory)
UI/UX Improvements
- UI-001: The dashboard (
/) MUST display current sync statistics (total activities, downloaded activities) and recent sync logs in a user-friendly table. - UI-002: All user interactions (e.g., triggering syncs, fetching data) MUST provide feedback via non-blocking toast notifications instead of disruptive
alert()popups. - UI-003: The dashboard MUST provide clear buttons to trigger activity sync, health metrics sync, and navigate to setup/configuration and API documentation.
- UI-004: The dashboard MUST clearly display a link to the
/setuppage for authentication.
Authentication and Setup
- AS-001: The system MUST provide an endpoint (
/setup/auth-status) to check the current authentication status for Garmin. (Fitbit status is a placeholder). - AS-002: Users MUST be able to initiate a connection to their Garmin account by providing their username and password via a
POSTto/setup/garmin. The system automatically configures the correct Garmin domain (garmin.comorgarmin.cn) based on theis_chinaflag. - AS-003: The system MUST handle Garmin's Multi-Factor Authentication (MFA) process. If MFA is required, the
/setup/garminendpoint will returnmfa_requiredstatus, and the user must complete the process by sending the verification code to/setup/garmin/mfa. - AS-004: The system MUST securely store the resulting
garthauthentication tokens (OAuth1 and OAuth2) and serializable MFA state in the database for future use, associated with the 'garmin' token type. - AS-005: The system MUST provide an endpoint (
/setup/garmin/test-token) to validate the stored Garmin authentication tokens by attempting to fetch user profile information viagarth.UserProfile.get().
Functional Requirements
- FR-001: Users MUST be able to trigger a manual synchronization of their Garmin activities via a
POSTrequest to the/api/sync/activitiesendpoint. This endpoint accepts adays_backparameter to specify the sync range. - FR-002: The system MUST fetch activity metadata from Garmin using
garth.client.connectapi("/activitylist-service/activities/search/activities", ...)and store users' Garmin activities in the database, includinggarmin_activity_id,activity_name,activity_type(extracted fromactivityType.typeKey),start_time,duration,file_type,download_status,downloaded_at, and the binaryfile_content. - FR-003: When downloading activity files, the system MUST attempt to download in preferred formats (
original,tcx,gpx,fit) sequentially until a successful download is achieved. Thegarth.client.download()method MUST be used with a dynamically constructed path like/download-service/export/{file_type}/activity/{activity_id}. - FR-004: Users MUST be able to view a paginated list of their synchronized activities via a
GETrequest to the/api/activities/listendpoint. - FR-005: Users MUST be able to filter their activities by
activity_type,start_date,end_date, anddownload_statusvia aGETrequest to the/api/activities/queryendpoint. - FR-006: Users MUST be able to download the original data file for an individual activity via a
GETrequest to the/api/activities/download/{activity_id}endpoint. - FR-007: Users MUST be able to trigger a manual synchronization of their Garmin health metrics via a
POSTrequest to the/api/sync/metricsendpoint. This endpoint accepts adays_backparameter. - FR-008: The system MUST fetch daily health metrics (steps, HRV, sleep) using
garth.stats.steps.DailySteps.list(),garth.stats.hrv.DailyHRV.list(), andgarth.data.sleep.SleepData.list()respectively. - FR-009: The system MUST store users' Garmin health metrics in the database, including
metric_type('steps', 'hrv', 'sleep'),metric_value,unit,timestamp,date, andsource('garmin'). - FR-010: Users MUST be able to see which types of health metrics are available for querying via a
GETrequest to the/api/metrics/listendpoint. - FR-011: Users MUST be able to query their health metrics by
metric_type,start_date, andend_datevia aGETrequest to the/api/metrics/queryendpoint. - FR-012: Users MUST be able to view a summary of their health data (e.g., totals, averages) over a specified time period via a
GETrequest to the/api/health-data/summaryendpoint. - FR-013: The system MUST provide clear feedback on the status of a synchronization. This includes a summary of sync status available via the
/api/statusendpoint and a detailed list of synchronization logs available via the/api/logsendpoint. - FR-014: The system MUST handle synchronization failures gracefully and log errors for troubleshooting.
- FR-015: Users MUST be able to trigger a manual synchronization of their weight data via a
POSTrequest to the/api/sync/weightendpoint. (Note: Actual implementation for weight sync is currently a placeholder).
Key Entities
- Activity: Represents a single fitness activity.
id: Unique identifier in the database.garmin_activity_id: The ID from Garmin.activity_name: User-defined name of the activity.activity_type: Type of activity (e.g., 'running', 'cycling'), extracted fromactivityType.typeKey.start_time: The start time of the activity.duration: Duration of the activity in seconds.file_type: The format of the downloaded file ('tcx', 'gpx', 'fit', or 'original').file_content: The binary content of the activity file.download_status: The status of the file download from Garmin ('pending', 'downloaded', 'failed').downloaded_at: Timestamp when the file was downloaded.
- Health Metric: Represents a single health data point for a specific day.
id: Unique identifier in the database.metric_type: The type of metric (e.g., 'steps', 'hrv', 'sleep').metric_value: The value of the metric.unit: The unit of measurement.timestamp: The precise timestamp of the metric.date: The date the metric was recorded.source: The source of the data (e.g., 'garmin').
- Sync Log: Records the outcome of each synchronization attempt.
id: Unique identifier in the database.operation: The type of sync operation ('activity_sync', 'health_metric_sync', 'weight_sync').status: The outcome ('started', 'completed', 'completed_with_errors', 'failed').message: A descriptive message about the outcome.start_time: When the sync began.end_time: When the sync completed.records_processed: Number of records successfully processed.records_failed: Number of records that failed.
- APIToken: Stores authentication tokens for third-party services.
id: Unique identifier in the database.token_type: The service the token is for ('garmin', 'fitbit').garth_oauth1_token: The OAuth1 token for Garmin (JSON string).garth_oauth2_token: The OAuth2 token for Garmin (JSON string).mfa_state: Stores serializable MFA state (JSON string) if MFA is required during login.mfa_expires_at: Timestamp indicating when the MFA state expires.
Success Criteria (mandatory)
Measurable Outcomes
- SC-001: 95% of users can successfully sync their last 30 days of activities from Garmin on the first attempt.
- SC-002: 100% of successfully synced activities appear in the user's activity list within 1 minute of sync completion.
- SC-003: 99% of activity list filtering operations return accurate results in under 2 seconds.
- SC-004: Users can successfully download the original data file for any activity where one is present.
- SC-005: 95% of users can successfully sync their last 30 days of health metrics from Garmin, including steps and HRV.
- SC-006: The health data summary for a 30-day period is calculated and displayed in under 3 seconds.
- SC-007: The system provides a user-friendly error message within 10 seconds if a Garmin sync fails due to authentication or service availability issues.