rowingdata 3.6.8__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.
Files changed (49) hide show
  1. rowingdata/__init__.py +2 -0
  2. rowingdata/__main__.py +2 -0
  3. rowingdata/boatedit.py +15 -0
  4. rowingdata/checkdatafiles.py +216 -0
  5. rowingdata/copystats.py +22 -0
  6. rowingdata/crewnerdplot.py +37 -0
  7. rowingdata/crewnerdplottime.py +37 -0
  8. rowingdata/csvparsers.py +3114 -0
  9. rowingdata/ergdataplot.py +31 -0
  10. rowingdata/ergdataplottime.py +31 -0
  11. rowingdata/ergdatatotcx.py +32 -0
  12. rowingdata/ergstickplot.py +31 -0
  13. rowingdata/ergstickplottime.py +32 -0
  14. rowingdata/ergsticktotcx.py +32 -0
  15. rowingdata/example.csv +5171 -0
  16. rowingdata/gpxtools.py +70 -0
  17. rowingdata/gpxwrite.py +151 -0
  18. rowingdata/konkatenaadje.py +19 -0
  19. rowingdata/laptesting.py +293 -0
  20. rowingdata/obsolete.py +654 -0
  21. rowingdata/otherparsers.py +718 -0
  22. rowingdata/painsled_desktop_plot.py +30 -0
  23. rowingdata/painsled_desktop_plottime.py +29 -0
  24. rowingdata/painsled_desktop_toc2.py +30 -0
  25. rowingdata/painsledplot.py +27 -0
  26. rowingdata/painsledplottime.py +27 -0
  27. rowingdata/painsledtoc2.py +23 -0
  28. rowingdata/roweredit.py +15 -0
  29. rowingdata/rowingdata.py +6941 -0
  30. rowingdata/rowproplot.py +31 -0
  31. rowingdata/rowproplottime.py +31 -0
  32. rowingdata/speedcoachplot.py +31 -0
  33. rowingdata/speedcoachplottime.py +31 -0
  34. rowingdata/speedcoachtoc2.py +36 -0
  35. rowingdata/tcxplot.py +38 -0
  36. rowingdata/tcxplot_nogeo.py +38 -0
  37. rowingdata/tcxplottime.py +33 -0
  38. rowingdata/tcxplottime_nogeo.py +33 -0
  39. rowingdata/tcxtoc2.py +30 -0
  40. rowingdata/tcxtools.py +417 -0
  41. rowingdata/trainingparser.py +302 -0
  42. rowingdata/utils.py +135 -0
  43. rowingdata/windcorrected.py +48 -0
  44. rowingdata/writetcx.py +312 -0
  45. rowingdata-3.6.8.dist-info/LICENSE +21 -0
  46. rowingdata-3.6.8.dist-info/METADATA +1149 -0
  47. rowingdata-3.6.8.dist-info/RECORD +49 -0
  48. rowingdata-3.6.8.dist-info/WHEEL +5 -0
  49. rowingdata-3.6.8.dist-info/top_level.txt +1 -0
