python-fitparse 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fitparse/__init__.py +10 -0
- fitparse/base.py +625 -0
- fitparse/processors.py +131 -0
- fitparse/profile.py +16646 -0
- fitparse/records.py +418 -0
- fitparse/utils.py +67 -0
- python_fitparse-2.0.0.dist-info/METADATA +191 -0
- python_fitparse-2.0.0.dist-info/RECORD +11 -0
- python_fitparse-2.0.0.dist-info/WHEEL +4 -0
- python_fitparse-2.0.0.dist-info/entry_points.txt +2 -0
- python_fitparse-2.0.0.dist-info/licenses/LICENSE +22 -0
fitparse/processors.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
from fitparse.utils import scrub_method_name, is_iterable
|
|
3
|
+
|
|
4
|
+
# Datetimes (uint32) represent seconds since this UTC_REFERENCE
|
|
5
|
+
UTC_REFERENCE = 631065600 # timestamp for UTC 00:00 Dec 31 1989
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FitFileDataProcessor:
|
|
9
|
+
# TODO: Document API
|
|
10
|
+
# Functions that will be called to do the processing:
|
|
11
|
+
#def run_type_processor(field_data)
|
|
12
|
+
#def run_field_processor(field_data)
|
|
13
|
+
#def run_unit_processor(field_data)
|
|
14
|
+
#def run_message_processor(data_message)
|
|
15
|
+
|
|
16
|
+
# By default, the above functions call these functions if they exist:
|
|
17
|
+
#def process_type_<type_name> (field_data)
|
|
18
|
+
#def process_field_<field_name> (field_data) -- can be unknown_DD but NOT recommended
|
|
19
|
+
#def process_units_<unit_name> (field_data)
|
|
20
|
+
#def process_message_<mesg_name / mesg_type_num> (data_message)
|
|
21
|
+
|
|
22
|
+
# Used to memoize scrubbed method names
|
|
23
|
+
_scrubbed_method_names = {}
|
|
24
|
+
|
|
25
|
+
def _scrub_method_name(self, method_name):
|
|
26
|
+
"""Scrubs a method name, returning result from local cache if available.
|
|
27
|
+
|
|
28
|
+
This method wraps fitparse.utils.scrub_method_name and memoizes results,
|
|
29
|
+
as scrubbing a method name is expensive.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
method_name: Method name to scrub.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Scrubbed method name.
|
|
36
|
+
"""
|
|
37
|
+
if method_name not in self._scrubbed_method_names:
|
|
38
|
+
self._scrubbed_method_names[method_name] = (
|
|
39
|
+
scrub_method_name(method_name))
|
|
40
|
+
|
|
41
|
+
return self._scrubbed_method_names[method_name]
|
|
42
|
+
|
|
43
|
+
def run_type_processor(self, field_data):
|
|
44
|
+
self._run_processor(self._scrub_method_name(
|
|
45
|
+
'process_type_%s' % field_data.type.name), field_data)
|
|
46
|
+
|
|
47
|
+
def run_field_processor(self, field_data):
|
|
48
|
+
self._run_processor(self._scrub_method_name(
|
|
49
|
+
'process_field_%s' % field_data.name), field_data)
|
|
50
|
+
|
|
51
|
+
def run_unit_processor(self, field_data):
|
|
52
|
+
if field_data.units:
|
|
53
|
+
self._run_processor(self._scrub_method_name(
|
|
54
|
+
'process_units_%s' % field_data.units), field_data)
|
|
55
|
+
|
|
56
|
+
def run_message_processor(self, data_message):
|
|
57
|
+
self._run_processor(self._scrub_method_name(
|
|
58
|
+
'process_message_%s' % data_message.def_mesg.name), data_message)
|
|
59
|
+
|
|
60
|
+
def _run_processor(self, processor_name, data):
|
|
61
|
+
try:
|
|
62
|
+
getattr(self, processor_name)(data)
|
|
63
|
+
except AttributeError:
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
def process_type_bool(self, field_data):
|
|
67
|
+
if field_data.value is not None:
|
|
68
|
+
field_data.value = bool(field_data.value)
|
|
69
|
+
|
|
70
|
+
def process_type_date_time(self, field_data):
|
|
71
|
+
value = field_data.value
|
|
72
|
+
if value is not None and value >= 0x10000000:
|
|
73
|
+
field_data.value = datetime.datetime.fromtimestamp(timestamp=(UTC_REFERENCE + value), tz=datetime.timezone.utc).replace(tzinfo=None)
|
|
74
|
+
field_data.units = None # Units were 's', set to None
|
|
75
|
+
|
|
76
|
+
def process_type_local_date_time(self, field_data):
|
|
77
|
+
if field_data.value is not None:
|
|
78
|
+
# NOTE: This value was created on the device using it's local timezone.
|
|
79
|
+
# Unless we know that timezone, this value won't be correct. However, if we
|
|
80
|
+
# assume UTC, at least it'll be consistent.
|
|
81
|
+
field_data.value = datetime.datetime.fromtimestamp(timestamp=(UTC_REFERENCE + field_data.value), tz=datetime.timezone.utc).replace(tzinfo=None)
|
|
82
|
+
field_data.units = None
|
|
83
|
+
|
|
84
|
+
def process_type_localtime_into_day(self, field_data):
|
|
85
|
+
if field_data.value is not None:
|
|
86
|
+
# NOTE: Values larger or equal to 86400 should not be possible.
|
|
87
|
+
# Additionally, if the value is exactly 86400, it will lead to an error when trying to
|
|
88
|
+
# create the time with datetime.time(24, 0 , 0).
|
|
89
|
+
#
|
|
90
|
+
# E.g. Garmin does add "sleep_time": 86400 to its fit files,
|
|
91
|
+
# which causes an error if not properly handled.
|
|
92
|
+
if field_data.value >= 86400:
|
|
93
|
+
field_data.value = datetime.time.max
|
|
94
|
+
else:
|
|
95
|
+
m, s = divmod(field_data.value, 60)
|
|
96
|
+
h, m = divmod(m, 60)
|
|
97
|
+
field_data.value = datetime.time(h, m, s)
|
|
98
|
+
field_data.units = None
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class StandardUnitsDataProcessor(FitFileDataProcessor):
|
|
102
|
+
def run_field_processor(self, field_data):
|
|
103
|
+
"""
|
|
104
|
+
Convert all '*_speed' fields using 'process_field_speed'
|
|
105
|
+
All other units will use the default method.
|
|
106
|
+
"""
|
|
107
|
+
if field_data.name.endswith("_speed"):
|
|
108
|
+
self.process_field_speed(field_data)
|
|
109
|
+
else:
|
|
110
|
+
super().run_field_processor(field_data)
|
|
111
|
+
|
|
112
|
+
def process_field_distance(self, field_data):
|
|
113
|
+
if field_data.value is not None:
|
|
114
|
+
field_data.value /= 1000.0
|
|
115
|
+
field_data.units = 'km'
|
|
116
|
+
|
|
117
|
+
def process_field_speed(self, field_data):
|
|
118
|
+
if field_data.value is not None:
|
|
119
|
+
factor = 60.0 * 60.0 / 1000.0
|
|
120
|
+
|
|
121
|
+
# record.enhanced_speed field can be a tuple
|
|
122
|
+
if is_iterable(field_data.value):
|
|
123
|
+
field_data.value = tuple(x * factor for x in field_data.value)
|
|
124
|
+
else:
|
|
125
|
+
field_data.value *= factor
|
|
126
|
+
field_data.units = 'km/h'
|
|
127
|
+
|
|
128
|
+
def process_units_semicircles(self, field_data):
|
|
129
|
+
if field_data.value is not None:
|
|
130
|
+
field_data.value *= 180.0 / (2 ** 31)
|
|
131
|
+
field_data.units = 'deg'
|