Skip to content

Commit 8a6471a

Browse files
brtkwrclaude
andcommitted
refactor: simplify API to /workouts/{index}, remove /metrics suffix
Single data endpoint at /workouts/{index} returns all metrics + GPS. Log shows metric and GPS counts separately. Updated fetch script, mock server, tests, and README. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e68140b commit 8a6471a

5 files changed

Lines changed: 20 additions & 20 deletions

File tree

HealthKitExporter/HTTPServer.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -128,16 +128,16 @@ final class HTTPServer: ObservableObject {
128128
return
129129
}
130130

131-
if let index = parseWorkoutSubpath(path: path, suffix: "metrics") {
132-
await handleGetMetrics(connection: connection, index: index)
131+
if let index = parseWorkoutIndex(path: path) {
132+
await handleGetWorkout(connection: connection, index: index)
133133
return
134134
}
135135

136136
sendResponse(connection: connection, status: 404, body: "{\"error\": \"Not found\"}")
137137
}
138138

139-
private func parseWorkoutSubpath(path: String, suffix: String) -> Int? {
140-
let pattern = "^/workouts/(\\d+)/\(suffix)$"
139+
private func parseWorkoutIndex(path: String) -> Int? {
140+
let pattern = "^/workouts/(\\d+)$"
141141
guard let regex = try? NSRegularExpression(pattern: pattern),
142142
let match = regex.firstMatch(
143143
in: path, range: NSRange(path.startIndex..., in: path)),
@@ -178,7 +178,7 @@ final class HTTPServer: ObservableObject {
178178
}
179179

180180
@MainActor
181-
private func handleGetMetrics(connection: NWConnection, index: Int) async {
181+
private func handleGetWorkout(connection: NWConnection, index: Int) async {
182182
guard let manager = healthKitManager else {
183183
sendResponse(
184184
connection: connection, status: 500,
@@ -198,13 +198,13 @@ final class HTTPServer: ObservableObject {
198198
return
199199
}
200200

201-
let metrics = try await manager.fetchAllMetrics(for: workouts[index])
202-
let routeCount = (metrics["route"] as? [[String: Any]])?.count ?? 0
203-
let metricCount = metrics.values.compactMap { ($0 as? [[String: Any]])?.count }.reduce(0, +) - routeCount
204-
log("/workouts/\(index)/metrics", status: 200, detail: "\(metricCount) metrics, \(routeCount) GPS")
205-
sendJSONResponse(connection: connection, object: metrics)
201+
let data = try await manager.fetchAllMetrics(for: workouts[index])
202+
let routeCount = (data["route"] as? [[String: Any]])?.count ?? 0
203+
let metricCount = data.values.compactMap { ($0 as? [[String: Any]])?.count }.reduce(0, +) - routeCount
204+
log("/workouts/\(index)", status: 200, detail: "\(metricCount) metrics, \(routeCount) GPS")
205+
sendJSONResponse(connection: connection, object: data)
206206
} catch {
207-
log("/workouts/\(index)/metrics", status: 500, detail: error.localizedDescription)
207+
log("/workouts/\(index)", status: 500, detail: error.localizedDescription)
208208
sendResponse(
209209
connection: connection, status: 500,
210210
body: "{\"error\": \"\(error.localizedDescription)\"}")

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,10 @@ This is a [known limitation](https://discussions.apple.com/thread/253843222) of
8585

8686
The iOS app serves JSON on port 8080:
8787

88-
| Endpoint | Description |
89-
| ------------------------------- | ------------------------------------- |
90-
| `GET /workouts` | List all workouts with metadata |
91-
| `GET /workouts/{index}/metrics` | All metrics + GPS route for a workout |
88+
| Endpoint | Description |
89+
| ---------------------- | ------------------------------------- |
90+
| `GET /workouts` | List all workouts with metadata |
91+
| `GET /workouts/{index}` | All metrics + GPS route for a workout |
9292

9393
## Development
9494

fetch_healthkit.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def main():
5353

5454
try:
5555
# Metrics response includes route data
56-
data = fetch_json(f"{base}/workouts/{idx}/metrics", timeout=120)
56+
data = fetch_json(f"{base}/workouts/{idx}", timeout=120)
5757
route = data.pop('route', [])
5858
workout_file = output_dir / f"{label}_{idx}.json"
5959
with open(workout_file, "w") as f:

mock_server.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ def do_GET(self):
158158
self._send_json(self.workouts or [])
159159
return
160160

161-
match = re.match(r"^/workouts/(\d+)/metrics$", self.path)
161+
match = re.match(r"^/workouts/(\d+)$", self.path)
162162
if match:
163163
idx = int(match.group(1))
164164
if 0 <= idx < len(self.workouts or []):
@@ -210,7 +210,7 @@ def main():
210210
print(f"Serving {num_workouts} sample workouts")
211211
print("Endpoints:")
212212
print(" GET /workouts")
213-
print(" GET /workouts/{index}/metrics")
213+
print(" GET /workouts/{index}")
214214
print(" GET /workouts/{index}/heart_rate")
215215
try:
216216
server.serve_forever()

test_fetch_healthkit.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,15 +114,15 @@ def test_workouts_endpoint(self):
114114
self.assertIn("activity_type", data[0])
115115

116116
def test_metrics_endpoint(self):
117-
data = self._fetch("/workouts/0/metrics")
117+
data = self._fetch("/workouts/0")
118118
self.assertIn("heart_rate", data)
119119
self.assertIn("route", data)
120120
self.assertGreater(len(data["heart_rate"]), 0)
121121

122122
def test_metrics_endpoint_invalid_index(self):
123123
import urllib.error
124124
with self.assertRaises(urllib.error.HTTPError) as ctx:
125-
self._fetch("/workouts/999/metrics")
125+
self._fetch("/workouts/999")
126126
self.assertEqual(ctx.exception.code, 404)
127127

128128
def test_404_for_unknown_path(self):

0 commit comments

Comments
 (0)