rowingdata/tcxtools.py ADDED
@@ -0,0 +1,417 @@
1
+ # pylint: disable=C0103
2
+ from __future__ import absolute_import
3
+ import pandas as pd
4
+ import re
5
+ import sys
6
+ import unicodedata
7
+ import codecs
8
+ import xmltodict as xd
9
+ from dateutil import parser
10
+ import arrow
11
+ import gzip
12
+ import numpy as np
13
+ import string
14
+ from six import unichr
15
+ from datetime import datetime, timedelta
16
+
17
+ from lxml import etree
18
+ from lxml.etree import XMLSyntaxError
19
+ from docopt import docopt
20
+ import xml.etree.ElementTree as ET
21
+
22
+ ns1 = 'http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2'
23
+ ns2 = 'http://www.garmin.com/xmlschemas/ActivityExtension/v2'
24
+
25
+ import unicodedata
26
+ def clean_string(input):
27
+ if input:
28
+ RE_XML_ILLEGAL = u'([\u0000-\u0008\u000b-\u000c\u000e-\u001f\ufffe-\uffff])' + \
29
+ u'|' + \
30
+ u'([%s-%s][^%s-%s])|([^%s-%s][%s-%s])|([%s-%s]$)|(^[%s-%s])' % \
31
+ (unichr(0xd800),unichr(0xdbff),unichr(0xdc00),unichr(0xdfff),
32
+ unichr(0xd800),unichr(0xdbff),unichr(0xdc00),unichr(0xdfff),
33
+ unichr(0xd800),unichr(0xdbff),unichr(0xdc00),unichr(0xdfff),
34
+ )
35
+
36
+ input = re.sub(RE_XML_ILLEGAL, "", input)
37
+ input = input.replace(RE_XML_ILLEGAL,'').strip()
38
+ return input
39
+
40
+ def clean_xml_content(xml_content):
41
+ # Clean the XML content of potentially problematic characters
42
+ return ''.join(char for char in xml_content if char.isprintable())
43
+
44
+ def remove_control_characters(s):
45
+ return "".join(ch for ch in s if unicodedata.category(ch)[0]!="C")
46
+
47
+ def strip_control_characters(input):
48
+ if input:
49
+
50
+ # unicode invalid characters
51
+ RE_XML_ILLEGAL = u'([\u0000-\u0008\u000b-\u000c\u000e-\u001f\ufffe-\uffff])' + \
52
+ u'|' + \
53
+ u'([%s-%s][^%s-%s])|([^%s-%s][%s-%s])|([%s-%s]$)|(^[%s-%s])' % \
54
+ (unichr(0xd800),unichr(0xdbff),unichr(0xdc00),unichr(0xdfff),
55
+ unichr(0xd800),unichr(0xdbff),unichr(0xdc00),unichr(0xdfff),
56
+ unichr(0xd800),unichr(0xdbff),unichr(0xdc00),unichr(0xdfff),
57
+ )
58
+
59
+ input = re.sub(RE_XML_ILLEGAL, "", input)
60
+
61
+
62
+
63
+ #input = "".join(ch for ch in input if unicodedata.category(ch)[0]!="C")
64
+
65
+
66
+ # ascii control characters
67
+ #input = re.sub(r"[\x01-\x1F\x7F]", "", input)
68
+
69
+ return input
70
+
71
+ def tcx_getdict(path):
72
+ extension = path[-3:].lower()
73
+ if extension == '.gz':
74
+ with gzip.open(path,'r') as f:
75
+ input = f.read()
76
+ input = strip_control_characters(input)
77
+ d = xd.parse(input)
78
+ else:
79
+ with open(path, 'r') as f:
80
+ input = f.read()
81
+ input = strip_control_characters(input)
82
+ d = xd.parse(input)
83
+ return d['TrainingCenterDatabase']
84
+
85
+ def tcxgetactivities(d):
86
+ try:
87
+ return d['Activities']['Activity']
88
+ except KeyError:
89
+ return None
90
+
91
+ def tcxactivitygetid(d):
92
+ try:
93
+ return d['Id']
94
+ except KeyError:
95
+ return None
96
+
97
+ def tcxactivitygetlaps(d):
98
+ try:
99
+ return d['Lap']
100
+ except KeyError:
101
+ return None
102
+ except TypeError:
103
+ try:
104
+ return d[0]['Lap']
105
+ except KeyError:
106
+ return None
107
+
108
+ def tcxlapgettrack(d):
109
+ try:
110
+ return d['Track']
111
+ except KeyError:
112
+ return None
113
+
114
+ def tcxtrackgettrackpoint(d):
115
+ try:
116
+ return d['Trackpoint']
117
+ except KeyError:
118
+ return None
119
+ except TypeError:
120
+ try:
121
+ return d[0]['Trackpoint']
122
+ except KeyError:
123
+ return None
124
+
125
+ def getvalue(x,key):
126
+ try:
127
+ return x[key]
128
+ except TypeError:
129
+ return np.nan
130
+
131
+ def tcxtrack_getdata(track):
132
+ trackpoints = tcxtrackgettrackpoint(track)
133
+ df = pd.DataFrame(trackpoints)
134
+ datetime = df['Time'].apply(lambda x: parser.parse(x, fuzzy=True))
135
+ df['timestamp'] = datetime.apply(
136
+ lambda x: arrow.get(x).timestamp()+arrow.get(x).microsecond/1.e6
137
+ )
138
+ try:
139
+ #df['latitude'] = df['Position'].apply(lambda x: x['LatitudeDegrees'])
140
+ #df['longitude'] = df['Position'].apply(lambda x: x['LongitudeDegrees'])
141
+ df['latitude'] = df['Position'].apply(
142
+ lambda x: getvalue(x,'LatitudeDegrees'))
143
+ df['longitude'] = df['Position'].apply(
144
+ lambda x: getvalue(x,'LongitudeDegrees'))
145
+ except KeyError:
146
+ pass
147
+ except TypeError:
148
+ pass
149
+
150
+ for key in df.keys():
151
+ v = df[key].dropna().values
152
+ try:
153
+ if len(v) and 'Value' in v[0]:
154
+ l = df[key].apply(pd.Series)
155
+ df[key] = l['Value']
156
+ except TypeError:
157
+ pass
158
+
159
+ if key == 'Extensions':
160
+ extensionsdf = df[key].apply(pd.Series)
161
+ thekeys = list(extensionsdf.keys())
162
+ for counter, key in enumerate(thekeys):
163
+ if key:
164
+ df['extension'+str(counter)] = key
165
+ l = extensionsdf[key].apply(pd.Series)
166
+ if 'Extensions' in list(l.keys()):
167
+ #print 'aap'
168
+ l = l.apply(pd.Series)['Extensions'].apply(pd.Series)
169
+ for kk in l.keys():
170
+ if kk != 0 and 'xmlns' not in kk:
171
+ df[kk] = l[kk]
172
+
173
+ return df
174
+
175
+ def tcxtodf(path):
176
+
177
+ data = tcx_getdict(path)
178
+ activity = tcxgetactivities(data)
179
+
180
+ laps = tcxactivitygetlaps(activity)
181
+
182
+ try:
183
+ track = tcxlapgettrack(laps)
184
+ df = tcxtrack_getdata(track)
185
+ except TypeError:
186
+ df = pd.DataFrame()
187
+ for nr, lap in enumerate(laps):
188
+ track = tcxlapgettrack(lap)
189
+ dfi = tcxtrack_getdata(track)
190
+ dfi['lapid'] = nr
191
+ df = pd.concat([df, dfi])
192
+
193
+ return df
194
+
195
+ def process_trackpoint(trackpoint):
196
+ trackp = {}
197
+ for child in trackpoint:
198
+ for elem in child.iter():
199
+ if elem.tag == '{%s}Time'%ns1:
200
+ trackp['time'] = elem.text
201
+ if elem.tag == '{%s}DistanceMeters'%ns1:
202
+ trackp['distance'] = float(elem.text)
203
+ if elem.tag == '{%s}Cadence'%ns1:
204
+ try:
205
+ trackp['cadence'] = float(elem.text)
206
+ except TypeError:
207
+ trackp['cadence'] = 0.
208
+ if elem.tag == '{%s}HeartRateBpm'%ns1:
209
+ for hrchild in elem:
210
+ if hrchild.tag == '{%s}Value'%ns1:
211
+ try:
212
+ trackp['hr'] = int(hrchild.text)
213
+ except TypeError:
214
+ trackp['hr'] = 0
215
+ if elem.tag == '{%s}Extensions'%ns1:
216
+ for extchild in elem:
217
+ if extchild.tag == '{%s}TPX'%ns2:
218
+ for pchild in extchild:
219
+ if pchild.tag == '{%s}Watts'%ns2:
220
+ try:
221
+ trackp['power'] = float(pchild.text)
222
+ except TypeError:
223
+ trackp['power'] = 0
224
+ if elem.tag == '{%s}Position'%ns1:
225
+ for poschild in elem:
226
+ if poschild.tag == '{%s}LatitudeDegrees'%ns1:
227
+ trackp['latitude'] = float(poschild.text)
228
+ if poschild.tag == '{%s}LongitudeDegrees'%ns1:
229
+ trackp['longitude'] = float(poschild.text)
230
+
231
+
232
+ return trackp
233
+
234
+ import os
235
+
236
+ def tcxtodf2(path):
237
+ extension = path[-3:].lower()
238
+ #p = etree.XMLParser(recover=True)
239
+ p = etree.XMLParser()
240
+ try:
241
+ if extension == '.gz':
242
+ with gzip.open(path,'r') as f:
243
+ input = f.read()
244
+ input = input.lstrip()
245
+ input = strip_control_characters(input)
246
+ else:
247
+ with open(path, 'r') as f:
248
+ input = f.read()
249
+ input = input.lstrip()
250
+ input = strip_control_characters(input)
251
+
252
+ with open('temp_xml.tcx','w') as f:
253
+ f.write(input)
254
+
255
+ tree = etree.parse('temp_xml.tcx',parser=p)
256
+ os.remove('temp_xml.tcx')
257
+ except (TypeError,XMLSyntaxError):
258
+ tree = etree.parse(path,parser=p)
259
+
260
+ root = tree.getroot()
261
+
262
+ tracks = []
263
+ lapnr = 0
264
+
265
+ if root is None:
266
+ return pd.DataFrame()
267
+ for element in root.iter():
268
+ if element.tag == '{%s}Lap'%ns1:
269
+ lapnr += 1
270
+ if element.tag == '{%s}Track'%ns1:
271
+ tracks.append((lapnr,element))
272
+
273
+ t = []
274
+ d = []
275
+ hr = []
276
+ power = []
277
+ lat = []
278
+ cadence = []
279
+ lon = []
280
+ lapid = []
281
+
282
+ for lapnr,element in tracks:
283
+ for child in element:
284
+ if child.tag == '{%s}Trackpoint'%ns1:
285
+ trackp = process_trackpoint(child)
286
+ try:
287
+ time = parser.parse(trackp['time'])
288
+ timestamp = arrow.get(time).timestamp()+arrow.get(time).microsecond/1.e6
289
+ t.append(timestamp)
290
+ except KeyError:
291
+ t.append(np.nan)
292
+
293
+ try:
294
+ d.append(trackp['distance'])
295
+ except KeyError:
296
+ d.append(np.nan)
297
+
298
+ try:
299
+ cadence.append(trackp['cadence'])
300
+ except KeyError:
301
+ cadence.append(np.nan)
302
+
303
+ try:
304
+ hr.append(trackp['hr'])
305
+ except KeyError:
306
+ hr.append(0)
307
+
308
+ try:
309
+ power.append(trackp['power'])
310
+ except KeyError:
311
+ power.append(0)
312
+
313
+ try:
314
+ lat.append(trackp['latitude'])
315
+ except KeyError:
316
+ lat.append(np.nan)
317
+
318
+ try:
319
+ lon.append(trackp['longitude'])
320
+ except KeyError:
321
+ lon.append(np.nan)
322
+
323
+ lapid.append(lapnr)
324
+
325
+
326
+ df = pd.DataFrame(
327
+ {
328
+ 'timestamp':t,
329
+ 'HeartRateBpm':hr,
330
+ 'DistanceMeters':d,
331
+ 'Cadence':cadence,
332
+ 'Watts':power,
333
+ 'latitude':lat,
334
+ 'longitude':lon,
335
+ 'lapid':lapid,
336
+ }
337
+ )
338
+
339
+ df['Speed'] = df['DistanceMeters'].diff()/df['timestamp'].diff()
340
+ df.loc[0,'Speed'] = 0
341
+
342
+ return df
343
+
344
+ def tcxtodf3(path):
345
+ try:
346
+ if path.endswith('.gz'):
347
+ with gzip.open(path, 'rt', encoding='utf-8', errors='replace') as gz_file:
348
+ # Parse XML data from the gzipped TCX file
349
+ xml_content = gz_file.read()
350
+ cleaned_xml_content = clean_string(xml_content)
351
+ tree = ET.fromstring(cleaned_xml_content)
352
+ else:
353
+ # Parse XML data from the regular TCX file
354
+ with open(path, 'r', encoding='utf-8', errors='replace') as tcx_file:
355
+ xml_content = tcx_file.read()
356
+ cleaned_xml_content = clean_string(xml_content)
357
+ tree = ET.fromstring(cleaned_xml_content)
358
+
359
+
360
+ activity_data = []
361
+ lap_id = 0
362
+ for lap_node in tree.findall(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Lap"):
363
+ lap_id += 1
364
+ intensity_node = lap_node.find(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Intensity")
365
+ workoutstate = 4
366
+ intensity = clean_string(intensity_node.text) if intensity_node is not None else ""
367
+ if intensity == "Actve":
368
+ workoutstate = 4
369
+ elif intensity == "Resting":
370
+ workoutstate = 3
371
+ for trackpoint in lap_node.findall(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Trackpoint"):
372
+ time_node = trackpoint.find(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Time")
373
+ timestamp_str = clean_string(time_node.text)
374
+ time = parser.parse(timestamp_str)
375
+ timestamp = arrow.get(time).timestamp()+arrow.get(time).microsecond/1.e6
376
+ #timestamp = datetime.strptime(timestamp_str, "%Y-%m-%dT%H:%M:%S%z")
377
+
378
+ watts_node = trackpoint.find(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Watts")
379
+ watts = float(clean_string(watts_node.text)) if watts_node is not None else 0
380
+
381
+ speed_node = trackpoint.find(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Speed")
382
+ speed = float(clean_string(speed_node.text)) if speed_node is not None else 0
383
+
384
+ cadence_node = trackpoint.find(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Cadence")
385
+ cadence = int(clean_string(cadence_node.text)) if cadence_node is not None else 0
386
+
387
+
388
+ hr_node = trackpoint.find(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}HeartRateBpm/{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Value")
389
+ heart_rate = int(clean_string(hr_node.text)) if hr_node is not None else 0
390
+
391
+ distance_node = trackpoint.find(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}DistanceMeters")
392
+ distance = float(clean_string(distance_node.text)) if distance_node is not None else 0
393
+
394
+ latitude_node = trackpoint.find(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Position/{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}LatitudeDegrees")
395
+ longitude_node = trackpoint.find(".//{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}Position/{http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2}LongitudeDegrees")
396
+ latitude = float(clean_string(latitude_node.text)) if latitude_node is not None else 0
397
+ longitude = float(clean_string(longitude_node.text)) if longitude_node is not None else 0
398
+
399
+ activity_data.append({
400
+ 'timestamp': timestamp,
401
+ 'latitude': latitude,
402
+ 'longitude': longitude,
403
+ 'HeartRateBpm': heart_rate,
404
+ 'DistanceMeters': distance,
405
+ 'Cadence': cadence,
406
+ 'Watts': watts,
407
+ 'lapid': lap_id,
408
+ 'Speed': speed,
409
+ ' WorkoutState': workoutstate
410
+ })
411
+
412
+ # Create a Pandas DataFrame from the activity data
413
+ df = pd.DataFrame(activity_data)
414
+ return df
415
+ except ET.ParseError as e:
416
+ print(e)
417
+ return pd.DataFrame()