From a0f2217f6909b771cc18870ae3acb36d097d7c96 Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Mon, 28 Sep 2026 15:58:57 +0200 Subject: [PATCH] fit export fix --- rowers/tasks.py | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/rowers/tasks.py b/rowers/tasks.py index 35f701ac..282ba4b4 100644 --- a/rowers/tasks.py +++ b/rowers/tasks.py @@ -59,6 +59,7 @@ from scipy.interpolate import griddata import rowingdata from rowingdata import make_cumvalues, make_cumvalues_array +from rowingdata.fitwrite import RECORDING_STRATEGY_STROKE_BOUNDARY, RECORDING_STRATEGY_GPS_UPDATE from uuid import uuid4 from rowingdata import rowingdata as rdata from rowingdata import FITParser as FP @@ -374,7 +375,7 @@ def email_user_workouts_zip_chunk(rower, workout_ids, filename, instrokedata, rowdata = rdata(csvfile=workout.csvfilename) # Determine recording strategy based on workout type - recording_strategy = 'StrokeBoundary' # Default for rowing + recording_strategy = RECORDING_STRATEGY_STROKE_BOUNDARY # Default for rowing if workout.workouttype in ['water', 'Water']: # Check if workout has GPS data try: @@ -385,16 +386,21 @@ def email_user_workouts_zip_chunk(rower, workout_ids, filename, instrokedata, avg_stroke_interval = stroke_intervals.median() # If records are more frequent than strokes, likely GPS-update recording if avg_stroke_interval < 2.0: # Less than 2 seconds between records - recording_strategy = 'GPSUpdate' + recording_strategy = RECORDING_STRATEGY_GPS_UPDATE except (KeyError, AttributeError): pass # Default to StrokeBoundary - + + # In-stroke curves require stroke-boundary recording; GPS-update recording rejects them + effective_instrokedata = 'off' if recording_strategy == RECORDING_STRATEGY_GPS_UPDATE else instrokedata + res = rowdata.exporttofit(fit_filename, sport=sport, notes=workout.name, - instroke_export=instrokedata, + instroke_export=effective_instrokedata, recording_strategy=recording_strategy) zip_file.write(fit_filename, arcname=fit_filename) os.remove(fit_filename) - if res.get('companion_file'): + # exporttofit returns None in the normal case, and a dict only + # when there's something notable to report (see its docstring) + if res and res.get('companion_file'): companion_filename = res['companion_file'] zip_file.write(companion_filename, arcname=os.path.basename(companion_filename)) os.remove(companion_filename) @@ -447,7 +453,7 @@ def email_all_user_workouts_zip(rower, start_date, end_date, fit_filename = f"workout_{workouttype}_{workout.id}_{workout.date.strftime('%Y%m%d')}.fit" # Determine recording strategy based on workout type - recording_strategy = 'StrokeBoundary' # Default for rowing + recording_strategy = RECORDING_STRATEGY_STROKE_BOUNDARY # Default for rowing if workout.workouttype in ['water', 'Water']: # Check if workout has GPS data try: @@ -458,20 +464,25 @@ def email_all_user_workouts_zip(rower, start_date, end_date, avg_stroke_interval = stroke_intervals.median() # If records are more frequent than strokes, likely GPS-update recording if avg_stroke_interval < 2.0: # Less than 2 seconds between records - recording_strategy = 'GPSUpdate' + recording_strategy = RECORDING_STRATEGY_GPS_UPDATE except (KeyError, AttributeError): pass # Default to StrokeBoundary - + + # In-stroke curves require stroke-boundary recording; GPS-update recording rejects them + effective_instrokedata = 'off' if recording_strategy == RECORDING_STRATEGY_GPS_UPDATE else instrokedata + # exporttofit creates a file, we need to add it to the zip_file res = rowdata.exporttofit(fit_filename, sport=workouttype, notes=workout.name, - instroke_export=instrokedata, + instroke_export=effective_instrokedata, recording_strategy=recording_strategy) zip_file.write(fit_filename, arcname=fit_filename) os.remove(fit_filename) - # res is a dict. If res[’companion_file’] is not None, - # it contains the filename of the companion file that was - # created (e.g. for instroke data) which also needs to be added to the zip - if res.get('companion_file'): + # exporttofit returns None in the normal case, and a dict only + # when there's something notable to report (see its docstring). + # If res['companion_file'] is set, it contains the filename of the + # companion file that was created (e.g. for instroke data) which + # also needs to be added to the zip + if res and res.get('companion_file'): companion_filename = res['companion_file'] zip_file.write(companion_filename, arcname=os.path.basename(companion_filename)) os.remove(companion_filename)