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/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'