-
Notifications
You must be signed in to change notification settings - Fork 1
/
cls_apple_health_xml_streams.py
158 lines (128 loc) · 5.19 KB
/
cls_apple_health_xml_streams.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import xml.etree.ElementTree as et
from constants_apple_health_data import *
__all__ = [
'AppleHealthDataElementsStream',
'AppleHealthDataActivitySummaryStream',
'AppleHealthDataRecordStream',
'AppleHealthDataRecordTypeStream',
'AppleHealthDataWorkoutStream'
]
class XmlStream:
def __init__(self, filepath: str):
self._filepath = filepath
self._context = None
self._root = None
self._context = et.iterparse(self._filepath, events=("start", "end"))
self._clear_root: bool = False
event, self._root = next(self._context)
def __enter__(self):
return self
def __next__(self):
if self._clear_root:
self._root.clear()
self._clear_root = False
while True:
event, elem = next(self._context)
if event == "end":
self._clear_root = True
break
return elem
def __iter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self._context = None
class AppleHealthDataElementsStream(XmlStream):
"""Returns the child elements of the root HealthData
The receiver of a child element can use the child to retrieve
the child's children.
"""
def __next__(self):
while True:
elem = super().__next__()
if elem.tag in HEALTH_ROOT_CHILDREN:
return elem
class AppleHealthDataActivitySummaryStream(AppleHealthDataElementsStream):
def __init__(self, xml_filepath: str):
super().__init__(xml_filepath)
self._activity_summary_found = False
def __next__(self):
while True:
elem = super().__next__()
if elem.tag == ACTIVITY_SUMMARY:
self._activity_summary_found = True
return elem
elif self._activity_summary_found:
raise StopIteration
class AppleHealthDataRecordStream(AppleHealthDataElementsStream):
def __init__(self, xml_filepath: str):
super().__init__(xml_filepath)
self._record_found = False
def __next__(self):
while True:
elem = super().__next__()
if elem.tag == RECORD:
self._record_found = True
return elem
elif self._record_found:
raise StopIteration
@staticmethod
def is_supported_record_type(record_type: str):
return record_type in {
'HKCategoryTypeIdentifierAppleStandHour',
'HKCategoryTypeIdentifierAudioExposureEvent',
'HKCategoryTypeIdentifierSleepAnalysis',
'HKQuantityTypeIdentifierActiveEnergyBurned',
'HKQuantityTypeIdentifierAppleExerciseTime',
'HKQuantityTypeIdentifierAppleStandTime',
'HKQuantityTypeIdentifierBasalEnergyBurned',
'HKQuantityTypeIdentifierBloodPressureDiastolic',
'HKQuantityTypeIdentifierBloodPressureSystolic',
'HKQuantityTypeIdentifierBodyFatPercentage',
'HKQuantityTypeIdentifierBodyMass',
'HKQuantityTypeIdentifierBodyMassIndex',
'HKQuantityTypeIdentifierDietaryCholesterol',
'HKQuantityTypeIdentifierDistanceWalkingRunning',
'HKQuantityTypeIdentifierEnvironmentalAudioExposure',
'HKQuantityTypeIdentifierFlightsClimbed',
'HKQuantityTypeIdentifierHeadphoneAudioExposure',
'HKQuantityTypeIdentifierHeartRate',
'HKQuantityTypeIdentifierHeartRateVariabilitySDNN',
'HKQuantityTypeIdentifierHeight',
'HKQuantityTypeIdentifierLeanBodyMass',
'HKQuantityTypeIdentifierRestingHeartRate',
'HKQuantityTypeIdentifierStepCount',
'HKQuantityTypeIdentifierVO2Max',
'HKQuantityTypeIdentifierWaistCircumference',
'HKQuantityTypeIdentifierWalkingHeartRateAverage'
}
class AppleHealthDataRecordTypeStream(AppleHealthDataElementsStream):
def __init__(self, xml_filepath: str, record_type: str):
super().__init__(xml_filepath)
if not AppleHealthDataRecordStream.is_supported_record_type(record_type):
raise ValueError(f'{record_type} is not supported.')
self._record_type = record_type
self._record_type_found = False
def __next__(self):
while True:
elem = super().__next__()
record = elem.get(FIELD_TYPE, '')
if record == self._record_type:
self._record_type_found = True
return elem
elif self._record_type_found:
# records of the same record type appear to be
# clustered together in the xml file; stop processing
# when the record type is read
raise StopIteration
class AppleHealthDataWorkoutStream(AppleHealthDataElementsStream):
def __init__(self, xml_filepath: str):
super().__init__(xml_filepath)
self._workout_found = False
def __next__(self):
while True:
elem = super().__next__()
if elem.tag == WORKOUT:
self._workout_found = True
return elem
elif self._workout_found:
raise StopIteration