This commit is contained in:
2025-10-12 06:38:44 -07:00
parent 9e0bd322d3
commit 3886dcb9ab
158 changed files with 2022 additions and 9699 deletions

View File

@@ -0,0 +1,111 @@
# CentralDB API Specification
This document specifies the API endpoints provided by the CentralDB for the FitTrack Report Generator.
## Endpoints
### Activities
- **`GET /activities/{activity_id}/file`**: Downloads the FIT file for a given activity.
- **`POST /activities/{activity_id}/analysis`**: Creates an analysis artifact for an activity.
- **Request Body**: `AnalysisArtifactCreate`
- **`GET /activities/{activity_id}/analysis`**: Retrieves an analysis artifact for an activity.
- **Response Body**: `AnalysisArtifact`
- **`POST /activities/{activity_id}/analysis/charts`**: Uploads a chart for an analysis.
- **Request Body**: `multipart/form-data` with `chart_type` and `file`.
- **`GET /activities/{activity_id}/analysis/charts/{chart_type}`**: Retrieves a chart for an analysis.
## Schemas
### AnalysisArtifactCreate
```json
{
"properties": {
"data": {
"additionalProperties": true,
"type": "object",
"title": "Data"
}
},
"type": "object",
"required": [
"data"
],
"title": "AnalysisArtifactCreate"
}
```
### AnalysisArtifact
```json
{
"properties": {
"activity_id": {
"type": "integer",
"title": "Activity Id"
},
"data": {
"additionalProperties": true,
"type": "object",
"title": "Data"
},
"id": {
"type": "integer",
"title": "Id"
},
"created_at": {
"type": "string",
"format": "date-time",
"title": "Created At"
}
},
"type": "object",
"required": [
"activity_id",
"data",
"id",
"created_at"
],
"title": "AnalysisArtifact"
}
```
### Chart
```json
{
"properties": {
"id": {
"type": "integer",
"title": "Id"
},
"activity_id": {
"type": "integer",
"title": "Activity Id"
},
"chart_type": {
"type": "string",
"title": "Chart Type"
},
"file_path": {
"type": "string",
"title": "File Path"
},
"created_at": {
"type": "string",
"format": "date-time",
"title": "Created At"
}
},
"type": "object",
"required": [
"id",
"activity_id",
"chart_type",
"file_path",
"created_at"
],
"title": "Chart"
}
```

View File

@@ -0,0 +1,34 @@
# Specification Quality Checklist: Use CentralDB for Activities
**Purpose**: Validate specification completeness and quality before proceeding to planning
**Created**: 2025-10-11
**Feature**: [spec.md](./../spec.md)
## Content Quality
- [X] No implementation details (languages, frameworks, APIs)
- [X] Focused on user value and business needs
- [X] Written for non-technical stakeholders
- [X] All mandatory sections completed
## Requirement Completeness
- [X] No [NEEDS CLARIFICATION] markers remain
- [X] Requirements are testable and unambiguous
- [X] Success criteria are measurable
- [X] Success criteria are technology-agnostic (no implementation details)
- [X] All acceptance scenarios are defined
- [X] Edge cases are identified
- [X] Scope is clearly bounded
- [X] Dependencies and assumptions identified
## Feature Readiness
- [X] All functional requirements have clear acceptance criteria
- [X] User scenarios cover primary flows
- [X] Feature meets measurable outcomes defined in Success Criteria
- [X] No implementation details leak into specification
## Notes
- Items marked incomplete require spec updates before `/speckit.clarify` or `/speckit.plan`

View File

@@ -0,0 +1,21 @@
# Data Model
## Entities
### WorkoutAnalysis (Cache)
- **Description**: Represents a cached workout analysis result. This is an in-memory object to speed up subsequent requests for the same analysis.
- **Fields**:
- `activity_id`: The ID of the activity.
- `analysis_summary`: JSON object containing the summary of the analysis.
- `charts`: Dictionary of chart types to chart data.
- `timestamp`: The time when the analysis was cached.
### AnalysisArtifact (CentralDB)
- **Description**: Represents a stored analysis artifact in the CentralDB. This is the long-term storage for the analysis results.
- **Fields** (based on the CentralDB API):
- `id`: Unique identifier of the artifact.
- `activity_id`: The ID of the activity.
- `data`: JSON object containing the analysis results.
- `created_at`: Timestamp of when the artifact was created.

View File

