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/gpxtools.py ADDED
@@ -0,0 +1,70 @@
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
+
16
+ from lxml import etree
17
+ from docopt import docopt
18
+
19
+ ns1 = 'http://www.topografix.com/GPX/1/1'
20
+
21
+
22
+
23
+ def gpxtodf2(path):
24
+ tree = etree.parse(path)
25
+ root = tree.getroot()
26
+
27
+ tracks = []
28
+ lapnr = 0
29
+
30
+ for element in root.iter():
31
+ if element.tag == '{%s}trkseg'%ns1:
32
+ lapnr += 1
33
+ if element.tag == '{%s}trkpt'%ns1:
34
+ tracks.append((lapnr,element))
35
+
36
+ t = []
37
+ d = []
38
+ hr = []
39
+ power = []
40
+ lat = []
41
+ cadence = []
42
+ lon = []
43
+ lapid = []
44
+
45
+ for lapnr,element in tracks:
46
+ latitude, longitude = element.values()
47
+ lapid.append(lapnr)
48
+ lat.append(float(latitude))
49
+ lon.append(float(longitude))
50
+ for child in element:
51
+ if child.tag == '{%s}time'%ns1:
52
+ try:
53
+ time = parser.parse(child.text)
54
+ timestamp = arrow.get(time).timestamp+arrow.get(time).microsecond/1.e6
55
+ t.append(timestamp)
56
+ except KeyError:
57
+ t.append(np.nan)
58
+
59
+
60
+ df = pd.DataFrame(
61
+ {
62
+ 'TimeStamp (sec)':t,
63
+ ' latitude':lat,
64
+ ' longitude':lon,
65
+ ' lapIdx':lapid,
66
+ }
67
+ )
68
+
69
+
70
+ return df
rowingdata/gpxwrite.py ADDED
@@ -0,0 +1,151 @@
1
+ from __future__ import absolute_import
2
+ from __future__ import print_function
3
+ import time
4
+ import datetime
5
+ from dateutil import parser as ps
6
+ import lxml
7
+ import arrow
8
+ import numpy as np
9
+ from lxml import etree, objectify
10
+ from lxml.etree import XMLSyntaxError
11
+ import six.moves.urllib.request, six.moves.urllib.error, six.moves.urllib.parse
12
+ from six.moves import range
13
+
14
+ import sys
15
+ if sys.version_info[0]<=2:
16
+ pythonversion = 2
17
+ textwritemode = 'w'
18
+ else:
19
+ pythonversion = 3
20
+ textwritemode = 'wt'
21
+ from io import open
22
+
23
+
24
+ namespace='http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2'
25
+
26
+ empty_gpx = """
27
+ <?xml version="1.0" encoding="UTF-8" standalone="no" ?><gpx xmlns="http://www.topografix.com/GPX/1/1" xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3" xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1" creator="Oregon 400t" version="1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd"><metadata><link href="http://www.garmin.com"><text>Garmin International</text></link><time>2018-03-17T12:59:13</time></metadata><trk><name>Export by rowingdata</name>
28
+ </trk></gpx>
29
+ """
30
+
31
+ def lap_begin(f,datetimestring,totalmeters,avghr,maxhr,avgspm,totalseconds):
32
+ f.write('<?xml version="1.0" encoding="UTF-8" standalone="no" ?>')
33
+ f.write('<gpx xmlns="http://www.topografix.com/GPX/1/1" xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3" xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1" creator="Oregon 400t" version="1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd"><metadata><link href="http://www.garmin.com"><text>Garmin International</text></link>')
34
+
35
+
36
+ def write_gpx(gpxFile,df,row_date="2016-01-01",notes="Exported by rowingdata"):
37
+ if notes==None:
38
+ notes="Exported by rowingdata"
39
+ f=open(gpxFile,'w')
40
+
41
+ totalseconds=int(df['TimeStamp (sec)'].max()-df['TimeStamp (sec)'].min())
42
+ totalmeters=int(df['cum_dist'].max())
43
+ avghr=int(df[' HRCur (bpm)'].mean())
44
+ if avghr == 0:
45
+ avghr=1
46
+ maxhr=int(df[' HRCur (bpm)'].max())
47
+ if maxhr == 0:
48
+ maxhr=1
49
+ avgspm=int(df[' Cadence (stokes/min)'].mean())
50
+
51
+ seconds=df['TimeStamp (sec)'].values
52
+ distancemeters=df['cum_dist'].values
53
+ heartrate=df[' HRCur (bpm)'].values.astype(int)
54
+ cadence=np.round(df[' Cadence (stokes/min)'].values).astype(int)
55
+
56
+ nr_rows=len(seconds)
57
+
58
+ try:
59
+ lat=df[' latitude'].values
60
+ except KeyError:
61
+ lat=np.zeros(nr_rows)
62
+
63
+ try:
64
+ lon=df[' longitude'].values
65
+ except KeyError:
66
+ lon=np.zeros(nr_rows)
67
+
68
+ haspower=1
69
+
70
+ try:
71
+ power=df[' Power (watts)'].values
72
+ except KeyError:
73
+ haspower=0
74
+
75
+ s="2000-01-01"
76
+ tt=ps.parse(s)
77
+ #timezero=time.mktime(tt.timetuple())
78
+ timezero = arrow.get(tt).timestamp()
79
+ if seconds[0]<timezero:
80
+ # print("Taking Row_Date ",row_date)
81
+ dateobj=ps.parse(row_date)
82
+ #unixtimes=seconds+time.mktime(dateobj.timetuple())
83
+ unixtimes=seconds+arrow.get(dateobj).timestamp()
84
+
85
+
86
+
87
+ datetimestring=row_date
88
+
89
+ lap_begin(f,datetimestring,totalmeters,avghr,maxhr,avgspm,totalseconds)
90
+
91
+ ts = datetime.datetime.fromtimestamp(unixtimes[0]).isoformat()
92
+ s = '<time>{ts}</time></metadata><trk><name>Export by rowingdata</name><trkseg>'.format(
93
+ ts=ts,
94
+ )
95
+
96
+ f.write(s)
97
+
98
+
99
+ for i in range(nr_rows):
100
+ s = ' <trkpt lat="{lat}" lon="{lon}">\n'.format(
101
+ lat=lat[i],
102
+ lon=lon[i]
103
+ )
104
+ f.write(s)
105
+ #s=datetime.datetime.fromtimestamp(unixtimes[i]).isoformat()
106
+ s = arrow.get(unixtimes[i]).isoformat()
107
+ f.write(' <time>{s}</time>\n'.format(s=s))
108
+ f.write(' </trkpt>\n')
109
+
110
+
111
+
112
+ f.write('</trkseg>')
113
+ f.write('</trk>')
114
+ f.write('</gpx>')
115
+
116
+ f.close()
117
+
118
+ file=open(gpxFile,'r')
119
+
120
+ some_xml_string=file.read()
121
+
122
+ file.close()
123
+
124
+ try:
125
+ xsd_file=six.moves.urllib.request.urlopen("http://www.topografix.com/GPX/1/1/gpx.xsd")
126
+ output=open('gpx.xsd','w')
127
+ if pythonversion <= 2:
128
+ output.write(xsd_file.read().replace('\n',''))
129
+ else:
130
+ output.write(xsd_file.read().decode('utf-8').replace('\n',''))
131
+ output.close()
132
+ xsd_filename="gpx.xsd"
133
+
134
+ # Run some tests
135
+ try:
136
+ tree=objectify.parse(gpxFile)
137
+ try:
138
+ schema=etree.XMLSchema(file=xsd_filename)
139
+ parser=objectify.makeparser(schema=schema)
140
+ objectify.fromstring(some_xml_string, parser)
141
+ except XMLSyntaxError:
142
+ pass
143
+ except:
144
+ pass
145
+
146
+ except six.moves.urllib.error.URLError:
147
+ pass
148
+
149
+
150
+
151
+ return 1
@@ -0,0 +1,19 @@
1
+ from __future__ import absolute_import
2
+ from __future__ import print_function
3
+ import numpy as np
4
+ import pandas as pd
5
+
6
+ x1=np.array([1,3,4])
7
+ y1=x1*2
8
+ x2=np.array([1,2,3,4,5,6,7])
9
+ y2=x2*2-1
10
+
11
+ def test():
12
+ x=np.concatenate((x1,x2))
13
+ y=np.concatenate((y1,y2))
14
+ print((len(x),len(y)))
15
+ data=pd.DataFrame({'x':x,
16
+ 'y':y})
17
+ data=data.drop_duplicates(subset='x').sort('x',ascending=1)
18
+
19
+ return data
@@ -0,0 +1,293 @@
1
+ import pandas as pd
2
+
3
+ def summarize_rowing_data(df):
4
+ # Ensure the DataFrame is sorted by time
5
+ df = df.sort_values(by='TimeStamp (sec)')
6
+
7
+ # Convert numeric columns to numeric type, handling non-numeric values
8
+ numeric_cols = [
9
+ 'TimeStamp (sec)',
10
+ ' Horizontal (meters)',
11
+ ' Cadence (stokes/min)',
12
+ ' HRCur (bpm)',
13
+ ' Stroke500mPace (sec/500m)',
14
+ ' Power (watts)',
15
+ ' DriveLength (meters)',
16
+ ' StrokeDistance (meters)',
17
+ ' DriveTime (ms)',
18
+ ' StrokeRecoveryTime (ms)',
19
+ ' AverageDriveForce (lbs)',
20
+ ' PeakDriveForce (lbs)',
21
+ ' lapIdx',
22
+ ' ElapsedTime (sec)',
23
+ ' WorkoutState'
24
+ ]
25
+ df[numeric_cols] = df[numeric_cols].apply(pd.to_numeric, errors='coerce')
26
+
27
+ # Drop rows with NaN values
28
+ df = df.dropna()
29
+
30
+ # Initialize variables to store summarized data
31
+ lap_summaries = []
32
+
33
+ # Initialize variables to track the start time and data for each lap
34
+ lap_start_time = None
35
+ lap_end_time = None
36
+ lap_cadences = []
37
+ lap_paces = [] # Added lap_paces to store paces for each lap
38
+ lap_distances = []
39
+ lap_powers = []
40
+ lap_heart_rates = []
41
+ lap_id = None
42
+
43
+ # Use a mask to filter rows where the workout_state is work strokes
44
+ work_strokes_mask = df[' WorkoutState'].isin([1, 4, 5, 6, 7, 8, 9])
45
+
46
+ # Iterate through rows in the DataFrame
47
+ for index, row in df.iterrows():
48
+ if work_strokes_mask[index]:
49
+ # If the lap has started or the lap ID changes, append cadence, pace, and heart rate to lap lists
50
+ if lap_start_time is not None and row[' lapIdx'] == lap_id:
51
+ lap_cadences.append(row[' Cadence (stokes/min)'])
52
+ lap_paces.append(row[' Stroke500mPace (sec/500m)'])
53
+ lap_distances.append(row[' StrokeDistance (meters)'])
54
+ lap_powers.append(row[' Power (watts)'])
55
+ lap_heart_rates.append(row[' HRCur (bpm)'])
56
+ lap_end_time = row['TimeStamp (sec)']
57
+ else:
58
+ # If the lap has not started or the lap ID changes, initialize lap-related variables
59
+ if lap_start_time is not None:
60
+ # If a lap has ended, calculate lap duration and append averages to summary list
61
+ lap_duration = lap_end_time - lap_start_time
62
+ avg_cadence = sum(lap_cadences) / len(lap_cadences)
63
+ avg_pace = sum(lap_paces) / len(lap_paces)
64
+ avg_speed = 500 / avg_pace # Speed is derived from the reciprocal of pace
65
+ total_distance_per_lap = sum(lap_distances) # Improved calculation of total distance per lap
66
+ avg_power = sum(lap_powers) / len(lap_powers)
67
+ max_heart_rate = max(lap_heart_rates)
68
+ avg_heart_rate = sum(lap_heart_rates) / len(lap_heart_rates)
69
+ total_strokes_in_lap = len(lap_cadences)
70
+ distance_per_stroke = total_distance_per_lap / total_strokes_in_lap
71
+
72
+ lap_summaries.append({
73
+ 'lap_id': lap_id,
74
+ 'lap_duration': lap_duration,
75
+ 'avg_cadence': avg_cadence,
76
+ 'avg_pace': avg_pace,
77
+ 'avg_speed': avg_speed,
78
+ 'total_distance_per_lap': total_distance_per_lap,
79
+ 'avg_power': avg_power,
80
+ 'max_heart_rate': max_heart_rate,
81
+ 'avg_heart_rate': avg_heart_rate,
82
+ 'total_strokes_in_lap': total_strokes_in_lap,
83
+ 'distance_per_stroke': distance_per_stroke
84
+ })
85
+
86
+ # Reset lap-related variables
87
+ lap_start_time = row['TimeStamp (sec)']
88
+ lap_end_time = row['TimeStamp (sec)']
89
+ lap_cadences = [row[' Cadence (stokes/min)']]
90
+ lap_paces = [row[' Stroke500mPace (sec/500m)']]
91
+ lap_distances = [row[' StrokeDistance (meters)']]
92
+ lap_powers = [row[' Power (watts)']]
93
+ lap_heart_rates = [row[' HRCur (bpm)']]
94
+ lap_id = row[' lapIdx']
95
+
96
+ # If the last lap is work stroke, add it to the summary
97
+ if lap_start_time is not None:
98
+ lap_duration = lap_end_time - lap_start_time
99
+ avg_cadence = sum(lap_cadences) / len(lap_cadences)
100
+ avg_pace = sum(lap_paces) / len(lap_paces)
101
+ avg_speed = 500 / avg_pace # Speed is derived from the reciprocal of pace
102
+ total_distance_per_lap = sum(lap_distances) # Improved calculation of total distance per lap
103
+ avg_power = sum(lap_powers) / len(lap_powers)
104
+ max_heart_rate = max(lap_heart_rates)
105
+ avg_heart_rate = sum(lap_heart_rates) / len(lap_heart_rates)
106
+ total_strokes_in_lap = len(lap_cadences)
107
+ distance_per_stroke = total_distance_per_lap / total_strokes_in_lap
108
+
109
+ lap_summaries.append({
110
+ 'lap_id': lap_id,
111
+ 'lap_duration': lap_duration,
112
+ 'avg_cadence': avg_cadence,
113
+ 'avg_pace': avg_pace,
114
+ 'avg_speed': avg_speed,
115
+ 'total_distance_per_lap': total_distance_per_lap,
116
+ 'avg_power': avg_power,
117
+ 'max_heart_rate': max_heart_rate,
118
+ 'avg_heart_rate': avg_heart_rate,
119
+ 'total_strokes_in_lap': total_strokes_in_lap,
120
+ 'distance_per_stroke': distance_per_stroke
121
+ })
122
+
123
+ # Create a new DataFrame with the summarized data
124
+ summary_df = pd.DataFrame(lap_summaries)
125
+
126
+ return summary_df
127
+
128
+ import pandas as pd
129
+
130
+
131
+ import pandas as pd
132
+
133
+ def summarize_entire_workout(df):
134
+ # Ensure the DataFrame is sorted by time
135
+ df = df.sort_values(by='TimeStamp (sec)')
136
+
137
+ # Convert numeric columns to numeric type, handling non-numeric values
138
+ numeric_cols = [
139
+ 'TimeStamp (sec)',
140
+ ' Horizontal (meters)',
141
+ ' Cadence (stokes/min)',
142
+ ' HRCur (bpm)',
143
+ ' Stroke500mPace (sec/500m)',
144
+ ' Power (watts)',
145
+ ' DriveLength (meters)',
146
+ ' StrokeDistance (meters)',
147
+ ' DriveTime (ms)',
148
+ ' StrokeRecoveryTime (ms)',
149
+ ' AverageDriveForce (lbs)',
150
+ ' PeakDriveForce (lbs)',
151
+ ' lapIdx',
152
+ ' ElapsedTime (sec)',
153
+ ' WorkoutState'
154
+ ]
155
+ df[numeric_cols] = df[numeric_cols].apply(pd.to_numeric, errors='coerce')
156
+
157
+ # Drop rows with NaN values
158
+ df = df.dropna()
159
+
160
+ # Use a mask to filter rows where the workout_state is work strokes or resting strokes
161
+ work_strokes_mask = df[' WorkoutState'].isin([1, 4, 5, 6, 7, 8, 9])
162
+ rest_strokes_mask = df[' WorkoutState'] == 3
163
+
164
+ # Filter rows for work strokes, resting strokes, and entire workout
165
+ work_intervals_df = df[work_strokes_mask]
166
+ rest_intervals_df = df[rest_strokes_mask]
167
+
168
+ # Calculate total distance for work intervals and resting intervals
169
+ total_distance_work_intervals = (
170
+ work_intervals_df.groupby(' lapIdx')[' Horizontal (meters)'].max().diff().fillna(
171
+ work_intervals_df.groupby(' lapIdx')[' Horizontal (meters)'].max()
172
+ ).sum()
173
+ )
174
+ total_distance_rest_intervals = (
175
+ rest_intervals_df.groupby(' lapIdx')[' Horizontal (meters)'].max().diff().fillna(
176
+ rest_intervals_df.groupby(' lapIdx')[' Horizontal (meters)'].max()
177
+ ).sum()
178
+ )
179
+
180
+ # Calculate total distance for the entire workout
181
+ total_distance_workout = (
182
+ df.groupby(' lapIdx')[' Horizontal (meters)'].max().diff().fillna(
183
+ df.groupby(' lapIdx')[' Horizontal (meters)'].max()
184
+ ).sum()
185
+ )
186
+
187
+ # Get the maximum total time among all laps
188
+ total_time = df.groupby(' lapIdx')[' ElapsedTime (sec)'].max().max()
189
+
190
+ # Calculate averages for the entire workout
191
+ avg_cadence_workout = df[' Cadence (stokes/min)'].mean()
192
+ avg_pace_workout = df[' Stroke500mPace (sec/500m)'].mean()
193
+ avg_speed_workout = 500 / avg_pace_workout
194
+ avg_power_workout = df[' Power (watts)'].mean()
195
+ avg_heart_rate_workout = df[' HRCur (bpm)'].mean()
196
+
197
+ # Calculate averages for work intervals
198
+ avg_cadence_work = work_intervals_df[' Cadence (stokes/min)'].mean()
199
+ avg_pace_work = work_intervals_df[' Stroke500mPace (sec/500m)'].mean()
200
+ avg_speed_work = 500 / avg_pace_work
201
+ avg_power_work = work_intervals_df[' Power (watts)'].mean()
202
+ avg_heart_rate_work = work_intervals_df[' HRCur (bpm)'].mean()
203
+
204
+ # Calculate averages for resting intervals
205
+ avg_cadence_rest = rest_intervals_df[' Cadence (stokes/min)'].mean()
206
+ avg_pace_rest = rest_intervals_df[' Stroke500mPace (sec/500m)'].mean()
207
+ avg_speed_rest = 500 / avg_pace_rest
208
+ avg_power_rest = rest_intervals_df[' Power (watts)'].mean()
209
+ avg_heart_rate_rest = rest_intervals_df[' HRCur (bpm)'].mean()
210
+
211
+ # Create a summary dictionary
212
+ summary_dict = {
213
+ 'total_distance_workout': total_distance_workout,
214
+ 'total_distance_work_intervals': total_distance_work_intervals,
215
+ 'total_distance_rest_intervals': total_distance_rest_intervals,
216
+ 'total_time': total_time,
217
+ 'avg_cadence_workout': avg_cadence_workout,
218
+ 'avg_pace_workout': avg_pace_workout,
219
+ 'avg_speed_workout': avg_speed_workout,
220
+ 'avg_power_workout': avg_power_workout,
221
+ 'avg_heart_rate_workout': avg_heart_rate_workout,
222
+ 'avg_cadence_work': avg_cadence_work,
223
+ 'avg_pace_work': avg_pace_work,
224
+ 'avg_speed_work': avg_speed_work,
225
+ 'avg_power_work': avg_power_work,
226
+ 'avg_heart_rate_work': avg_heart_rate_work,
227
+ 'avg_cadence_rest': avg_cadence_rest,
228
+ 'avg_pace_rest': avg_pace_rest,
229
+ 'avg_speed_rest': avg_speed_rest,
230
+ 'avg_power_rest': avg_power_rest,
231
+ 'avg_heart_rate_rest': avg_heart_rate_rest
232
+ }
233
+
234
+ return summary_dict
235
+
236
+ def create_workout_summary_string(summary_df, summary_dict):
237
+ # Extract values from the summary_dict
238
+ total_distance_workout = int(summary_dict['total_distance_workout'])
239
+ total_distance_work_intervals = int(summary_dict['total_distance_work_intervals'])
240
+ total_distance_rest_intervals = int(summary_dict['total_distance_rest_intervals'])
241
+ total_time = str(summary_dict['total_time'])
242
+ avg_cadence_workout = str(round(summary_dict['avg_cadence_workout'], 1))
243
+ avg_pace_workout = str(summary_dict['avg_pace_workout'])
244
+ avg_speed_workout = str(round(summary_dict['avg_speed_workout'], 1))
245
+ avg_power_workout = str(round(summary_dict['avg_power_workout'], 1))
246
+ avg_heart_rate_workout = str(round(summary_dict['avg_heart_rate_workout'], 1))
247
+ avg_cadence_work = str(round(summary_dict['avg_cadence_work'], 1))
248
+ avg_pace_work = str(summary_dict['avg_pace_work'])
249
+ avg_speed_work = str(round(summary_dict['avg_speed_work'], 1))
250
+ avg_power_work = str(round(summary_dict['avg_power_work'], 1))
251
+ avg_heart_rate_work = str(round(summary_dict['avg_heart_rate_work'], 1))
252
+ avg_cadence_rest = str(round(summary_dict['avg_cadence_rest'], 1))
253
+ avg_pace_rest = str(summary_dict['avg_pace_rest'])
254
+ avg_speed_rest = str(round(summary_dict['avg_speed_rest'], 1))
255
+ avg_power_rest = str(round(summary_dict['avg_power_rest'], 1))
256
+ avg_heart_rate_rest = str(round(summary_dict['avg_heart_rate_rest'], 1))
257
+
258
+ # Create the workout summary string
259
+ workout_summary_string = (
260
+ f"Workout Summary - testdata.csv\n"
261
+ "--|Total|-Total----|--Avg--|-Avg-|Avg-|-Avg-|-Max-|-Avg\n"
262
+ f"--|{total_distance_workout:05d}|{total_time}|{avg_pace_workout}|{avg_power_workout}|"
263
+ f"{avg_cadence_workout}|{avg_heart_rate_workout}|XXX.X|{avg_heart_rate_workout}\n"
264
+ f"W-|{total_distance_work_intervals:05d}|{total_time}|{avg_pace_work}|{avg_power_work}|"
265
+ f"{avg_cadence_work}|{avg_heart_rate_work}|XXX.X|{avg_heart_rate_work}\n"
266
+ f"R-|{total_distance_rest_intervals:05d}|00:00.0|00:00.0|000.0|00.0|000.0|{avg_heart_rate_rest}|00.0\n"
267
+ "Workout Details\n"
268
+ "#-|SDist|-Split-|-SPace-|-Pwr-|SPM-|AvgHR|MaxHR|DPS-\n"
269
+ )
270
+
271
+ # Iterate over rows in the summary DataFrame to create details
272
+ for _, row in summary_df.iterrows():
273
+ lap_distance = int(row['total_distance_per_lap'])
274
+ split_time = str(row['lap_duration'])
275
+ split_pace = str(row['avg_pace'])
276
+ split_power = str(row['avg_power'])
277
+ split_cadence = str(row['avg_cadence'])
278
+ avg_heart_rate_split = str(row['avg_heart_rate'])
279
+ max_heart_rate_split = str(row['max_heart_rate'])
280
+ dps = str(row['distance_per_stroke'])
281
+ lap_id = int(row['lap_id'])
282
+
283
+ workout_summary_string += (
284
+ f"{lap_id:02d}|{lap_distance:05d}|{split_time}|{split_pace}|{split_power}|"
285
+ f"{split_cadence}|{avg_heart_rate_split}|{max_heart_rate_split}|{dps}\n"
286
+ )
287
+
288
+ return workout_summary_string
289
+
290
+ # Example usage:
291
+ # Assuming your DataFrame is named 'rowing_data_df'
292
+ #summary_entire_workout = summarize_entire_workout(rowing_data_df)
293
+ #print(summary_entire_workout)