Files
FitTrack2/FitnessSync/specs/002-fitbit-garmin-sync/spec.md

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:

  1. 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.
  2. 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:

  1. 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.
  2. 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.
  3. 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:

  1. Given I have connected my Garmin account, When I trigger a health metric sync, Then my metrics from the last 30 days are updated.
  2. 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:

  1. 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").
  2. 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.
  3. 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 /setup page 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 POST to /setup/garmin. The system automatically configures the correct Garmin domain (garmin.com or garmin.cn) based on the is_china flag.
  • AS-003: The system MUST handle Garmin's Multi-Factor Authentication (MFA) process. If MFA is required, the /setup/garmin endpoint will return mfa_required status, 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 garth authentication 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 via garth.UserProfile.get().

Functional Requirements

  • FR-001: Users MUST be able to trigger a manual synchronization of their Garmin activities via a POST request to the /api/sync/activities endpoint. This endpoint accepts a days_back parameter 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, including garmin_activity_id, activity_name, activity_type (extracted from activityType.typeKey), start_time, duration, file_type, download_status, downloaded_at, and the binary file_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. The garth.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 GET request to the /api/activities/list endpoint.
  • FR-005: Users MUST be able to filter their activities by activity_type, start_date, end_date, and download_status via a GET request to the /api/activities/query endpoint.
  • FR-006: Users MUST be able to download the original data file for an individual activity via a GET request 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 POST request to the /api/sync/metrics endpoint. This endpoint accepts a days_back parameter.
  • FR-008: The system MUST fetch daily health metrics (steps, HRV, sleep) using garth.stats.steps.DailySteps.list(), garth.stats.hrv.DailyHRV.list(), and garth.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, and source ('garmin').
  • FR-010: Users MUST be able to see which types of health metrics are available for querying via a GET request to the /api/metrics/list endpoint.
  • FR-011: Users MUST be able to query their health metrics by metric_type, start_date, and end_date via a GET request to the /api/metrics/query endpoint.
  • 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 GET request to the /api/health-data/summary endpoint.
  • 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/status endpoint and a detailed list of synchronization logs available via the /api/logs endpoint.
  • 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 POST request to the /api/sync/weight endpoint. (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 from activityType.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.