Spaces:
Running
Running
update dates
Browse files- create_test_db.py +0 -24
- data/export.xml +44 -44
- mcp_server.py +103 -4
create_test_db.py
DELETED
@@ -1,24 +0,0 @@
|
|
1 |
-
#!/usr/bin/env python3
|
2 |
-
"""Create a test SQLite database from the export.xml file."""
|
3 |
-
|
4 |
-
from src.parser.parser import AppleHealthParser
|
5 |
-
import os
|
6 |
-
|
7 |
-
def main():
|
8 |
-
xml_file = "data/export.xml"
|
9 |
-
db_file = "data/health_data.db"
|
10 |
-
|
11 |
-
# Remove existing database if it exists
|
12 |
-
if os.path.exists(db_file):
|
13 |
-
os.remove(db_file)
|
14 |
-
print(f"Removed existing {db_file}")
|
15 |
-
|
16 |
-
# Create the parser with no date cutoff to include all records
|
17 |
-
from datetime import timedelta
|
18 |
-
parser = AppleHealthParser(db_path=db_file, data_cutoff=timedelta(days=36500)) # 100 years
|
19 |
-
print(f"Parsing {xml_file}...")
|
20 |
-
parser.parse_file(xml_file)
|
21 |
-
print(f"Successfully created {db_file}")
|
22 |
-
|
23 |
-
if __name__ == "__main__":
|
24 |
-
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data/export.xml
CHANGED
@@ -218,129 +218,129 @@
|
|
218 |
>
|
219 |
]>
|
220 |
<HealthData locale="en_US">
|
221 |
-
<ExportDate value="2025-
|
222 |
<Me HKCharacteristicTypeIdentifierDateOfBirth="1995-01-15" HKCharacteristicTypeIdentifierBiologicalSex="HKBiologicalSexMale" HKCharacteristicTypeIdentifierBloodType="HKBloodTypeNotSet" HKCharacteristicTypeIdentifierFitzpatrickSkinType="HKFitzpatrickSkinTypeNotSet" HKCharacteristicTypeIdentifierCardioFitnessMedicationsUse="None"/>
|
223 |
|
224 |
<!-- Blood Glucose Records -->
|
225 |
-
<Record type="HKQuantityTypeIdentifierBloodGlucose" sourceName="Health" sourceVersion="17.3" unit="mmol<180.1558800000541>/L" creationDate="
|
226 |
<MetadataEntry key="HKWasUserEntered" value="1"/>
|
227 |
<MetadataEntry key="HKBloodGlucoseMealTime" value="1"/>
|
228 |
</Record>
|
229 |
-
<Record type="HKQuantityTypeIdentifierBloodGlucose" sourceName="Health" sourceVersion="17.3" unit="mmol<180.1558800000541>/L" creationDate="
|
230 |
<MetadataEntry key="HKWasUserEntered" value="1"/>
|
231 |
<MetadataEntry key="HKBloodGlucoseMealTime" value="2"/>
|
232 |
</Record>
|
233 |
|
234 |
<!-- Body Mass Index Records -->
|
235 |
-
<Record type="HKQuantityTypeIdentifierBodyMassIndex" sourceName="Zepp Life" sourceVersion="202311211629" unit="count" creationDate="
|
236 |
-
<Record type="HKQuantityTypeIdentifierBodyMassIndex" sourceName="Zepp Life" sourceVersion="202311211629" unit="count" creationDate="
|
237 |
|
238 |
<!-- Body Mass Records -->
|
239 |
-
<Record type="HKQuantityTypeIdentifierBodyMass" sourceName="Zepp Life" sourceVersion="202311211629" unit="kg" creationDate="
|
240 |
-
<Record type="HKQuantityTypeIdentifierBodyMass" sourceName="Zepp Life" sourceVersion="202311211629" unit="kg" creationDate="
|
241 |
|
242 |
<!-- Heart Rate Records -->
|
243 |
-
<Record type="HKQuantityTypeIdentifierHeartRate" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" unit="count/min" creationDate="
|
244 |
<MetadataEntry key="HKMetadataKeyHeartRateMotionContext" value="0"/>
|
245 |
</Record>
|
246 |
-
<Record type="HKQuantityTypeIdentifierHeartRate" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" unit="count/min" creationDate="
|
247 |
<MetadataEntry key="HKMetadataKeyHeartRateMotionContext" value="1"/>
|
248 |
</Record>
|
249 |
-
<Record type="HKQuantityTypeIdentifierHeartRate" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" unit="count/min" creationDate="
|
250 |
<MetadataEntry key="HKMetadataKeyHeartRateMotionContext" value="2"/>
|
251 |
</Record>
|
252 |
|
253 |
<!-- Step Count Records -->
|
254 |
-
<Record type="HKQuantityTypeIdentifierStepCount" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" unit="count" creationDate="
|
255 |
-
<Record type="HKQuantityTypeIdentifierStepCount" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" unit="count" creationDate="
|
256 |
|
257 |
<!-- Blood Pressure Correlation -->
|
258 |
-
<Correlation type="HKCorrelationTypeIdentifierBloodPressure" sourceName="Health" sourceVersion="17.3" creationDate="
|
259 |
<MetadataEntry key="HKWasUserEntered" value="1"/>
|
260 |
-
<Record type="HKQuantityTypeIdentifierBloodPressureSystolic" sourceName="Health" sourceVersion="17.3" unit="mmHg" creationDate="
|
261 |
<MetadataEntry key="HKWasUserEntered" value="1"/>
|
262 |
</Record>
|
263 |
-
<Record type="HKQuantityTypeIdentifierBloodPressureDiastolic" sourceName="Health" sourceVersion="17.3" unit="mmHg" creationDate="
|
264 |
<MetadataEntry key="HKWasUserEntered" value="1"/>
|
265 |
</Record>
|
266 |
</Correlation>
|
267 |
|
268 |
<!-- Sleep Analysis Records -->
|
269 |
-
<Record type="HKCategoryTypeIdentifierSleepAnalysis" sourceName="AutoSleep" sourceVersion="6.4.0" creationDate="
|
270 |
<MetadataEntry key="HKTimeZone" value="Europe/Zurich"/>
|
271 |
</Record>
|
272 |
-
<Record type="HKCategoryTypeIdentifierSleepAnalysis" sourceName="AutoSleep" sourceVersion="6.4.0" creationDate="
|
273 |
|
274 |
<!-- Workout Records -->
|
275 |
-
<Workout workoutActivityType="HKWorkoutActivityTypeWalking" duration="88.23378186623255" durationUnit="min" totalDistance="5.234" totalDistanceUnit="km" totalEnergyBurned="342.5" totalEnergyBurnedUnit="Cal" sourceName="Guillaume's Apple Watch" sourceVersion="6.2.5" device="<<HKDevice: 0x533b03430>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:6.2.5>" creationDate="
|
276 |
<MetadataEntry key="HKIndoorWorkout" value="0"/>
|
277 |
<MetadataEntry key="HKAverageMETs" value="5.43210 kcal/hr·kg"/>
|
278 |
-
<WorkoutEvent type="HKWorkoutEventTypePause" date="
|
279 |
-
<WorkoutEvent type="HKWorkoutEventTypeResume" date="
|
280 |
-
<WorkoutStatistics type="HKQuantityTypeIdentifierHeartRate" startDate="
|
281 |
-
<WorkoutStatistics type="HKQuantityTypeIdentifierActiveEnergyBurned" startDate="
|
282 |
-
<WorkoutRoute sourceName="Guillaume's Apple Watch" sourceVersion="11.2" creationDate="
|
283 |
<MetadataEntry key="HKMetadataKeySyncVersion" value="2"/>
|
284 |
<MetadataEntry key="HKMetadataKeySyncIdentifier" value="48F229FA-1BC6-47E6-91BE-BBA6796D5E8C"/>
|
285 |
-
<FileReference path="/workout-routes/
|
286 |
</WorkoutRoute>
|
287 |
-
<WorkoutRoute sourceName="Guillaume's Apple Watch" sourceVersion="11.2" creationDate="
|
288 |
<MetadataEntry key="HKMetadataKeySyncVersion" value="2"/>
|
289 |
<MetadataEntry key="HKMetadataKeySyncIdentifier" value="48F229FA-1BC6-47E6-91BE-BBA6796D5E8C"/>
|
290 |
-
<FileReference path="/workout-routes/
|
291 |
</WorkoutRoute>
|
292 |
</Workout>
|
293 |
|
294 |
-
<Workout workoutActivityType="HKWorkoutActivityTypeCycling" duration="28.81272718310356" durationUnit="min" totalDistance="12.5" totalDistanceUnit="km" totalEnergyBurned="289.3" totalEnergyBurnedUnit="Cal" sourceName="Guillaume's Apple Watch" sourceVersion="6.2.5" device="<<HKDevice: 0x533b03430>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:6.2.5>" creationDate="
|
295 |
<MetadataEntry key="HKIndoorWorkout" value="0"/>
|
296 |
-
<WorkoutStatistics type="HKQuantityTypeIdentifierHeartRate" startDate="
|
297 |
</Workout>
|
298 |
|
299 |
<!-- Activity Summary Records -->
|
300 |
-
<ActivitySummary dateComponents="
|
301 |
-
<ActivitySummary dateComponents="
|
302 |
-
<ActivitySummary dateComponents="
|
303 |
|
304 |
<!-- Additional diverse records -->
|
305 |
-
<Record type="HKQuantityTypeIdentifierActiveEnergyBurned" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" unit="Cal" creationDate="
|
306 |
|
307 |
-
<Record type="HKQuantityTypeIdentifierBasalEnergyBurned" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" unit="Cal" creationDate="
|
308 |
|
309 |
-
<Record type="HKQuantityTypeIdentifierDistanceWalkingRunning" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" unit="km" creationDate="
|
310 |
|
311 |
-
<Record type="HKCategoryTypeIdentifierAppleStandHour" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" creationDate="
|
312 |
|
313 |
-
<Record type="HKCategoryTypeIdentifierMindfulSession" sourceName="Breathe" sourceVersion="1.0" creationDate="
|
314 |
|
315 |
-
<Record type="HKQuantityTypeIdentifierBodyFatPercentage" sourceName="Health" sourceVersion="17.3" unit="%" creationDate="
|
316 |
<MetadataEntry key="HKWasUserEntered" value="1"/>
|
317 |
</Record>
|
318 |
|
319 |
-
<Record type="HKQuantityTypeIdentifierHeight" sourceName="Health" sourceVersion="17.3" unit="cm" creationDate="
|
320 |
<MetadataEntry key="HKWasUserEntered" value="1"/>
|
321 |
</Record>
|
322 |
|
323 |
-
<Record type="HKQuantityTypeIdentifierRestingHeartRate" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" unit="count/min" creationDate="
|
324 |
|
325 |
-
<Record type="HKQuantityTypeIdentifierVO2Max" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" unit="mL/kg·min" creationDate="
|
326 |
<MetadataEntry key="HKVO2MaxTestType" value="2"/>
|
327 |
</Record>
|
328 |
|
329 |
-
<Record type="HKQuantityTypeIdentifierAppleSleepingWristTemperature" sourceName="Guillaume's Apple Watch" sourceVersion="10.0" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch6,2, software:10.0>" unit="degC" creationDate="
|
330 |
<MetadataEntry key="HKSleepingWristTemperatureSourceType" value="0"/>
|
331 |
</Record>
|
332 |
|
333 |
-
<Record type="HKQuantityTypeIdentifierHeartRateVariabilitySDNN" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" unit="ms" creationDate="
|
334 |
<HeartRateVariabilityMetadataList>
|
335 |
<InstantaneousBeatsPerMinute bpm="72" time="7:47:41.86 PM"/>
|
336 |
<InstantaneousBeatsPerMinute bpm="68" time="7:47:53.78 PM"/>
|
337 |
</HeartRateVariabilityMetadataList>
|
338 |
</Record>
|
339 |
|
340 |
-
<Record type="HKQuantityTypeIdentifierOxygenSaturation" sourceName="Health" sourceVersion="17.3" unit="%" creationDate="
|
341 |
<MetadataEntry key="HKWasUserEntered" value="1"/>
|
342 |
</Record>
|
343 |
|
344 |
-
<Record type="HKQuantityTypeIdentifierRespiratoryRate" sourceName="Guillaume's Apple Watch" sourceVersion="10.0" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch6,2, software:10.0>" unit="count/min" creationDate="
|
345 |
|
346 |
</HealthData>
|
|
|
218 |
>
|
219 |
]>
|
220 |
<HealthData locale="en_US">
|
221 |
+
<ExportDate value="2025-06-17 12:00:00 +0100"/>
|
222 |
<Me HKCharacteristicTypeIdentifierDateOfBirth="1995-01-15" HKCharacteristicTypeIdentifierBiologicalSex="HKBiologicalSexMale" HKCharacteristicTypeIdentifierBloodType="HKBloodTypeNotSet" HKCharacteristicTypeIdentifierFitzpatrickSkinType="HKFitzpatrickSkinTypeNotSet" HKCharacteristicTypeIdentifierCardioFitnessMedicationsUse="None"/>
|
223 |
|
224 |
<!-- Blood Glucose Records -->
|
225 |
+
<Record type="HKQuantityTypeIdentifierBloodGlucose" sourceName="Health" sourceVersion="17.3" unit="mmol<180.1558800000541>/L" creationDate="2025-05-18 22:32:54 +0100" startDate="2025-05-18 12:30:00 +0100" endDate="2025-05-18 12:30:00 +0100" value="4.59">
|
226 |
<MetadataEntry key="HKWasUserEntered" value="1"/>
|
227 |
<MetadataEntry key="HKBloodGlucoseMealTime" value="1"/>
|
228 |
</Record>
|
229 |
+
<Record type="HKQuantityTypeIdentifierBloodGlucose" sourceName="Health" sourceVersion="17.3" unit="mmol<180.1558800000541>/L" creationDate="2025-05-18 22:45:00 +0100" startDate="2025-05-18 18:30:00 +0100" endDate="2025-05-18 18:30:00 +0100" value="5.23">
|
230 |
<MetadataEntry key="HKWasUserEntered" value="1"/>
|
231 |
<MetadataEntry key="HKBloodGlucoseMealTime" value="2"/>
|
232 |
</Record>
|
233 |
|
234 |
<!-- Body Mass Index Records -->
|
235 |
+
<Record type="HKQuantityTypeIdentifierBodyMassIndex" sourceName="Zepp Life" sourceVersion="202311211629" unit="count" creationDate="2025-04-18 22:41:05 +0100" startDate="2025-04-18 22:38:45 +0100" endDate="2025-04-18 22:38:45 +0100" value="21.3294"/>
|
236 |
+
<Record type="HKQuantityTypeIdentifierBodyMassIndex" sourceName="Zepp Life" sourceVersion="202311211629" unit="count" creationDate="2025-04-19 08:50:30 +0100" startDate="2025-04-19 01:07:25 +0100" endDate="2025-04-19 01:07:25 +0100" value="22.4251"/>
|
237 |
|
238 |
<!-- Body Mass Records -->
|
239 |
+
<Record type="HKQuantityTypeIdentifierBodyMass" sourceName="Zepp Life" sourceVersion="202311211629" unit="kg" creationDate="2025-04-18 22:41:05 +0100" startDate="2025-04-18 22:38:45 +0100" endDate="2025-04-18 22:38:45 +0100" value="68.5"/>
|
240 |
+
<Record type="HKQuantityTypeIdentifierBodyMass" sourceName="Zepp Life" sourceVersion="202311211629" unit="kg" creationDate="2025-04-19 08:50:30 +0100" startDate="2025-04-19 01:07:25 +0100" endDate="2025-04-19 01:07:25 +0100" value="72"/>
|
241 |
|
242 |
<!-- Heart Rate Records -->
|
243 |
+
<Record type="HKQuantityTypeIdentifierHeartRate" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" unit="count/min" creationDate="2025-05-28 19:26:08 +0100" startDate="2025-05-28 19:20:56 +0100" endDate="2025-05-28 19:20:56 +0100" value="56">
|
244 |
<MetadataEntry key="HKMetadataKeyHeartRateMotionContext" value="0"/>
|
245 |
</Record>
|
246 |
+
<Record type="HKQuantityTypeIdentifierHeartRate" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" unit="count/min" creationDate="2025-05-28 19:42:40 +0100" startDate="2025-05-28 19:42:39 +0100" endDate="2025-05-28 19:42:39 +0100" value="96">
|
247 |
<MetadataEntry key="HKMetadataKeyHeartRateMotionContext" value="1"/>
|
248 |
</Record>
|
249 |
+
<Record type="HKQuantityTypeIdentifierHeartRate" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" unit="count/min" creationDate="2025-05-28 19:59:33 +0100" startDate="2025-05-28 19:59:19 +0100" endDate="2025-05-28 19:59:19 +0100" value="150">
|
250 |
<MetadataEntry key="HKMetadataKeyHeartRateMotionContext" value="2"/>
|
251 |
</Record>
|
252 |
|
253 |
<!-- Step Count Records -->
|
254 |
+
<Record type="HKQuantityTypeIdentifierStepCount" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" unit="count" creationDate="2025-05-28 14:15:00 +0100" startDate="2025-05-28 14:00:00 +0100" endDate="2025-05-28 14:15:00 +0100" value="234"/>
|
255 |
+
<Record type="HKQuantityTypeIdentifierStepCount" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" unit="count" creationDate="2025-05-28 14:30:00 +0100" startDate="2025-05-28 14:15:00 +0100" endDate="2025-05-28 14:30:00 +0100" value="456"/>
|
256 |
|
257 |
<!-- Blood Pressure Correlation -->
|
258 |
+
<Correlation type="HKCorrelationTypeIdentifierBloodPressure" sourceName="Health" sourceVersion="17.3" creationDate="2025-05-23 12:52:42 +0100" startDate="2025-05-23 11:30:00 +0100" endDate="2025-05-23 11:30:00 +0100">
|
259 |
<MetadataEntry key="HKWasUserEntered" value="1"/>
|
260 |
+
<Record type="HKQuantityTypeIdentifierBloodPressureSystolic" sourceName="Health" sourceVersion="17.3" unit="mmHg" creationDate="2025-05-23 12:52:42 +0100" startDate="2025-05-23 11:30:00 +0100" endDate="2025-05-23 11:30:00 +0100" value="136">
|
261 |
<MetadataEntry key="HKWasUserEntered" value="1"/>
|
262 |
</Record>
|
263 |
+
<Record type="HKQuantityTypeIdentifierBloodPressureDiastolic" sourceName="Health" sourceVersion="17.3" unit="mmHg" creationDate="2025-05-23 12:52:42 +0100" startDate="2025-05-23 11:30:00 +0100" endDate="2025-05-23 11:30:00 +0100" value="69">
|
264 |
<MetadataEntry key="HKWasUserEntered" value="1"/>
|
265 |
</Record>
|
266 |
</Correlation>
|
267 |
|
268 |
<!-- Sleep Analysis Records -->
|
269 |
+
<Record type="HKCategoryTypeIdentifierSleepAnalysis" sourceName="AutoSleep" sourceVersion="6.4.0" creationDate="2025-04-28 08:37:42 +0100" startDate="2025-04-28 01:05:06 +0100" endDate="2025-04-28 08:30:00 +0100" value="HKCategoryValueSleepAnalysisInBed">
|
270 |
<MetadataEntry key="HKTimeZone" value="Europe/Zurich"/>
|
271 |
</Record>
|
272 |
+
<Record type="HKCategoryTypeIdentifierSleepAnalysis" sourceName="AutoSleep" sourceVersion="6.4.0" creationDate="2025-04-28 08:37:42 +0100" startDate="2025-04-28 01:15:00 +0100" endDate="2025-04-28 08:30:00 +0100" value="HKCategoryValueSleepAnalysisAsleepUnspecified"/>
|
273 |
|
274 |
<!-- Workout Records -->
|
275 |
+
<Workout workoutActivityType="HKWorkoutActivityTypeWalking" duration="88.23378186623255" durationUnit="min" totalDistance="5.234" totalDistanceUnit="km" totalEnergyBurned="342.5" totalEnergyBurnedUnit="Cal" sourceName="Guillaume's Apple Watch" sourceVersion="6.2.5" device="<<HKDevice: 0x533b03430>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:6.2.5>" creationDate="2025-05-08 18:57:19 +0100" startDate="2025-05-08 17:26:15 +0100" endDate="2025-05-08 18:57:18 +0100">
|
276 |
<MetadataEntry key="HKIndoorWorkout" value="0"/>
|
277 |
<MetadataEntry key="HKAverageMETs" value="5.43210 kcal/hr·kg"/>
|
278 |
+
<WorkoutEvent type="HKWorkoutEventTypePause" date="2025-05-08 17:45:00 +0100"/>
|
279 |
+
<WorkoutEvent type="HKWorkoutEventTypeResume" date="2025-05-08 17:47:30 +0100"/>
|
280 |
+
<WorkoutStatistics type="HKQuantityTypeIdentifierHeartRate" startDate="2025-05-08 17:26:15 +0100" endDate="2025-05-08 18:57:18 +0100" average="92.5" minimum="65" maximum="125" unit="count/min"/>
|
281 |
+
<WorkoutStatistics type="HKQuantityTypeIdentifierActiveEnergyBurned" startDate="2025-05-08 17:26:15 +0100" endDate="2025-05-08 18:57:18 +0100" sum="342.5" unit="Cal"/>
|
282 |
+
<WorkoutRoute sourceName="Guillaume's Apple Watch" sourceVersion="11.2" creationDate="2025-06-07 13:17:22 +0200" startDate="2025-06-07 12:58:33 +0200" endDate="2025-06-07 12:59:02 +0200">
|
283 |
<MetadataEntry key="HKMetadataKeySyncVersion" value="2"/>
|
284 |
<MetadataEntry key="HKMetadataKeySyncIdentifier" value="48F229FA-1BC6-47E6-91BE-BBA6796D5E8C"/>
|
285 |
+
<FileReference path="/workout-routes/route_2025-06-07_11.59am.gpx"/>
|
286 |
</WorkoutRoute>
|
287 |
+
<WorkoutRoute sourceName="Guillaume's Apple Watch" sourceVersion="11.2" creationDate="2025-06-07 13:17:22 +0200" startDate="2025-06-07 12:58:33 +0200" endDate="2025-06-07 12:59:02 +0200">
|
288 |
<MetadataEntry key="HKMetadataKeySyncVersion" value="2"/>
|
289 |
<MetadataEntry key="HKMetadataKeySyncIdentifier" value="48F229FA-1BC6-47E6-91BE-BBA6796D5E8C"/>
|
290 |
+
<FileReference path="/workout-routes/route_2025-06-07_11.59am.gpx"/>
|
291 |
</WorkoutRoute>
|
292 |
</Workout>
|
293 |
|
294 |
+
<Workout workoutActivityType="HKWorkoutActivityTypeCycling" duration="28.81272718310356" durationUnit="min" totalDistance="12.5" totalDistanceUnit="km" totalEnergyBurned="289.3" totalEnergyBurnedUnit="Cal" sourceName="Guillaume's Apple Watch" sourceVersion="6.2.5" device="<<HKDevice: 0x533b03430>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:6.2.5>" creationDate="2025-05-09 15:24:12 +0100" startDate="2025-05-09 14:55:22 +0100" endDate="2025-05-09 15:24:11 +0100">
|
295 |
<MetadataEntry key="HKIndoorWorkout" value="0"/>
|
296 |
+
<WorkoutStatistics type="HKQuantityTypeIdentifierHeartRate" startDate="2025-05-09 14:55:22 +0100" endDate="2025-05-09 15:24:11 +0100" average="115.2" minimum="88" maximum="145" unit="count/min"/>
|
297 |
</Workout>
|
298 |
|
299 |
<!-- Activity Summary Records -->
|
300 |
+
<ActivitySummary dateComponents="2025-05-08" activeEnergyBurned="547.163" activeEnergyBurnedGoal="680" activeEnergyBurnedUnit="Cal" appleMoveTime="0" appleMoveTimeGoal="0" appleExerciseTime="44" appleExerciseTimeGoal="30" appleStandHours="10" appleStandHoursGoal="12"/>
|
301 |
+
<ActivitySummary dateComponents="2025-05-09" activeEnergyBurned="732.037" activeEnergyBurnedGoal="680" activeEnergyBurnedUnit="Cal" appleMoveTime="0" appleMoveTimeGoal="0" appleExerciseTime="55" appleExerciseTimeGoal="30" appleStandHours="14" appleStandHoursGoal="12"/>
|
302 |
+
<ActivitySummary dateComponents="2025-05-10" activeEnergyBurned="824.261" activeEnergyBurnedGoal="680" activeEnergyBurnedUnit="Cal" appleMoveTime="0" appleMoveTimeGoal="0" appleExerciseTime="53" appleExerciseTimeGoal="30" appleStandHours="15" appleStandHoursGoal="12"/>
|
303 |
|
304 |
<!-- Additional diverse records -->
|
305 |
+
<Record type="HKQuantityTypeIdentifierActiveEnergyBurned" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" unit="Cal" creationDate="2025-05-28 14:30:00 +0100" startDate="2025-05-28 14:00:00 +0100" endDate="2025-05-28 14:30:00 +0100" value="45.3"/>
|
306 |
|
307 |
+
<Record type="HKQuantityTypeIdentifierBasalEnergyBurned" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" unit="Cal" creationDate="2025-05-28 15:00:00 +0100" startDate="2025-05-28 14:00:00 +0100" endDate="2025-05-28 15:00:00 +0100" value="85.7"/>
|
308 |
|
309 |
+
<Record type="HKQuantityTypeIdentifierDistanceWalkingRunning" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" unit="km" creationDate="2025-05-28 14:30:00 +0100" startDate="2025-05-28 14:00:00 +0100" endDate="2025-05-28 14:30:00 +0100" value="0.523"/>
|
310 |
|
311 |
+
<Record type="HKCategoryTypeIdentifierAppleStandHour" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" creationDate="2025-05-28 15:00:00 +0100" startDate="2025-05-28 14:00:00 +0100" endDate="2025-05-28 15:00:00 +0100" value="HKCategoryValueAppleStandHourStood"/>
|
312 |
|
313 |
+
<Record type="HKCategoryTypeIdentifierMindfulSession" sourceName="Breathe" sourceVersion="1.0" creationDate="2025-05-28 16:00:00 +0100" startDate="2025-05-28 15:55:00 +0100" endDate="2025-05-28 16:00:00 +0100" value="HKCategoryValueNotApplicable"/>
|
314 |
|
315 |
+
<Record type="HKQuantityTypeIdentifierBodyFatPercentage" sourceName="Health" sourceVersion="17.3" unit="%" creationDate="2025-05-23 09:00:00 +0100" startDate="2025-05-23 09:00:00 +0100" endDate="2025-05-23 09:00:00 +0100" value="18.5">
|
316 |
<MetadataEntry key="HKWasUserEntered" value="1"/>
|
317 |
</Record>
|
318 |
|
319 |
+
<Record type="HKQuantityTypeIdentifierHeight" sourceName="Health" sourceVersion="17.3" unit="cm" creationDate="2025-04-08 10:00:00 +0100" startDate="2025-04-08 10:00:00 +0100" endDate="2025-04-08 10:00:00 +0100" value="180">
|
320 |
<MetadataEntry key="HKWasUserEntered" value="1"/>
|
321 |
</Record>
|
322 |
|
323 |
+
<Record type="HKQuantityTypeIdentifierRestingHeartRate" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" unit="count/min" creationDate="2025-05-29 07:00:00 +0100" startDate="2025-05-28 00:00:00 +0100" endDate="2025-05-29 00:00:00 +0100" value="52"/>
|
324 |
|
325 |
+
<Record type="HKQuantityTypeIdentifierVO2Max" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" unit="mL/kg·min" creationDate="2025-05-28 18:00:00 +0100" startDate="2025-05-28 17:26:15 +0100" endDate="2025-05-28 18:00:00 +0100" value="45.2">
|
326 |
<MetadataEntry key="HKVO2MaxTestType" value="2"/>
|
327 |
</Record>
|
328 |
|
329 |
+
<Record type="HKQuantityTypeIdentifierAppleSleepingWristTemperature" sourceName="Guillaume's Apple Watch" sourceVersion="10.0" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch6,2, software:10.0>" unit="degC" creationDate="2025-05-29 07:30:00 +0100" startDate="2025-05-29 01:00:00 +0100" endDate="2025-05-29 07:00:00 +0100" value="0.23">
|
330 |
<MetadataEntry key="HKSleepingWristTemperatureSourceType" value="0"/>
|
331 |
</Record>
|
332 |
|
333 |
+
<Record type="HKQuantityTypeIdentifierHeartRateVariabilitySDNN" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3>" unit="ms" creationDate="2025-05-28 08:00:00 +0100" startDate="2025-05-28 07:55:00 +0100" endDate="2025-05-28 08:00:00 +0100" value="45.3">
|
334 |
<HeartRateVariabilityMetadataList>
|
335 |
<InstantaneousBeatsPerMinute bpm="72" time="7:47:41.86 PM"/>
|
336 |
<InstantaneousBeatsPerMinute bpm="68" time="7:47:53.78 PM"/>
|
337 |
</HeartRateVariabilityMetadataList>
|
338 |
</Record>
|
339 |
|
340 |
+
<Record type="HKQuantityTypeIdentifierOxygenSaturation" sourceName="Health" sourceVersion="17.3" unit="%" creationDate="2025-05-23 10:00:00 +0100" startDate="2025-05-23 10:00:00 +0100" endDate="2025-05-23 10:00:00 +0100" value="98">
|
341 |
<MetadataEntry key="HKWasUserEntered" value="1"/>
|
342 |
</Record>
|
343 |
|
344 |
+
<Record type="HKQuantityTypeIdentifierRespiratoryRate" sourceName="Guillaume's Apple Watch" sourceVersion="10.0" device="<<HKDevice: 0x533ad2350>, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch6,2, software:10.0>" unit="count/min" creationDate="2025-05-29 07:00:00 +0100" startDate="2025-05-29 01:00:00 +0100" endDate="2025-05-29 07:00:00 +0100" value="14.5"/>
|
345 |
|
346 |
</HealthData>
|
mcp_server.py
CHANGED
@@ -3,12 +3,101 @@ import sqlite3
|
|
3 |
import pandas as pd
|
4 |
import json
|
5 |
import os
|
6 |
-
|
|
|
|
|
7 |
|
8 |
# Configuration
|
9 |
LOCAL_DB_PATH = "data/health_data.db"
|
|
|
10 |
DATA_REPO = os.getenv("DATA_REPO", None) # e.g., "username/project-data"
|
11 |
IS_HF_SPACE = os.getenv("SPACE_ID") is not None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
|
13 |
def get_db_connection(token=None):
|
14 |
"""Get a connection to the SQLite database."""
|
@@ -19,7 +108,7 @@ def get_db_connection(token=None):
|
|
19 |
repo_id=DATA_REPO,
|
20 |
filename="health_data.db",
|
21 |
repo_type="dataset",
|
22 |
-
token=token
|
23 |
)
|
24 |
return sqlite3.connect(db_path)
|
25 |
except Exception as e:
|
@@ -27,7 +116,7 @@ def get_db_connection(token=None):
|
|
27 |
else:
|
28 |
# Local development mode
|
29 |
if not os.path.exists(LOCAL_DB_PATH):
|
30 |
-
raise FileNotFoundError(f"Database file not found: {LOCAL_DB_PATH}.
|
31 |
return sqlite3.connect(LOCAL_DB_PATH)
|
32 |
|
33 |
def execute_sql_query(sql_query, hf_token=None):
|
@@ -56,6 +145,12 @@ def execute_sql_query(sql_query, hf_token=None):
|
|
56 |
except Exception as e:
|
57 |
return f"Error executing SQL query: {str(e)}"
|
58 |
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
# MCP Server Interface
|
60 |
with gr.Blocks(title="Apple Health MCP Server") as demo:
|
61 |
if IS_HF_SPACE and DATA_REPO:
|
@@ -96,8 +191,12 @@ with gr.Blocks(title="Apple Health MCP Server") as demo:
|
|
96 |
gr.Examples(
|
97 |
examples=[
|
98 |
["SELECT name FROM sqlite_master WHERE type='table';"],
|
|
|
|
|
|
|
|
|
99 |
["SELECT * FROM workout LIMIT 5;"],
|
100 |
-
["SELECT
|
101 |
],
|
102 |
inputs=sql_input
|
103 |
)
|
|
|
3 |
import pandas as pd
|
4 |
import json
|
5 |
import os
|
6 |
+
import tempfile
|
7 |
+
from huggingface_hub import hf_hub_download, HfApi
|
8 |
+
from huggingface_hub.utils import EntryNotFoundError
|
9 |
|
10 |
# Configuration
|
11 |
LOCAL_DB_PATH = "data/health_data.db"
|
12 |
+
LOCAL_XML_PATH = "data/export.xml"
|
13 |
DATA_REPO = os.getenv("DATA_REPO", None) # e.g., "username/project-data"
|
14 |
IS_HF_SPACE = os.getenv("SPACE_ID") is not None
|
15 |
+
HF_TOKEN = os.getenv("HF_TOKEN", None) # Space secret for write access
|
16 |
+
|
17 |
+
def check_and_parse_if_needed():
|
18 |
+
"""Check if database exists and parse XML if not (both HF and local)."""
|
19 |
+
if IS_HF_SPACE and DATA_REPO:
|
20 |
+
# HF Spaces mode
|
21 |
+
print(f"Checking if database exists in {DATA_REPO}...")
|
22 |
+
api = HfApi(token=HF_TOKEN)
|
23 |
+
|
24 |
+
try:
|
25 |
+
# Try to download the database
|
26 |
+
db_path = hf_hub_download(
|
27 |
+
repo_id=DATA_REPO,
|
28 |
+
filename="health_data.db",
|
29 |
+
repo_type="dataset",
|
30 |
+
token=HF_TOKEN
|
31 |
+
)
|
32 |
+
print("Database already exists in dataset.")
|
33 |
+
return
|
34 |
+
except (EntryNotFoundError, Exception) as e:
|
35 |
+
print(f"Database not found, will parse export.xml: {str(e)}")
|
36 |
+
|
37 |
+
try:
|
38 |
+
# Download export.xml
|
39 |
+
print("Downloading export.xml...")
|
40 |
+
xml_path = hf_hub_download(
|
41 |
+
repo_id=DATA_REPO,
|
42 |
+
filename="export.xml",
|
43 |
+
repo_type="dataset",
|
44 |
+
token=HF_TOKEN
|
45 |
+
)
|
46 |
+
|
47 |
+
# Parse the XML file
|
48 |
+
print("Parsing export.xml...")
|
49 |
+
from src.parser.parser import AppleHealthParser
|
50 |
+
|
51 |
+
with tempfile.TemporaryDirectory() as temp_dir:
|
52 |
+
db_path = os.path.join(temp_dir, "health_data.db")
|
53 |
+
|
54 |
+
# Parse with default date cutoff (6 months)
|
55 |
+
parser = AppleHealthParser(db_path=db_path)
|
56 |
+
parser.parse_file(xml_path)
|
57 |
+
|
58 |
+
print("Uploading parsed database to dataset...")
|
59 |
+
# Upload the database back to the dataset
|
60 |
+
api.upload_file(
|
61 |
+
path_or_fileobj=db_path,
|
62 |
+
path_in_repo="health_data.db",
|
63 |
+
repo_id=DATA_REPO,
|
64 |
+
repo_type="dataset",
|
65 |
+
token=HF_TOKEN,
|
66 |
+
commit_message="Add parsed SQLite database from export.xml"
|
67 |
+
)
|
68 |
+
print("Successfully created and uploaded health_data.db")
|
69 |
+
|
70 |
+
except Exception as e:
|
71 |
+
print(f"Error during HF parsing: {str(e)}")
|
72 |
+
raise
|
73 |
+
else:
|
74 |
+
# Local mode
|
75 |
+
print(f"Checking if local database exists at {LOCAL_DB_PATH}...")
|
76 |
+
|
77 |
+
if os.path.exists(LOCAL_DB_PATH):
|
78 |
+
print("Local database already exists.")
|
79 |
+
return
|
80 |
+
|
81 |
+
if not os.path.exists(LOCAL_XML_PATH):
|
82 |
+
print(f"Warning: Neither database ({LOCAL_DB_PATH}) nor XML file ({LOCAL_XML_PATH}) found.")
|
83 |
+
return
|
84 |
+
|
85 |
+
try:
|
86 |
+
print(f"Parsing local export.xml at {LOCAL_XML_PATH}...")
|
87 |
+
from src.parser.parser import AppleHealthParser
|
88 |
+
|
89 |
+
# Create data directory if it doesn't exist
|
90 |
+
os.makedirs(os.path.dirname(LOCAL_DB_PATH), exist_ok=True)
|
91 |
+
|
92 |
+
# Parse with default date cutoff (6 months)
|
93 |
+
parser = AppleHealthParser(db_path=LOCAL_DB_PATH)
|
94 |
+
parser.parse_file(LOCAL_XML_PATH)
|
95 |
+
|
96 |
+
print(f"Successfully created local database at {LOCAL_DB_PATH}")
|
97 |
+
|
98 |
+
except Exception as e:
|
99 |
+
print(f"Error during local parsing: {str(e)}")
|
100 |
+
raise
|
101 |
|
102 |
def get_db_connection(token=None):
|
103 |
"""Get a connection to the SQLite database."""
|
|
|
108 |
repo_id=DATA_REPO,
|
109 |
filename="health_data.db",
|
110 |
repo_type="dataset",
|
111 |
+
token=token or HF_TOKEN
|
112 |
)
|
113 |
return sqlite3.connect(db_path)
|
114 |
except Exception as e:
|
|
|
116 |
else:
|
117 |
# Local development mode
|
118 |
if not os.path.exists(LOCAL_DB_PATH):
|
119 |
+
raise FileNotFoundError(f"Database file not found: {LOCAL_DB_PATH}. Try restarting the server to trigger auto-parsing.")
|
120 |
return sqlite3.connect(LOCAL_DB_PATH)
|
121 |
|
122 |
def execute_sql_query(sql_query, hf_token=None):
|
|
|
145 |
except Exception as e:
|
146 |
return f"Error executing SQL query: {str(e)}"
|
147 |
|
148 |
+
# Run one-time parsing check when server starts
|
149 |
+
try:
|
150 |
+
check_and_parse_if_needed()
|
151 |
+
except Exception as e:
|
152 |
+
print(f"Warning: Could not check/parse database: {str(e)}")
|
153 |
+
|
154 |
# MCP Server Interface
|
155 |
with gr.Blocks(title="Apple Health MCP Server") as demo:
|
156 |
if IS_HF_SPACE and DATA_REPO:
|
|
|
191 |
gr.Examples(
|
192 |
examples=[
|
193 |
["SELECT name FROM sqlite_master WHERE type='table';"],
|
194 |
+
["SELECT * FROM activitysummary LIMIT 5;"],
|
195 |
+
["SELECT * FROM healthdata;"],
|
196 |
+
["SELECT date_components, active_energy_burned, apple_exercise_time FROM activitysummary ORDER BY date_components DESC LIMIT 10;"],
|
197 |
+
["SELECT COUNT(*) as count, type FROM record GROUP BY type ORDER BY count DESC LIMIT 10;"],
|
198 |
["SELECT * FROM workout LIMIT 5;"],
|
199 |
+
["SELECT type, value, unit, date(start_date) as date FROM record WHERE type LIKE '%HeartRate%' ORDER BY start_date DESC LIMIT 10;"]
|
200 |
],
|
201 |
inputs=sql_input
|
202 |
)
|