Skip to content

Commit 0e5244f

Browse files
authored
feat: v1.0.0 - Unified Insights API (Sprint 3) (#11)
* feat: v1.0.0 - Unified Insights API (Sprint 3) Complete Sprint 3 implementation with unified insights endpoint aggregating all analytics into a single response for downstream consumers. New Features: - Unified /users/{id}/insights endpoint - InsightsService aggregating baselines, patterns, anomalies - ObservationGenerator for natural language coaching observations - Feature unlock timeline (7/21/30/60 days thresholds) - Pydantic schemas for type-safe responses - Data readiness tracking with progress indicators Documentation: - Updated API reference with insights endpoint - OpenAPI spec at /schema/openapi.json - Swagger UI at /schema/swagger - ReDoc at /schema/redoc - CHANGELOG updated for v1.0.0 Tests: - 17 new test cases for insights functionality - Coverage for various data ages (0, 7, 30, 60 days) - Observation and suggestion generation tests Stu Mason + AI <me@stumason.dev> * fix: Update Claude review workflow to post PR comments - Add Bash(gh pr comment:*) to allowed tools - Update prompt to instruct Claude to post via gh pr comment - Follows anthropics/claude-code-action recommended pattern Stu Mason + AI <me@stumason.dev>
1 parent f21a849 commit 0e5244f

File tree

16 files changed

+1986
-126
lines changed

16 files changed

+1986
-126
lines changed

.github/workflows/claude-code-review.yml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,24 @@ jobs:
2828
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
2929
use_sticky_comment: true
3030
claude_args: |
31-
--allowedTools "Bash(gh pr diff *),Bash(gh pr view *),Read,Glob,Grep,Write"
31+
--allowedTools "Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Read,Glob,Grep"
3232
prompt: |
33-
Review PR #${{ github.event.pull_request.number }} thoroughly.
33+
REPO: ${{ github.repository }}
34+
PR NUMBER: ${{ github.event.pull_request.number }}
35+
36+
Review this pull request thoroughly.
3437
3538
First, run `gh pr diff ${{ github.event.pull_request.number }}` to see the changes.
3639
37-
Then for each changed file, analyze:
40+
Then analyze each changed file for:
3841
1. **Summary**: What does this change do?
3942
2. **Security**: Any security concerns (auth, injection, secrets)?
4043
3. **Bugs**: Logic errors, edge cases, error handling?
4144
4. **Style**: Consistency with codebase patterns?
4245
5. **Improvements**: Suggestions for better approaches?
4346
44-
Be specific. Reference line numbers.
47+
Be specific. Reference file paths and line numbers.
4548
46-
IMPORTANT: Do NOT try to post comments using `gh pr comment`. Instead, output your complete review directly as your final response. The GitHub Action will automatically post it as a PR comment.
49+
When your review is complete, use `gh pr comment ${{ github.event.pull_request.number }} --body "YOUR_REVIEW"` to post your findings as a PR comment.
4750
4851
Format your review with clear markdown sections and provide an overall recommendation (approve, request changes, or comment).

CHANGELOG.md

Lines changed: 83 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,94 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [Unreleased]
8+
## [1.0.0] - 2025-01-13
9+
10+
First stable release of polar-flow-server - a self-hosted health analytics server for Polar devices.
911

1012
### Added
11-
- Initial release of polar-flow-server
12-
- REST API with user-scoped endpoints
13-
- Database models with multi-user support
14-
- Polar Flow SDK integration for data sync
15-
- Support for Sleep, Nightly Recharge, Activity, and Exercise data
13+
14+
**Core Infrastructure**
15+
- REST API with user-scoped endpoints (`/api/v1/users/{user_id}/...`)
16+
- Per-user API key authentication with Argon2 hashing
17+
- Database models with multi-user support from day one
18+
- Polar Flow SDK integration for automated data sync
1619
- DuckDB support for self-hosted deployments
17-
- PostgreSQL support for SaaS deployments
18-
- Litestar async web framework
20+
- PostgreSQL support for SaaS/multi-user deployments
21+
- Alembic database migrations
22+
- Litestar async web framework with OpenAPI documentation
1923
- Structured logging with structlog
20-
- Comprehensive test suite
24+
25+
**Data Endpoints**
26+
- Sleep data with sleep stages and HRV metrics
27+
- Nightly Recharge (ANS charge, recovery metrics)
28+
- Daily activity (steps, calories, activity zones)
29+
- Exercises/workouts with detailed metrics
30+
- Cardio Load tracking with acute/chronic load ratios
31+
- Continuous heart rate data
32+
- Activity samples (time series)
33+
34+
**Analytics Engine**
35+
- Personal baselines with IQR-based anomaly detection
36+
- Rolling averages (7-day, 30-day, 90-day windows)
37+
- Sleep-HRV correlation analysis (Spearman)
38+
- Overtraining risk scoring (multi-metric composite)
39+
- Trend detection (7-day vs 30-day baseline comparison)
40+
- Training load ratio monitoring
41+
- Automatic anomaly alerts (warning/critical severity)
42+
43+
**Unified Insights API**
44+
- Single `/users/{id}/insights` endpoint aggregating all analytics
45+
- Feature unlock timeline based on data availability:
46+
- 7 days: Basic baselines
47+
- 21 days: Patterns and anomaly detection
48+
- 30 days: Extended baselines
49+
- 60 days: ML predictions (planned)
50+
- Natural language observation generation for coaching layers
51+
- Actionable suggestions based on current metrics
52+
- Data readiness tracking with progress indicators
53+
54+
**Developer Experience**
55+
- Comprehensive test suite (74+ tests)
2156
- Type safety with mypy strict mode
57+
- Ruff for linting and formatting
58+
- OpenAPI schema at `/schema/openapi.json`
59+
- Swagger UI at `/schema/swagger`
60+
- ReDoc at `/schema/redoc`
61+
- MkDocs Material documentation
62+
63+
**Deployment**
64+
- Docker support with health checks
2265
- CI/CD workflows (tests, lint, security, publish, docs)
2366
- Dependabot with auto-merge for minor/patch updates
24-
- MkDocs Material documentation
25-
- Docker support (planned)
26-
- HTMX admin panel (planned)
27-
- MCP server for Claude Desktop (planned)
28-
- Analytics engine for HRV baselines and recovery scores (planned)
2967

30-
[Unreleased]: https://github.com/StuMason/polar-flow-server/commits/main
68+
### Documentation
69+
70+
- Quick Start guide
71+
- Architecture overview
72+
- Complete API reference with examples
73+
- ADR for per-user API key design
74+
75+
---
76+
77+
## [0.2.0] - 2025-01-10
78+
79+
### Added
80+
- Pattern detection service (correlations, trends, risk scores)
81+
- Anomaly scanning across all tracked metrics
82+
- Cardio Load endpoints with training load ratios
83+
- Per-user API key authentication
84+
85+
### Changed
86+
- Migrated from simple token auth to per-user API keys
87+
88+
## [0.1.0] - 2025-01-05
89+
90+
### Added
91+
- Initial project structure
92+
- Basic REST API endpoints
93+
- Database models for Polar data types
94+
- Sync service foundation
95+
96+
[1.0.0]: https://github.com/StuMason/polar-flow-server/compare/v0.2.0...v1.0.0
97+
[0.2.0]: https://github.com/StuMason/polar-flow-server/compare/v0.1.0...v0.2.0
98+
[0.1.0]: https://github.com/StuMason/polar-flow-server/releases/tag/v0.1.0

docs/api/overview.md

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,19 @@ REST API for accessing synced Polar health data.
44

55
Base URL: `http://localhost:8000/api/v1`
66

7+
## OpenAPI Specification
8+
9+
The full OpenAPI 3.1 specification is available at:
10+
11+
- **JSON**: `GET /schema/openapi.json`
12+
- **Interactive Docs**: `GET /schema/swagger` (Swagger UI)
13+
- **ReDoc**: `GET /schema/redoc`
14+
15+
```bash
16+
# Download OpenAPI spec
17+
curl http://localhost:8000/schema/openapi.json > openapi.json
18+
```
19+
720
## Authentication
821

922
All data endpoints require a `user_id` in the URL path. For self-hosted deployments, this is your Polar user ID. For SaaS integrations, this is your application's user identifier.
@@ -632,6 +645,227 @@ curl http://localhost:8000/api/v1/users/12345/anomalies
632645

633646
---
634647

648+
### Unified Insights
649+
650+
The unified insights endpoint aggregates all analytics into a single response optimized for downstream consumers (dashboards, coaching apps, etc.).
651+
652+
| Method | Endpoint | Description |
653+
|--------|----------|-------------|
654+
| GET | `/users/{user_id}/insights` | Get complete insights package |
655+
656+
**Response includes:**
657+
- Current metric values
658+
- Personal baseline comparisons
659+
- Detected patterns (correlations, trends, risk scores)
660+
- Anomalies (values outside normal bounds)
661+
- Natural language observations
662+
- Actionable suggestions
663+
- Feature availability based on data history
664+
665+
#### Feature Unlock Timeline
666+
667+
Features unlock progressively as more data becomes available:
668+
669+
| Days of Data | Features Available |
670+
|--------------|-------------------|
671+
| 0-6 | Raw data only, no analytics |
672+
| 7+ | 7-day baselines |
673+
| 21+ | Pattern detection, anomaly detection |
674+
| 30+ | Full 30-day baselines |
675+
| 60+ | ML predictions (future) |
676+
677+
#### Example Request
678+
679+
```bash
680+
curl http://localhost:8000/api/v1/users/12345/insights
681+
```
682+
683+
#### Example Response (30+ days of data)
684+
685+
```json
686+
{
687+
"user_id": "12345",
688+
"generated_at": "2026-01-13T10:30:00Z",
689+
"data_freshness": "2026-01-13T06:00:00Z",
690+
"data_age_days": 45,
691+
"status": "ready",
692+
693+
"feature_availability": {
694+
"baselines_7d": {"available": true, "message": null},
695+
"baselines_30d": {"available": true, "message": null},
696+
"patterns": {"available": true, "message": null},
697+
"anomaly_detection": {"available": true, "message": null},
698+
"ml_predictions": {"available": false, "message": "Unlocks in 15 days", "unlock_at_days": 60}
699+
},
700+
701+
"unlock_progress": {
702+
"next_unlock": "ml_predictions",
703+
"days_until_next": 15,
704+
"percent_to_next": 75.0
705+
},
706+
707+
"current_metrics": {
708+
"hrv": 45.2,
709+
"sleep_score": 78,
710+
"resting_hr": 52,
711+
"training_load_ratio": 1.1
712+
},
713+
714+
"baselines": {
715+
"hrv_rmssd": {
716+
"current": 45.2,
717+
"baseline": 52.0,
718+
"baseline_7d": 48.5,
719+
"baseline_30d": 52.0,
720+
"percent_of_baseline": 86.9,
721+
"trend": "declining",
722+
"status": "ready"
723+
},
724+
"sleep_score": {
725+
"current": 78,
726+
"baseline": 82.0,
727+
"percent_of_baseline": 95.1,
728+
"trend": "stable",
729+
"status": "ready"
730+
}
731+
},
732+
733+
"patterns": [
734+
{
735+
"name": "sleep_hrv_correlation",
736+
"pattern_type": "correlation",
737+
"score": 0.72,
738+
"significance": "high",
739+
"factors": [],
740+
"interpretation": "Strong positive correlation between sleep quality and HRV"
741+
},
742+
{
743+
"name": "overtraining_risk",
744+
"pattern_type": "composite",
745+
"score": 35,
746+
"significance": "medium",
747+
"factors": ["HRV trending below baseline"],
748+
"interpretation": null
749+
}
750+
],
751+
752+
"anomalies": [],
753+
754+
"observations": [
755+
{
756+
"category": "recovery",
757+
"priority": "high",
758+
"fact": "HRV is 13% below personal baseline",
759+
"context": "Current: 45ms, Baseline: 52ms",
760+
"trend": "declining"
761+
},
762+
{
763+
"category": "recovery",
764+
"priority": "info",
765+
"fact": "Strong connection between your sleep quality and HRV detected",
766+
"context": "Better sleep directly improves your recovery metrics",
767+
"trend": null
768+
}
769+
],
770+
771+
"suggestions": [
772+
{
773+
"action": "prioritize_recovery",
774+
"description": "Prioritize sleep and recovery today",
775+
"confidence": 0.85,
776+
"reason": "HRV is 13% below baseline"
777+
}
778+
]
779+
}
780+
```
781+
782+
#### Example Response (New User - 5 days)
783+
784+
```json
785+
{
786+
"user_id": "12345",
787+
"generated_at": "2026-01-13T10:30:00Z",
788+
"data_age_days": 5,
789+
"status": "unavailable",
790+
791+
"feature_availability": {
792+
"baselines_7d": {"available": false, "message": "Unlocks in 2 days", "unlock_at_days": 7},
793+
"baselines_30d": {"available": false, "message": "Unlocks in 25 days", "unlock_at_days": 30},
794+
"patterns": {"available": false, "message": "Unlocks in 16 days", "unlock_at_days": 21},
795+
"anomaly_detection": {"available": false, "message": "Unlocks in 16 days", "unlock_at_days": 21},
796+
"ml_predictions": {"available": false, "message": "Unlocks in 55 days", "unlock_at_days": 60}
797+
},
798+
799+
"unlock_progress": {
800+
"next_unlock": "baselines_7d",
801+
"days_until_next": 2,
802+
"percent_to_next": 71.4
803+
},
804+
805+
"current_metrics": {
806+
"hrv": 48.0,
807+
"sleep_score": 75,
808+
"resting_hr": 55,
809+
"training_load_ratio": null
810+
},
811+
812+
"baselines": {},
813+
"patterns": [],
814+
"anomalies": [],
815+
816+
"observations": [
817+
{
818+
"category": "onboarding",
819+
"priority": "info",
820+
"fact": "Building your personal baselines (5/7 days)",
821+
"context": "Keep wearing your device. Basic insights unlock in 2 days.",
822+
"trend": null
823+
}
824+
],
825+
826+
"suggestions": []
827+
}
828+
```
829+
830+
#### Response Fields
831+
832+
| Field | Description |
833+
|-------|-------------|
834+
| `status` | Overall status: `ready`, `partial`, or `unavailable` |
835+
| `data_age_days` | Number of days of data available |
836+
| `feature_availability` | Which analytics features are unlocked |
837+
| `unlock_progress` | Progress toward unlocking the next feature |
838+
| `current_metrics` | Most recent values of key health metrics |
839+
| `baselines` | Personal baseline comparisons by metric |
840+
| `patterns` | Detected patterns (correlations, trends, risk scores) |
841+
| `anomalies` | Values outside normal IQR bounds |
842+
| `observations` | Natural language observations for coaching |
843+
| `suggestions` | Actionable suggestions based on current state |
844+
845+
#### Observation Categories
846+
847+
| Category | Description |
848+
|----------|-------------|
849+
| `recovery` | Recovery and HRV-related observations |
850+
| `sleep` | Sleep quality observations |
851+
| `training` | Training load and overtraining observations |
852+
| `anomaly` | Detected anomalies in metrics |
853+
| `onboarding` | New user guidance |
854+
| `trend` | Trend-related observations |
855+
856+
#### Observation Priorities
857+
858+
| Priority | Description |
859+
|----------|-------------|
860+
| `critical` | Requires immediate attention |
861+
| `high` | Important observation |
862+
| `medium` | Moderate importance |
863+
| `low` | Minor observation |
864+
| `info` | Informational |
865+
| `positive` | Good news |
866+
867+
---
868+
635869
### Health
636870

637871
| Method | Endpoint | Description |

0 commit comments

Comments
 (0)