analytics(healthkit): wire historical HealthKit reference into reports (KC-51 ph4e)
Some checks failed
CI / build-and-test (push) Has been cancelled

Completes 'available HealthKit trends' for weekly/monthly reports. Adds
HealthKitManager.dailyReference (3 parallel bucketed HKStatisticsCollectionQuery
for steps/active-calories/resting-HR over ~13 months), AnalyticsService.
refreshHealthReference to cache and merge it into DaySamples, and avgSteps/
avgActiveCalories/avgRestingHR on PeriodSummary (nil when absent — honest).
Surfaced in AnalyticsView's weekly/monthly cards.

Verified standalone (swiftc, 8/8): aggregation correct when present, nil when
absent, workout metrics unaffected, reports stay deterministic. Tests added to
AnalyticsEngineTests. This completes all analytics logic — remaining work is the
first Xcode build (device, user-run) to catch any SwiftUI compile issues.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
kutesir
2026-07-08 10:39:00 +03:00
parent 67863fca44
commit 839ac72846
8 changed files with 138 additions and 3 deletions

View File

@@ -170,6 +170,30 @@ final class AnalyticsEngineTests: XCTestCase {
XCTAssertEqual(s.consistency, 2.0/3.0, accuracy: 0.0001)
}
// MARK: - HealthKit reference aggregation (partial/missing data honest)
func testSummarizeAggregatesHealthKitAveragesWhenPresent() {
let days = [
DaySample(dateKey: "2026-07-06", didWorkout: true, scheduled: true, sessions: 1, sets: 20, volume: 1000,
steps: 8000, activeCalories: 500, restingHeartRate: 55),
DaySample(dateKey: "2026-07-07", didWorkout: true, scheduled: true, sessions: 1, sets: 18, volume: 900,
steps: 10000, activeCalories: 600, restingHeartRate: 57),
DaySample(dateKey: "2026-07-08", didWorkout: false, scheduled: false) // no HK reference
]
let s = AnalyticsEngine.summarize(days)
XCTAssertEqual(s.avgSteps, 9000)
XCTAssertEqual(s.avgActiveCalories, 550)
XCTAssertEqual(s.avgRestingHR, 56)
}
func testSummarizeHealthKitNilWhenAbsent() {
let s = AnalyticsEngine.summarize([DaySample(dateKey: "2026-07-06", didWorkout: true, scheduled: true, sets: 20, volume: 1000)])
XCTAssertNil(s.avgSteps)
XCTAssertNil(s.avgActiveCalories)
XCTAssertNil(s.avgRestingHR)
XCTAssertEqual(s.volume, 1000) // workout metrics unaffected
}
// MARK: - Reports (idempotency / determinism)
func testWeeklyReportDeterministic() {