@@ -0,0 +1,105 @@
# Implementation Plan: [FEATURE]
**Branch**: `[###-feature-name]` | **Date**: [DATE] | **Spec**: [link]
**Input**: Feature specification from `/specs/[###-feature-name]/spec.md`
**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/templates/commands/plan.md` for the execution workflow.
## Summary
This feature updates the GET /api/analysis/{analysis_id}/charts and GET /api/analysis/{analysis_id}/summary endpoints to automatically fetch and analyze workout files from a CentralDB.
## Technical Context
<!--
ACTION REQUIRED: Replace the content in this section with the technical details
for the project. The structure here is presented in advisory capacity to guide
the iteration process.
-->
**Language/Version**: Python 3.11
**Primary Dependencies**: FastAPI, pandas, numpy, scipy, matplotlib, fitparse, tcxparser, gpxpy, requests
**Storage**: Ephemeral in-memory cache (last 5), CentralDB (long-term)
**Testing**: pytest
**Target Platform**: Linux server
**Project Type**: web
**Performance Goals**: 95% of cached requests < 500ms; new analyses < 45s for a 2-hour workout.
**Constraints**: `analysis_id` must match CentralDB activity ID.
**Scale/Scope**: 50 concurrent analysis requests.
## Constitution Check
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
| Principle | Status | Justification (if violation) |
|---|---|---|
| I. Library-First | Aligned | The new functionality will be part of the existing library structure. |
| II. CLI Interface | VIOLATION | The feature is API-only, which is the primary interface for this web service. |
| III. Test-First (NON-NEGOTIABLE) | Aligned | The spec has acceptance scenarios that can be tested first. |
| IV. Integration Testing | Aligned | The feature requires integration with CentralDB. |
| V. Observability | Aligned | No new observability requirements for this feature. |
| VI. Versioning & Breaking Changes | Aligned | No breaking changes are expected. |
| VII. Simplicity | Aligned | The feature simplifies the user workflow by removing the need for manual file uploads. |
## Project Structure
### Documentation (this feature)
```
specs/[###-feature]/
├── plan.md # This file (/speckit.plan command output)
├── research.md # Phase 0 output (/speckit.plan command)
├── data-model.md # Phase 1 output (/speckit.plan command)
├── quickstart.md # Phase 1 output (/speckit.plan command)
├── contracts/ # Phase 1 output (/speckit.plan command)
└── tasks.md # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan)
```
### Source Code (repository root)
<!--
ACTION REQUIRED: Replace the placeholder tree below with the concrete layout
for this feature. Delete unused options and expand the chosen structure with
real paths (e.g., apps/admin, packages/something). The delivered plan must
not include Option labels.
-->
```
src/
├── core/
│ ├── batch_processor.py
│ ├── chart_generator.py
│ ├── file_parser.py
│ ├── logger.py
│ ├── report_generator.py
│ ├── workout_analyzer.py
│ └── workout_data.py
├── db/
│ ├── models.py
│ └── session.py
└── utils/
└── zone_calculator.py
api/
├── routers/
│ └── analysis.py
├── main.py
└── schemas.py
tests/
├── unit/
├── integration/
├── contract/
└── performance/
```
**Structure Decision**: The project will continue to use the existing structure, which separates the core logic, API, and tests into `src`, `api`, and `tests` directories respectively.
## Complexity Tracking
*Fill ONLY if Constitution Check has violations that must be justified*
| Violation | Why Needed | Simpler Alternative Rejected Because |
|-----------|------------|-------------------------------------|
| Violation | Why Needed | Simpler Alternative Rejected Because |
|---|---|---|
| II. CLI Interface | The primary interface for this feature is a REST API, as it is extending an existing web service. | A CLI is not the primary interface for this service and would add unnecessary complexity at this stage. |

View File

@@ -0,0 +1,137 @@
# Quickstart Guide: FitTrack Report Generator API
This guide provides a quick overview of how to interact with the FitTrack Report Generator API.
## Base URL
The base URL for all API endpoints is `/api`.
## 1. Analyze a Single Workout File
To analyze a single workout file, send a `POST` request to the `/analyze/workout` endpoint with the workout file and optional user/FTP information.
### Endpoint
`POST /api/analyze/workout`
### Request Example (using `curl`)
```bash
curl -X POST \
-H "Content-Type: multipart/form-data" \
-F "file=@/path/to/your/workout.fit" \
-F "user_id=a1b2c3d4-e5f6-7890-1234-567890abcdef" \
-F "ftp_value=250" \
http://localhost:8000/api/analyze/workout
```
### Response Example (200 OK)
```json
{
"analysis_id": "f1e2d3c4-b5a6-9876-5432-10fedcba9876",
"user_id": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
"file_name": "workout.fit",
"analysis_date": "2025-10-09T10:30:00Z",
"status": "completed",
"metrics": {
"duration": "01:00:00",
"distance_km": 25.5,
"normalized_power_watts": 220,
"intensity_factor": 0.88,
"tss": 75
},
"report_url": "/api/analysis/f1e2d3c4-b5a6-9876-5432-10fedcba9876/report",
"chart_urls": {
"power_curve": "/api/analysis/f1e2d3c4-b5a6-9876-5432-10fedcba9876/charts?chart_type=power_curve"
}
}
```
## 2. Analyze Multiple Workout Files (Batch)
To analyze multiple workout files, compress them into a `.zip` file and send a `POST` request to the `/analyze/batch` endpoint.
### Endpoint
`POST /api/analyze/batch`
### Request Example (using `curl`)
```bash
curl -X POST \
-H "Content-Type: multipart/form-data" \
-F "zip_file=@/path/to/your/workouts.zip" \
-F "user_id=a1b2c3d4-e5f6-7890-1234-567890abcdef" \
http://localhost:8000/api/analyze/batch
```
### Response Example (200 OK)
```json
{
"batch_id": "g1h2i3j4-k5l6-7890-1234-567890abcdef",
"status": "processing",
"total_files": 10
}
```
## 3. Retrieve Charts for an Analysis
To get a specific chart for a previously analyzed workout, send a `GET` request to the `/analysis/{analysis_id}/charts` endpoint.
### Endpoint
`GET /api/analysis/{analysis_id}/charts`
### Path Parameters
- `analysis_id`: The unique ID of the workout analysis.
### Query Parameters
- `chart_type`: The type of chart to retrieve (e.g., `power_curve`, `elevation_profile`, `zone_distribution_power`).
### Request Example
```bash
curl -X GET http://localhost:8000/api/analysis/f1e2d3c4-b5a6-9876-5432-10fedcba9876/charts?chart_type=power_curve
```
### Response
Returns a PNG image of the requested chart.
## 4. Retrieve Analysis Summary
To get a detailed summary of a previously analyzed workout, send a `GET` request to the `/analysis/{analysis_id}/summary` endpoint.
### Endpoint
`GET /api/analysis/{analysis_id}/summary`
### Path Parameters
- `analysis_id`: The unique ID of the workout analysis.
### Request Example
```bash
curl -X GET http://localhost:8000/api/analysis/f1e2d3c4-b5a6-9876-5432-10fedcba9876/summary
```
### Response Example (200 OK)
```json
{
"analysis_id": "f1e2d3c4-b5a6-9876-5432-10fedcba9876",
"user_id": "a1b2c3d4-e5f6-7890-1234-567890abcdef",
"file_name": "workout.fit",
"analysis_date": "2025-10-09T10:30:00Z",
"status": "completed",
"metrics": {
"duration": "01:00:00",
"distance_km": 25.5,
"normalized_power_watts": 220,
"intensity_factor": 0.88,
"tss": 75
},
"report_url": "/api/analysis/f1e2d3c4-b5a6-9876-5432-10fedcba9876/report",
"chart_urls": {
"power_curve": "/api/analysis/f1e2d3c4-b5a6-9876-5432-10fedcba9876/charts?chart_type=power_curve"
}
}
```

View File

@@ -0,0 +1,24 @@
# Research Findings
## Decision: Use `httpx` for asynchronous HTTP requests
**Rationale**: The existing application uses FastAPI, which is an asynchronous framework. The `requests` library is synchronous and will block the event loop, degrading performance. `httpx` provides a similar API to `requests` but is fully asynchronous, making it the ideal choice for this project.
**Alternatives considered**: Using `requests` in a thread pool. This is a viable option but is more complex to implement and maintain than using a native async library like `httpx`.
## Decision: Use SQLAlchemy with `asyncpg` for database integration
**Rationale**: SQLAlchemy is a powerful and widely used ORM for Python that supports asynchronous operations with the `asyncpg` driver. This allows for non-blocking database calls, which is essential for a FastAPI application. It also provides a robust way to manage database sessions and connection pooling.
**Alternatives considered**:
- **Using a synchronous ORM**: This would block the event loop and is not recommended for FastAPI.
- **Writing raw SQL queries**: This is more error-prone, less secure (risk of SQL injection), and harder to maintain than using an ORM.
- **SQLModel**: While SQLModel is a good option, SQLAlchemy is more mature and has a larger community. Since the project already uses SQLAlchemy, it's better to stick with it for consistency.
## Decision: Use Pydantic `BaseSettings` for configuration management
**Rationale**: Pydantic's `BaseSettings` provides a convenient way to manage application settings and secrets from environment variables. This keeps sensitive information out of the codebase and makes the application more portable across different environments.
**Alternatives considered**:
- **Hardcoding configuration**: This is insecure and makes the application difficult to configure.
- **Using `.ini` or `.json` files**: This is a viable option, but Pydantic's `BaseSettings` provides validation and type hinting, which makes the configuration more robust.

View File

@@ -0,0 +1,72 @@
# Feature Specification: Use CentralDB for Activities
**Feature Branch**: `002-feature-use-centraldb`
**Created**: 2025-10-11
**Status**: Draft
**Input**: User description: "Feature: Use CentralDB for Activites. I have a CentralDB that I use to store activities. I want to update the `GET /api/analysis/{analysis_id}/charts` and `GET /api/analysis/{analysis_id}/summary` to automatically have service reach out to CentralDB download the fit file and perform the analysis. Also, we should make sure that the {analysis_id} variable we use always matches the activity ID."
## Clarifications
### Session 2025-10-11
- Q: Are the `analysis_id` and `activityID` variables interchangeable? i.e. if I know the `activity_id` can I use that in the API calls? → A: Yes, `analysis_id` and `activityID` should be treated as the same identifier.
- Q: Does the CentralDB API provide an endpoint to retrieve analysis artifacts? The current spec only shows an endpoint for *creating* them. → A: No, this endpoint does not exist and needs to be created by the CentralDB team.
- Q: What is the structure of the analysis artifact that will be stored? → A: A JSON object for the summary, and separate files for each chart.
## User Scenarios & Testing *(mandatory)*
### User Story 1 - Retrieve analysis from CentralDB (Priority: P1)
As a user, I want to be able to retrieve analysis charts and summaries for my activities stored in CentralDB by using the activity ID, so that I don't have to manually upload the files every time.
**Why this priority**: This is the core functionality of the feature and provides the main user value.
**Independent Test**: A user can request a chart or a summary for an activity ID that exists in CentralDB and receive the correct analysis without having to upload the file.
**Acceptance Scenarios**:
1. **Given** an activity ID that exists in CentralDB and has not been analyzed before, **When** a user requests a summary for that activity ID, **Then** the system should download the corresponding FIT file from CentralDB, perform the analysis, and return the summary.
2. **Given** an activity ID that exists in CentralDB and has been analyzed before, **When** a user requests a summary for that activity ID, **Then** the system should return the cached analysis summary without re-analyzing the file.
3. **Given** an activity ID that exists in CentralDB and has not been analyzed before, **When** a user requests a chart for that activity ID, **Then** the system should download the corresponding FIT file from CentralDB, perform the analysis, and return the chart.
4. **Given** an activity ID that exists in CentralDB and has been analyzed before, **When** a user requests a chart for that activity ID, **Then** the system should return the cached chart without re-analyzing the file.
5. **Given** an activity ID that does not exist in CentralDB, **When** a user requests a summary or a chart for that activity ID, **Then** the system should return a "not found" error.
### Edge Cases
- What happens when the FIT file in CentralDB is corrupted or malformed?
- How does the system handle network errors when trying to reach CentralDB?
- What happens if the analysis process fails for a valid FIT file?
## Requirements *(mandatory)*
### Functional Requirements
- **FR-001**: The system MUST be able to connect to the CentralDB.
- **FR-002**: The `GET /api/analysis/{analysis_id}/summary` endpoint MUST be updated to accept an `analysis_id` which is the same as the `activity_id` in CentralDB.
- **FR-003**: The `GET /api/analysis/{analysis_id}/charts` endpoint MUST be updated to accept an `analysis_id` which is the same as the `activity_id` in CentralDB.
- **FR-004**: If an analysis for the given activity ID does not exist, the system MUST download the FIT file from CentralDB.
- **FR-005**: After downloading the FIT file, the system MUST perform a workout analysis.
- **FR-006**: The system MUST store the analysis results. The last 5 analysis results should be cached in local ephemeral storage.
- **FR-007**: If an analysis for the given activity ID already exists, the system MUST return the stored results without re-downloading or re-analyzing the file.
- **FR-008**: The system MUST handle errors gracefully, such as when an activity ID is not found in CentralDB, the FIT file is unavailable, or the analysis fails.
- **FR-009**: The system MUST provide a mechanism to store the analysis results in the CentralDB.
- **FR-010**: A new endpoint `GET /activities/{activity_id}/analysis` MUST be defined for the CentralDB API to retrieve analysis artifacts.
- **FR-011**: The analysis artifact stored in CentralDB MUST consist of a JSON object for the summary and separate files for each chart.
### Key Entities *(include if feature involves data)*
- **Activity**: Represents a workout activity stored in CentralDB. It has an ID and a corresponding FIT file.
- **WorkoutAnalysis**: Represents the analysis result of an activity. It is associated with an activity ID.
### Assumptions
- The API for CentralDB is defined and available for the development team.
## Success Criteria *(mandatory)*
### Measurable Outcomes
- **SC-001**: 95% of requests for existing analyses should be returned in under 500ms.
- **SC-002**: For new analyses, the complete process of downloading, analyzing, and returning the result should take less than 45 seconds for a typical 2-hour workout file.
- **SC-003**: The system should be able to handle at least 50 concurrent analysis requests.
- **SC-004**: The error rate for analysis requests should be less than 1%.

View File

@@ -0,0 +1,60 @@
# Tasks: Use CentralDB for Activities
**Feature Branch**: `002-feature-use-centraldb`
**Date**: 2025-10-11
**Spec**: /home/sstent/Projects/FitTrack_ReportGenerator/specs/002-feature-use-centraldb/spec.md
## Summary
This document outlines the tasks required to implement the "Use CentralDB for Activities" feature.
## Phase 1: Setup
- [X] T001: Add `httpx` to the project dependencies in `pyproject.toml`.
- [X] T002: Add configuration for the CentralDB API base URL to the application settings.
## Phase 2: Foundational Components
- [X] T003: Implement a `CentralDBClient` class in `src/clients/centraldb_client.py` to interact with the CentralDB API. This client should use `httpx` for making asynchronous requests.
- [X] T004: Implement methods in `CentralDBClient` for:
- Downloading a FIT file (`GET /activities/{activity_id}/file`).
- Getting an analysis artifact (`GET /activities/{activity_id}/analysis`).
- Creating an analysis artifact (`POST /activities/{activity_id}/analysis`).
- Uploading a chart (`POST /activities/{activity_id}/analysis/charts`).
- Retrieving a chart (`GET /activities/{activity_id}/analysis/charts/{chart_type}`).
- [X] T005: Implement an in-memory cache in `src/core/cache.py` to store the last 5 analysis results.
## Phase 3: User Story 1 - Retrieve analysis from CentralDB
**Goal**: Enable users to retrieve analysis charts and summaries for activities stored in CentralDB.
**Independent Test Criteria**: A user can request a chart or a summary for an activity ID that exists in CentralDB and receive the correct analysis without having to upload the file.
- [X] T006 [US1]: Update the `GET /api/analysis/{analysis_id}/summary` endpoint in `api/routers/analysis.py`.
- [X] T007 [US1]: In the summary endpoint, implement the logic to:
- Check the local cache for the analysis.
- If not in cache, check CentralDB for the analysis artifact.
- If not in CentralDB, download the FIT file, perform the analysis, and store the results in CentralDB and the local cache.
- [X] T008 [US1]: Update the `GET /api/analysis/{analysis_id}/charts` endpoint in `api/routers/analysis.py`.
- [X] T009 [US1]: In the charts endpoint, implement the logic to:
- Check the local cache for the chart.
- If not in cache, check CentralDB for the chart.
- If not in CentralDB, download the FIT file, perform the analysis, generate the chart, and store it in CentralDB and the local cache.
- [X] T010 [US1]: Write integration tests for the updated endpoints to verify the interaction with the CentralDB API. (tests/integration/test_centraldb_integration.py)
## Phase 4: Polish & Cross-Cutting Concerns
- [X] T011: Review and refine error handling for the new CentralDB integration.
- [X] T012: Add logging for all interactions with the CentralDB API.
## Dependencies
- Phase 1 must be completed before Phase 2.
- Phase 2 must be completed before Phase 3.
- Phase 3 must be completed before Phase 4.
## Parallel Execution Examples
### User Story 1 (P1)
- T006 and T008 can be worked on in parallel.
- T007 and T009 can be worked on in parallel after T006 and T008 are complete.