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

157 lines
12 KiB
Markdown

# 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.