Private
Public Access
1
0

Merge branch 'release/v23.9.0'

This commit is contained in:
2026-03-13 11:25:19 +01:00
4 changed files with 100 additions and 0 deletions

View File

@@ -357,6 +357,78 @@ def correct_intensity(workout):
return workout
<<<<<<< HEAD
=======
import io
import zipfile
@app.task
def email_all_user_workouts_zip(rower, start_date=None, end_date=None, debug=False, **kwargs):
# Get all workouts for this user, optionally filtered by date range
workouts = Workout.objects.filter(user=rower).order_by('-date')
# Apply date filters if provided
if start_date:
workouts = workouts.filter(date__gte=start_date)
if end_date:
workouts = workouts.filter(date__lte=end_date)
# for debug, limit to 5 workouts
if settings.DEBUG:
workouts = workouts[:5]
if not workouts.exists():
dologging('export_all_workouts.log', f"No workouts found for user {rower.user.id} in date range {start_date} to {end_date}")
return 0
# Create ZIP file in memory
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file:
for workout in workouts:
try:
rowdata = rdata(csvfile=workout.csvfilename)
workouttype = mytypes.fitmapping.get(workout.workouttype, 'generic')
fit_filename = f"workout_{workout.id}_{workout.date.strftime('%Y%m%d')}.fit"
# exporttofit creates a file, we need to add it to the zip_file
rowdata.exporttofit(fit_filename, sport=workouttype, notes=workout.name)
zip_file.write(fit_filename, arcname=fit_filename)
os.remove(fit_filename)
except Exception as e: # pragma: no cover
dologging('export_all_workouts.log', f"Error exporting workout {workout.id}: {str(e)}")
continue
# Save ZIP file to disk
export_date = datetime.datetime.now().strftime('%Y%m%d')
filename = f"{rower.user.username}_workouts_{export_date}_from_{start_date}_to_{end_date}_{uuid4().hex[:8]}.zip"
zip_file_path = os.path.join(settings.MEDIA_ROOT, filename)
try:
with open(zip_file_path, 'wb') as f:
f.write(zip_buffer.getvalue())
except Exception as e: # pragma: no cover
dologging('export_all_workouts.log', f"Error saving ZIP file: {str(e)}")
return 0
# Send email with download link
subject = "Rowsandall Workouts Export"
from_email = 'Rowsandall <info@rowsandall.com>'
useremail = rower.user.email
# Generate download URL
download_url = f"{SITE_URL}/rowers/workouts/download/?file={filename}"
_ = send_template_email(
from_email, [useremail],
subject,
'workouts_export_email.html',
{'download_url': download_url, 'filename': filename},
)
return 1
>>>>>>> release/v23.9.0
@app.task
def handle_loadnextweek(rower, debug=False, **kwargs):

View File

@@ -260,6 +260,11 @@
TCX
</a>
</li>
<li id="export-fit">
<a href="/rowers/workout/{{ workout.id|encode }}/emailfit/">
FIT
</a>
</li>
</ul>
</li>
<li class="has-children" id="data">

View File

@@ -490,6 +490,8 @@ urlpatterns = [
name='workout_comment_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/emailtcx/$', views.workout_tcxemail_view,
name='workout_tcxemail_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/emailfit/$', views.workout_fitemail_view,
name='workout_fitemail_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/emailgpx/$', views.workout_gpxemail_view,
name='workout_gpxemail_view'),
re_path(r'^workout/(?P<id>\b[0-9A-Fa-f]+\b)/emailcsv/$', views.workout_csvemail_view,

View File

@@ -22,6 +22,27 @@ def workout_tcxemail_view(request, id=0):
os.remove(tcxfilename)
return response
@permission_required('workout.change_workout', fn=get_workout_by_opaqueid, raise_exception=True)
def workout_fitemail_view(request, id=0):
w = get_workout(id)
row = rdata(csvfile=w.csvfilename)
code = str(uuid4())
fitfilename = code+'.fit'
workouttype = mytypes.fitmapping.get(w.workouttype, 'generic')
row.exporttofit(fitfilename, sport=workouttype, notes=w.name)
with open(fitfilename, 'rb') as f:
response = HttpResponse(f)
response['Content-Disposition'] = 'attachment; filename="%s"' % fitfilename
response['Content-Type'] = 'application/octet-stream'
os.remove(fitfilename)
return response
@login_required()
def plannedsessions_icsemail_view(request, userid=0):